Таймеры - прерывания
Базовая работа с таймерами, использование прерываний
Часто, нам надо выполнять какие-то действия с фиксированной частотой или, наоборот, немедленно реагировать на внешние события. Обе этих задачи решаются прерываниями.
Разберем самый простой пример, и попробуем менять частоту мигания диода по синусоиде с помощью прерываний и таймеров. Будем использовать настройку выходов микроконтроллера из предыдущего раздела.
Таймеры
Настройка внутреннего тактирования
Откроем Clock Configuration. Первое изменение, которое надо сделать, это переключить с HSI на HSE (слева) и Input frequency усттановить 12. Далее, HCLK установить в 160 МГц. Это даст всей переферии (справа), включая таймеры, базовую частоту в 160 МГц. Можно указать и другое значение, в зависимости от задачи, но здесь мы будем работать с этим.
Настройка частоты таймера
Настроим третий таймер (TIM3).
Clock Source - Internal Clock. Это "включает" таймер в базовом варианте. Prescaler - "делитель" для частоты таймера. Поделив частоту таймера на 16000, мы получаем таймер с частотой 10 КГц. Counter Period - значение, по достижению которого счетчик таймера сбрасывается.
Также в NVIC Settings поставим галочку на TIM3 global interrupt. Теперь, по достижению значения counter period, таймер не только сбрасывается, но и вызывается прерывание (код для которого будет сгенерирован в stm32g4xx_it.c
). Учитывая, что частота таймера 10 КГц, а период 10000, мы получили прерывание, вызывающееся 1 раз в секунду.
Обратите внимание, что из всех числовых параметров в редакторе вычитается единица (16000-1
, 10000-1
). Это связано с тем, что на самом деле prescaler при значении 0, конечно, делит входящую частоту не на 0, а на 1, соответственно его реальное значение на единицу больше. А counter ведет отсчет от 0.
Код прерывания и управление периодом
Заведем в Inc/main.h
переменную extern double x;
, отвечающую за "время" - ось X для нашего синуса. Этот хедер включается и в main.c, и в stm32g4xx_it.c
, что позволит нам использовать эту переменную в обеих файлах.
Вообще говоря, использовать глобальные переменные как разделяемое (shared) состояние в разных файлах - антипаттерн и крайне подверженная багам конструкция (как со стороны программиста, так и со стороны компилятора - например "initialization order fiasco"). Но в этом туториале для наглядности мы воспользуемся одной глобальной переменной.
Инициализируем x
в main.c
:
Запустим прерывания внутри блока USER CODE
в main
, после всех инициализаций но до бесконечного цикла:
И в главном цикле будем увеличивать его значение со временем в диапазоне от 0 до 2:
В файле ы CubeIDE для нас сгенерировала функцию-обработчик прерывания таймера. В ней, мы будем делать две вещи:
Переключать состояние диода.
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
Менять значение counter period таймера по синусоиде, чтобы частота переключений плавно менялась.
__HAL_TIM_SET_AUTORELOAD(&htim3, (sin(3.14*x)+1.2)*500);
. Мы пользуемся третьим таймером,x
меняется от 0 до 2, значение синуса нам надо немного "поднять" над 0, поэтому+1.2
, и*500
, так как получившееся до этого значение лежит только в пределе [0.2, 2.2]
Приведем код функции прерывания целиком:
Все! Залив этот код на контроллер, мы получим мерцающий по синусоиде светодиод.
Last updated