Часто возникает необходимость точного отсчета времени, то есть, например, нужно опрашивать состояние какого-либо датчика через равные промежутки времени. Можно, конечно, разместить всю работу с датчиком в главном цикле программы, вычислить точное количество операций контроллера, рассчитать время... Откровенно говоря, это не самый лучший вариант ) В данной ситуации на помощь может прийти таймер, которых в контроллерах STM32 очень много, причем как базовых, так и более продвинутых.
Вот как раз об этом и пойдет сегодня речь. Стоит оговориться, что статья будет очень краткая - сразу же будем писать пример программы - а причиной краткости является то, что по большому счету особых отличий в использовании таймеров в STM32F10x и STM32F3 нет, а для STM32F10x этот вопрос мы обсуждали довольно-таки подробно тут и тут.
Собственно, поэтому давайте сразу же переходить к программированию. Возьмем любой из базовых таймеров микроконтроллера STM32F3, произведем его минимальную настройку и попытаемся сгенерировать прерывания через равные промежутки времени. Максимально простой пример )
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также небольшая подборка по таймерам из нового курса:
- STM32 и таймеры. STM32CubeMx. Настройка и использование.
- STM32 и watchdog. STM32CubeMx. Настройка модуля WWDG.
- STM32 и Timer Input Capture. Режим захвата сигнала.
- STM32CubeMx и watchdog. Настройка и использование IWDG.
Итак, из Standard Peripheral Library нам понадобятся парочка файлов, в которых реализовано взаимодействие с регистрами таймеров:
/***************************************************************************************/ #include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" #include "stm32f30x_tim.h" #include "stm32f30x.h" /***************************************************************************************/ TIM_TimeBaseInitTypeDef timer; /***************************************************************************************/
Минимальная инициализация таймера выглядит следующим образом. Кстати заодно настроим одну из ножек контроллера на работу в режиме выхода. Это нужно всего лишь для того, чтобы мигать светодиодом, куда без этого:
/***************************************************************************************/ void initAll() { // Тактирование RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // На этом выводе у нас синий светодиод (STM32F3Discovery) gpio.GPIO_Mode = GPIO_Mode_OUT; gpio.GPIO_Pin = GPIO_Pin_8; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &gpio); // А вот и долгожданная настройка таймера TIM2 TIM_TimeBaseStructInit(&timer); timer.TIM_Prescaler = 7200; timer.TIM_Period = 20000; TIM_TimeBaseInit(TIM2, &timer); /***************************************************************************************/
Тут стоит уделить внимание двум непонятно откуда взявшимся числам - 7200 и 20000. Давайте разберемся!
Таймер у меня тактируется частотой 72 МГц. Prescaler, он же предделитель, нужен для того, чтобы эту частоту делить. Таким образом, получаем 72 МГц / 7200 = 10 КГц. Значит один "тик" таймера соответствует (1 / 10000) секунд, что равняется 100 микросекундам.
Период таймера - это величина, досчитав до которой программа улетит на обработчик прерывания по переполнению таймера. В нашем случае таймер дотикает до 20000, что соотвествует (100 * 20000) мкс или 2 секундам. То есть светодиод (который мы зажигаем и гасим в обработчике прерывания) будет мигать с периодом 4 секунды (2 секунды горит, 2 секунды не горит). Теперь с этим все понятно, продолжаем...
В функции main() вызываем функцию инициализации, а также включаем прерывания и таймер. В цикле while(1) кода и того меньше - он просто пуст:
/***************************************************************************************/ int main() { __enable_irq(); initAll(); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_EnableIRQ(TIM2_IRQn); while(1) { } } /***************************************************************************************/
Все, осталось написать пару строк для обработчика прерываний, и дело сделано:
/***************************************************************************************/ void TIM2_IRQHandler() { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_8) == 1) { GPIO_ResetBits(GPIOE, GPIO_Pin_8); } else { GPIO_SetBits(GPIOE, GPIO_Pin_8); } } /***************************************************************************************/
Прошив программу в контроллер, наблюдаем мигающий синий светодиод, следовательно программа функционирует верно. В принципе, на этом все на сегодня, такая вот получилась краткая статейка )