Прерывание по переполнению и сравнению
В прошлой статье мы познакомились с таймерами (ссылка тут) Теперь давайте разберёмся с наиболее часто используемым режимом работы таймера, прерываниями по переполнению. Прерывания по переполнению ( и по сравнению ) широко применяются в системах реального времени: для генерации периодических событий, измерения временных интервалов, организации задержек и т.д. Коротко разберём что такое прерывания вообще, а затем что такое переполнение и сравнение.
Что такое прерывания?
Прерывания это механизм который реагирует на событие которое происходит с микроконтроллером. Бывают внешние (кнопки, датчики, сигналы с других устройств) и Внутренние (internal interrupts) – генерируются внутренними модулями микроконтроллера (таймеры, АЦП, UART и т. д.) и системные.
В нашем случае при работе с таймером генерируется внутреннее прерывание. Например наш таймер досчитал до нужного нам значения.Что происходит в этот момент ?
- Возникновение события .Когда таймер достигает своего максимального значения (переполнение) или заданного порога (сравнение), в соответствующем регистре флагов устанавливается бит события.
- Запрос прерывания.Если прерывания разрешены (бит разрешения в регистре настроен), микроконтроллер приостанавливает выполнение основного кода и переходит к обработчику прерывания. (подпрограмме в основном коде программы). При этом сохраняется точка куда программа вернётся после выполнения обработчика прерываний.
- Выполнение обработчика. Выполняется код нашего обработчика прерывания.
- Возврат в основную программу.Программа возвращается в то место откуда ушла в вектор прерываний.
Переполнение и сравнение
Прерывание по переполнению — это тип прерывания, которое срабатывает, когда счетчик таймера(в регистре ARR) достигает своего максимального значения. Возьмём таймер 4 в микроконтроллере STM32F407VGT6. Это 16 битный таймер а значит его максимальное значение 65535 (помним что счет начинается с нуля). на практике это означает что как только значение регистра ARR достигнет 65535 произойдёт прерывание.
Прерывание по сравнению — это тип прерывания, которое срабатывает когда счетчик таймера достигает заданного значения. Единственное отличие состоит в том что мы задаём значение в регистре ARR, что позволяет точно отсчитывать интервалы времени.
Рассмотрим как применить это на практике. Задача следующая — используя прерывания по сравнению заставить мигать светодиод на плате с периодом в одну секунду. Будем использовать таймер 2 в микроконтроллере STM32F407VGT6.Первая настройка это источник тактирования. Заходим в настройки таймера (Timers-TIM2) строка Clock Source и выбираем Internal Clock. Используем первый канал в режиме Input capture direct mode.
Далее нужно понять к какой шине подключен таймер. В нашем случае второй таймер подключен к шине APB1 Timers Clocks.(если не уверены, лучше убедится в этом в даташите )
Выставим значения Prescaler на 15999 и Counter period 999.
Перейдем к расчетам.Частота 16 МГц. Наш предделитель 15999 ( счёт начинается с нуля поэтому всегда на 1 меньше ).Частота после предделителя.
16 000 000 / 16000 = 1000 Гц
Чтобы получить время срабатывания.Берём то что задано в регистре Counter period + 1 и делим на полученное значение
1 000 / 1 000 (Гц) = 1 секунда
Не забудьте включить прерывания нашего таймера.
Код программы.
После генерации нашего кода необходимо добавить несколько строк для запуска и обработки нашего прерывания. В блоке кода добавляем следующие строки.
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF); HAL_TIM_Base_Start_IT(&htim2);
Первая строка сбрасывает флаг переполнения таймера (UIF — Update Interrupt Flag). Это важно сделать перед запуском таймера, чтобы прерывание не сработало немедленно из-за ранее установленного флага. Флаг устанавливается автоматически после инициализации в CubeIDE.
Вторая строка запускает таймер в режиме генерации прерываний и соответственно будет вызываться соответствующее прерывание.
Когда происходит переполнение таймера(счетчика таймера досчитывает до значения нашего регистра ARR, в нашем случае 999), программа переходит в вектор прерывания, соответствующий заданному таймеру — например, TIM2_IRQHandler для таймера TIM2. На этом этапе у нас есть два способа обработки прерывания:
- Работать напрямую с функцией обработчиком прерывания — TIM2_IRQHandler.
- Использовать callback-функцию HAL — HAL_TIM_PeriodElapsedCallback.
Если вы используете HAL-библиотеку, рекомендуется использовать именно HAL_TIM_PeriodElapsedCallback. Это более гибкий и безопасный способ, так как HAL сам вызывает этот callback внутри обработчика TIM2_IRQHandler, при этом сохраняя всю свою внутреннюю логику и совместимость. Если же вы не используете HAL или вам нужен максимальный контроль на низком уровне, тогда можно писать код прямо в TIM2_IRQHandler. В нашем случае мы используем HAL, поэтому будем обрабатывать прерывание через HAL_TIM_PeriodElapsedCallback. Например, в этой функции мы будем зажигать и гасить зеленый светодиод на нашей отладочной плате:
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef) { if(htim->Instance == TIM2)//Проверяем что TIM2 { //Переключаем состояние светодиода HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); } }
В результате наш диод моргает раз в секунду.