Timer16 Использование прерываний
Содержание
Особенность установки флагов прерываний
Флаг прерывания по сравнению выставляется, если значение счета таймера совпадает со значением сравнения. Из-за этого возможна ситуация, когда после сброса флага прерывания по сравнению внутри обработчика флаг снова выставляется на следующем такте, если счетчик таймера не успел увеличиться. В таком случае, после выхода из обработчика прерывания происходит повторное срабатывание.
Пример ниже демонстрирует эту ситуацию. В этом примере Timer16 тактируется от встроенного низкочастотного источника тактирования.
#include "mik32_hal.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #include "uart_lib.h" #include "xprintf.h" Timer16_HandleTypeDef htimer16_1; void SystemClock_Config(void); static void Timer16_1_Init(void); void GPIO_Init(); int main() { HAL_Init(); SystemClock_Config(); GPIO_Init(); Timer16_1_Init(); HAL_Timer16_Counter_Start(&htimer16_1, 0x7); HAL_Timer16_SetCMP(&htimer16_1, 0x7 / 2); HAL_Timer16_SetInterruptMask(&htimer16_1, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); /* Включать прерывания Timer16 рекомендуется после его инициализации */ HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_1_MASK); HAL_IRQ_EnableInterrupts(); while (1) { } } void SystemClock_Config(void) { PCC_InitTypeDef PCC_OscInit = {0}; PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; PCC_OscInit.AHBDivider = 0; PCC_OscInit.APBMDivider = 0; PCC_OscInit.APBPDivider = 0; PCC_OscInit.HSI32MCalibrationValue = 128; PCC_OscInit.LSI32KCalibrationValue = 8; PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; HAL_PCC_Config(&PCC_OscInit); } static void Timer16_1_Init(void) { htimer16_1.Instance = TIMER16_1; /* Настройка тактирования */ htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_OSC32K; htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_16; htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ /* Настройка режима обновления регистра ARR и CMP */ htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE; /* Настройка триггера */ htimer16_1.Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9; htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; /* При использовании триггера значение должно быть отлично от software */ htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; /* Разрешить повторное срабатывание триггера */ /* Настройки фильтра */ htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE; htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE; /* Настройка режима энкодера */ htimer16_1.EncoderMode = TIMER16_ENCODER_DISABLE; /* Выходной сигнал */ htimer16_1.Waveform.Enable = TIMER16_WAVEFORM_GENERATION_DISABLE; htimer16_1.Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; HAL_Timer16_Init(&htimer16_1); } void GPIO_Init() { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_PCC_GPIO_0_CLK_ENABLE(); __HAL_PCC_GPIO_1_CLK_ENABLE(); __HAL_PCC_GPIO_2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_1; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); } void trap_handler() { if (TIMER16_1->ISR & TIMER16_FLAG_CMPM) { GPIO_0->SET = 1; TIMER16_1->ICR = TIMER16_FLAG_CMPM; GPIO_0->CLEAR = 1; } if (TIMER16_1->ISR & TIMER16_FLAG_ARRM) { GPIO_0->SET = 2; TIMER16_1->ICR = TIMER16_FLAG_ARRM; GPIO_0->CLEAR = 2; } }
На временной диаграмме видны многократные срабатывания прерывания по сравнению (CMP). Однократные прерывания по перезагрузке (ARR) связаны с реализацией Timer16.
Варианты обхода
Повторная установка флага потребует подбора обходного пути в зависимости от задачи. Основная идея - отключение прерывания на время счета, равного значению сравнения. Рассмотрим некоторые из возможных обходных путей работы с прерываниями по сравнению.
Отключение прерывания по сравнению и повторное включение в основном цикле
Один из возможных вариантов обхода: временное отключение прерывания по сравнению на время до смены значения счетчика в другой части программы, например, в основном цикле, как в примере ниже:
#include "mik32_hal.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #include "uart_lib.h" #include "xprintf.h" Timer16_HandleTypeDef htimer16_1; void SystemClock_Config(void); static void Timer16_1_Init(void); void GPIO_Init(); int main() { HAL_Init(); SystemClock_Config(); GPIO_Init(); Timer16_1_Init(); HAL_Timer16_Counter_Start(&htimer16_1, 0x7); HAL_Timer16_SetCMP(&htimer16_1, 0x7 / 2); HAL_Timer16_SetInterruptMask(&htimer16_1, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); /* Включать прерывания Timer16 рекомендуется после его инициализации */ HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_1_MASK); HAL_IRQ_EnableInterrupts(); while (1) { if ((TIMER16_1->CNT != TIMER16_1->CMP) && ((TIMER16_1->IER & TIMER16_FLAG_CMPM) == 0)) { TIMER16_1->IER |= TIMER16_FLAG_CMPM; TIMER16_1->ICR = TIMER16_FLAG_CMPM; } if ((TIMER16_1->CNT != TIMER16_1->ARR) && ((TIMER16_1->IER & TIMER16_FLAG_ARRM) == 0)) { TIMER16_1->IER |= TIMER16_FLAG_ARRM; TIMER16_1->ICR = TIMER16_FLAG_ARRM; } } } void SystemClock_Config(void) { PCC_InitTypeDef PCC_OscInit = {0}; PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; PCC_OscInit.AHBDivider = 0; PCC_OscInit.APBMDivider = 0; PCC_OscInit.APBPDivider = 0; PCC_OscInit.HSI32MCalibrationValue = 128; PCC_OscInit.LSI32KCalibrationValue = 8; PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; HAL_PCC_Config(&PCC_OscInit); } static void Timer16_1_Init(void) { htimer16_1.Instance = TIMER16_1; /* Настройка тактирования */ htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_OSC32K; htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_16; htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ /* Настройка режима обновления регистра ARR и CMP */ htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE; /* Настройка триггера */ htimer16_1.Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9; htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; /* При использовании триггера значение должно быть отлично от software */ htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; /* Разрешить повторное срабатывание триггера */ /* Настройки фильтра */ htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE; htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE; /* Настройка режима энкодера */ htimer16_1.EncoderMode = TIMER16_ENCODER_DISABLE; /* Выходной сигнал */ htimer16_1.Waveform.Enable = TIMER16_WAVEFORM_GENERATION_DISABLE; htimer16_1.Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; HAL_Timer16_Init(&htimer16_1); } void GPIO_Init() { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_PCC_GPIO_0_CLK_ENABLE(); __HAL_PCC_GPIO_1_CLK_ENABLE(); __HAL_PCC_GPIO_2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_1; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); } void trap_handler() { if (TIMER16_1->ISR & TIMER16_FLAG_CMPM) { GPIO_0->SET = 1; TIMER16_1->IER &= ~TIMER16_FLAG_CMPM; TIMER16_1->ICR = TIMER16_FLAG_CMPM; GPIO_0->CLEAR = 1; } if (TIMER16_1->ISR & TIMER16_FLAG_ARRM) { GPIO_0->SET = 2; TIMER16_1->IER &= ~TIMER16_FLAG_ARRM; TIMER16_1->ICR = TIMER16_FLAG_ARRM; GPIO_0->CLEAR = 2; } }
На временной диаграмме видны однократные срабатывания прерываний по сравнению (CMP) и перезагрузке (ARR):
Такой вариант можно использовать, если программа работает в режиме pooling'а, регулярно вызывая основной цикл.
Отключение прерывания по сравнению и повторное включение в прерывании по перезагрузке
Возможный вариант обхода: отключение прерывания по сравнению и повторное включение в прерывании по переполнению. Пример представлен ниже:
#include "mik32_hal.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #include "uart_lib.h" #include "xprintf.h" /* * В примере демонстрируется обход повторного входа в прерывание по CMP. * Обход заключается в отключении прерывания CMP при внутри обработчика флага CMP * и его включении внутри обработчика прерывания ARR. * */ extern unsigned long __TEXT_START__; #define ARR_VALUE 6 #define CMP_VALUE (ARR_VALUE / 2) Timer16_HandleTypeDef htimer16_1; void SystemClock_Config(void); static void Timer16_1_Init(void); void GPIO_Init(); int main() { write_csr(mtvec, &__TEXT_START__); HAL_Init(); SystemClock_Config(); UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0); xprintf("\nStart\n"); GPIO_Init(); Timer16_1_Init(); HAL_Timer16_Counter_Start(&htimer16_1, ARR_VALUE); HAL_Timer16_SetCMP(&htimer16_1, CMP_VALUE); HAL_Timer16_SetInterruptMask(&htimer16_1, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); /* Включать прерывания Timer16 рекомендуется после его инициализации */ HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_1_MASK); HAL_IRQ_EnableInterrupts(); while (1) { GPIO_0->OUTPUT = (GPIO_0->OUTPUT & ~(0b111 << 7)) | (TIMER16_1->CNT << 7); // Вывод текущего счета таймера } } void SystemClock_Config(void) { PCC_InitTypeDef PCC_OscInit = {0}; PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; PCC_OscInit.AHBDivider = 0; PCC_OscInit.APBMDivider = 0; PCC_OscInit.APBPDivider = 0; PCC_OscInit.HSI32MCalibrationValue = 128; PCC_OscInit.LSI32KCalibrationValue = 8; PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; HAL_PCC_Config(&PCC_OscInit); } static void Timer16_1_Init(void) { htimer16_1.Instance = TIMER16_1; /* Настройка тактирования */ htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_OSC32K; htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_128; htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ /* Настройка режима обновления регистра ARR и CMP */ htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE; /* Настройка триггера */ htimer16_1.Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9; htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; /* При использовании триггера значение должно быть отлично от software */ htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; /* Разрешить повторное срабатывание триггера */ /* Настройки фильтра */ htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE; htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE; /* Настройка режима энкодера */ htimer16_1.EncoderMode = TIMER16_ENCODER_DISABLE; /* Выходной сигнал */ htimer16_1.Waveform.Enable = TIMER16_WAVEFORM_GENERATION_DISABLE; htimer16_1.Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; HAL_Timer16_Init(&htimer16_1); } void GPIO_Init() { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_PCC_GPIO_0_CLK_ENABLE(); __HAL_PCC_GPIO_1_CLK_ENABLE(); __HAL_PCC_GPIO_2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9; GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); } void trap_handler() { GPIO_0->OUTPUT = (GPIO_0->OUTPUT & ~(0b111 << 7)) | (TIMER16_1->CNT << 7); // Вывод текущего счета таймера if ((TIMER16_1->ISR & TIMER16_FLAG_CMPM) && (TIMER16_1->IER & TIMER16_FLAG_CMPM)) { GPIO_0->SET = 1 << 0; TIMER16_1->IER &= ~TIMER16_FLAG_CMPM; // Запретить прерывание по CMP. /* Обработчик CMP */ TIMER16_1->ICR = TIMER16_FLAG_CMPM; GPIO_0->CLEAR = 1 << 0; } if ((TIMER16_1->ISR & TIMER16_FLAG_ARRM) && (TIMER16_1->IER & TIMER16_FLAG_ARRM)) { GPIO_0->SET = 1 << 2; TIMER16_1->ICR = TIMER16_FLAG_CMPM; TIMER16_1->IER |= TIMER16_FLAG_CMPM; // Разрешить прерывание по CMP. /* Обработчик ARR */ TIMER16_1->ICR = TIMER16_FLAG_ARRM; GPIO_0->CLEAR = 1 << 2; } }
На временной диаграмме видны однократные срабатывания прерываний по сравнению (CMP) и перезагрузке (ARR) на выводах GPIO0.0 и GPIO0.2 соответственно:
Пример с использованием двух таймеров
Подобный подход можно применить к прерываниям от двух таймеров. В примере ниже запущены два таймера. В их обработчиках меняется состояние выводов GPIO0.3 и GPIO1.3, это происходит "околосинхронно".
Программа работает неправильно при следующих условиях:
- Закомментирована строка #define TIMER_CMP_IRQ_WORKAROUND;
- Работа из внешней флеш в режиме Quad SPI или XIP с кэшированием.
Это выражается в несинхронном переключении выводов.
Фрагменты, активные при заданном макросе TIMER_CMP_IRQ_WORKAROUND, отвечают за обход повторной установки флага CMP.
Код примера представлен ниже:
#include "mik32_hal.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #include "uart_lib.h" #include "xprintf.h" /* * Пример использования двух Timer16 в прерывании по CMP. * В примере демонстрируется обход повторного входа в прерывание по CMP. * Обход заключается в отключении прерывания CMP при внутри обработчика флага CMP * и его включении внутри обработчика прерывания ARR. * */ #define TIMER_CMP_IRQ_WORKAROUND extern unsigned long __TEXT_START__; #define ARR_VALUE 50000 #define CMP_VALUE 25000 Timer16_HandleTypeDef htimer16_0; Timer16_HandleTypeDef htimer16_1; void SystemClock_Config(void); static void Timer16_Init( Timer16_HandleTypeDef *htimer16, TIMER16_TypeDef *timer_ptr); void GPIO_Init(); void main() { // interrupt vector init write_csr(mtvec, &__TEXT_START__); HAL_Init(); SystemClock_Config(); UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0); xprintf("\nStart\n"); GPIO_Init(); Timer16_Init(&htimer16_0, TIMER16_0); Timer16_Init(&htimer16_1, TIMER16_1); HAL_Timer16_Counter_Start(&htimer16_0, ARR_VALUE); HAL_Timer16_Counter_Start(&htimer16_1, ARR_VALUE); HAL_Timer16_SetCMP(&htimer16_0, CMP_VALUE); HAL_Timer16_SetCMP(&htimer16_1, CMP_VALUE); HAL_Timer16_SetInterruptMask(&htimer16_0, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); HAL_Timer16_SetInterruptMask(&htimer16_1, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); /* Включать прерывания Timer16 рекомендуется после его инициализации */ HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_0_MASK | HAL_EPIC_TIMER16_1_MASK); HAL_IRQ_EnableInterrupts(); for (;;) { } } void SystemClock_Config(void) { PCC_InitTypeDef PCC_OscInit = {0}; PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; PCC_OscInit.AHBDivider = 0; PCC_OscInit.APBMDivider = 0; PCC_OscInit.APBPDivider = 0; PCC_OscInit.HSI32MCalibrationValue = 128; PCC_OscInit.LSI32KCalibrationValue = 8; PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; HAL_PCC_Config(&PCC_OscInit); } static void Timer16_Init( Timer16_HandleTypeDef *htimer16, TIMER16_TypeDef *timer_ptr) { htimer16->Instance = timer_ptr; /* Настройка тактирования */ htimer16->Clock.Source = TIMER16_SOURCE_INTERNAL_OSC32M; htimer16->CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ htimer16->Clock.Prescaler = TIMER16_PRESCALER_128; htimer16->ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ /* Настройка режима обновления регистра ARR и CMP */ htimer16->Preload = TIMER16_PRELOAD_AFTERWRITE; /* Настройка триггера */ htimer16->Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9; htimer16->Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; /* При использовании триггера значение должно быть отлично от software */ htimer16->Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; /* Разрешить повторное срабатывание триггера */ /* Настройки фильтра */ htimer16->Filter.ExternalClock = TIMER16_FILTER_NONE; htimer16->Filter.Trigger = TIMER16_FILTER_NONE; /* Настройка режима энкодера */ htimer16->EncoderMode = TIMER16_ENCODER_DISABLE; /* Выходной сигнал */ htimer16->Waveform.Enable = TIMER16_WAVEFORM_GENERATION_DISABLE; htimer16->Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; HAL_Timer16_Init(htimer16); } void GPIO_Init() { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_PCC_GPIO_0_CLK_ENABLE(); __HAL_PCC_GPIO_1_CLK_ENABLE(); __HAL_PCC_GPIO_2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); HAL_GPIO_Init(GPIO_1, &GPIO_InitStruct); } void trap_handler() { if ((TIMER16_0->ISR & TIMER16_FLAG_CMPM) && (TIMER16_0->IER & TIMER16_FLAG_CMPM)) { GPIO_0->OUTPUT ^= 1 << 3; #ifdef TIMER_CMP_IRQ_WORKAROUND TIMER16_0->IER &= ~TIMER16_FLAG_CMPM; // Запретить прерывание по CMP. #endif /* Обработчик CMP */ TIMER16_0->ICR = TIMER16_FLAG_CMPM; } #ifdef TIMER_CMP_IRQ_WORKAROUND if ((TIMER16_0->ISR & TIMER16_FLAG_ARRM) && (TIMER16_0->IER & TIMER16_FLAG_ARRM)) { TIMER16_0->ICR = TIMER16_FLAG_CMPM; TIMER16_0->IER |= TIMER16_FLAG_CMPM; // Разрешить прерывание по CMP. /* Обработчик ARR */ TIMER16_0->ICR = TIMER16_FLAG_ARRM; } #endif if ((TIMER16_1->ISR & TIMER16_FLAG_CMPM) && (TIMER16_1->IER & TIMER16_FLAG_CMPM)) { GPIO_1->OUTPUT ^= 1 << 3; #ifdef TIMER_CMP_IRQ_WORKAROUND TIMER16_1->IER &= ~TIMER16_FLAG_CMPM; // Запретить прерывание по CMP. #endif /* Обработчик CMP */ TIMER16_1->ICR = TIMER16_FLAG_CMPM; } #ifdef TIMER_CMP_IRQ_WORKAROUND if ((TIMER16_1->ISR & TIMER16_FLAG_ARRM) && (TIMER16_1->IER & TIMER16_FLAG_ARRM)) { TIMER16_1->ICR = TIMER16_FLAG_CMPM; TIMER16_1->IER |= TIMER16_FLAG_CMPM; // Разрешить прерывание по CMP. /* Обработчик ARR */ TIMER16_1->ICR = TIMER16_FLAG_ARRM; } #endif }