#include "Arduino.h" #include "pins_arduino.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #define PRESCALERS_QTY 7 typedef struct { unsigned int frequency; uint8_t prescaler; uint32_t period_ticks; }FrequencyParams_t; FrequencyParams_t frequencyParams = {0, 0, 0}; // timer handler Timer16_HandleTypeDef htimer16_1; // timer_toggle_count: // > 0 - duration specified // = 0 - stopped // < 0 - infinitely (until noTone() called) volatile long timer_toggle_count; // tone pin params volatile int8_t timer_pin = -1; volatile GPIO_TypeDef* timer_pin_port = 0; volatile HAL_PinsTypeDef timer_pin_mask = (HAL_PinsTypeDef)0; bool timerIsOn = false; static void Timer16_Init(uint8_t prescaler) { htimer16_1.Instance = TIMER16_1; htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_SYSTEM; // prescaler depends on required frequency htimer16_1.Clock.Prescaler = prescaler; htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE; htimer16_1.Trigger.Source = 0; // timer triggers by software htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE; htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE; htimer16_1.Waveform.Enable = TIMER16_WAVEFORM_GENERATION_ENABLE; htimer16_1.Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; htimer16_1.EncoderMode = TIMER16_ENCODER_DISABLE; HAL_Timer16_Init(&htimer16_1); } // calculate prescaler and period in timer ticks for required frequancy static void calcFrequencyParams(FrequencyParams_t* params, unsigned int newFrequency) { // limit the frequency to an acceptable range if (newFrequency < TONE_MIN_FREQUENCY_HZ) newFrequency = TONE_MIN_FREQUENCY_HZ; if (newFrequency > TONE_MAX_FREQUENCY_HZ) newFrequency = TONE_MAX_FREQUENCY_HZ; // go through the prescalers possible values ​to find the appropriate one. // prescaler is set to a value from 0 to 7 uint32_t tempPeriod; for (uint8_t prescaler = 0; prescaler < PRESCALERS_QTY; prescaler++) { // timer frequency is 2 times greater than the required signal frequency tempPeriod = F_CPU / ((1 << prescaler) * 2 * newFrequency); if (tempPeriod <= UINT16_MAX) { // if period is suitable save the current prescaler and period params->frequency = newFrequency; params->prescaler = prescaler; params->period_ticks = tempPeriod; break; } } } // start tone with frequency (in hertz) and duration (in milliseconds) void tone(uint8_t pin, unsigned int frequency, unsigned long duration) { if (!timerIsOn) // if tone is not generated at the moment { // calculate the parameters necessary to ensure a given frequency if the frequency has changed if (frequencyParams.frequency != frequency) calcFrequencyParams(&frequencyParams, frequency); // Calculate the toggle count if (duration > 0) timer_toggle_count = (duration * (F_CPU/(frequencyParams.period_ticks*(1<CLEAR = timer_pin_mask; HAL_Timer16_Counter_Start_IT(&htimer16_1, frequencyParams.period_ticks); timerIsOn = true; } // if tone is generated, but the same pin that works now is specified else if (timerIsOn && (pin == timer_pin)) { // change frequency if necessary if (frequency != frequencyParams.frequency) { calcFrequencyParams(&frequencyParams, frequency); htimer16_1.Clock.Prescaler = frequencyParams.prescaler; // update value in common structure HAL_Timer16_SetPrescaler(&htimer16_1, frequencyParams.prescaler); // the timer is turned off inside the function HAL_Timer16_Counter_Start_IT(&htimer16_1, frequencyParams.period_ticks); // start with a new period } } } // stop tone void noTone(uint8_t pin) { if (timerIsOn) { timer_pin_port->CLEAR = timer_pin_mask; // pin to 0 htimer16_1.Instance->CR &= ~TIMER16_CR_ENABLE_M; // disable timer HAL_EPIC_MaskLevelClear(HAL_EPIC_TIMER16_1_MASK); pinMode(pin, INPUT); // deinit pin timer_pin = -1; // reset to default timerIsOn = false; } } // irq handler extern "C" void tone_interrupt_handler(void) { if ((htimer16_1.Instance->ISR & htimer16_1.Instance->IER) & TIMER16_ISR_ARR_MATCH_M) { // timer period has passed, change the pin state if (timer_toggle_count != 0) { timer_pin_port->OUTPUT_ ^= timer_pin_mask; if (timer_toggle_count > 0) timer_toggle_count--; } else { // turn off if the specified duration has passed timer_pin_port->CLEAR = timer_pin_mask; noTone(timer_pin); } } // reset timer interrupt flags htimer16_1.Instance->ICR = 0xFFFFFFFF; }