В данном примере попробуем настроить самый простой таймер STM32. Под простым таймером будем понимать таймер с минимально возможными настройками. Для платы stm32f3discovery это таймер TIM2. Будем генерировать или выходить в прерывания с определенным интервалом, в методе прерывания будем изменять состояние вывода порта. У в нашем примере для нашей платы это вывод 8 на порте E.
Перед тем как начать работу с таймером, нам необходимо с начала подключить следующие файлы:
#include "stm32f30x.h" #include "stm32f30x_rcc.h" #include "stm32f30x_tim.h" #include "stm32f30x_gpio.h"
Первый файл импортируется по умолчанию и необходим для базовых настроек контроллера.
Файл с суффиксом RCC необходим для включения тактирования переферии микроконтроллера. Перед тем как использовать какую либо перефирию сначала на нее необходимо включить, подавая на нее тактовые импульсы, именно для этого и предназначен данный файл.
Файл с суффиксом TIM содержит все необходимы методы константы и переменные для настройки таймера.
Файл с суффиксом GPIO необходим для настройки порта. Подробнее о том что такое порты микроконтроллера stm32 было сказано в предыдущей статье.
GPIO_InitTypeDef GPIO_InitStructure; //Структура для настройки порта TIM_TimeBaseInitTypeDef TIM_InitStructure; //Структура настройки таймера
Структура представляет собой некий объект который содержит в себе ряд переменных которым нужно присвоить определенные значения. Названия всех структур и возможные значения переменных можно посмотреть в файле stm32f30x_tim.h - для таймера и stm32f30x_gpio.h - для порта.
void rccinit(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); }
Данные методы включают тактирование порта и таймера TIM2. Подробнее в фале stm32f30x_rcc.h
void init_timer(void) { TIM_InitStructure.TIM_Prescaler = 48000-1; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM2, &TIM_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_EnableIRQ(TIM2_IRQn); }
Очень важно соблюдать последовательность кода. Не правильно написанный код работать не будет, даже при условии хорошей компиляции иотсутсвии ошибок и предупреждений. Настраивая данный таймер я убил около получаса на то чтобы найти ошибку. Она была в прямом смысле перед носом. Оказывается что перед тем как произвести инициализацию таймера при помощи кода
TIM_TimeBaseInit(TIM6, &TIM_InitStructure);
Нужно сначала настроить структуру таймера TIM_InitStructure.
Код компилировался без ошибок и без проблем заливался в плату. Но так таймер был настроен "пустой" - непроинициализированной структурой, плата не подавала признаков жизни и лампочка не мигала должным образом, а точнее вообще не мигала.
Следующие строчкой мы настраиваем предделитель. Наш таймер затактирован от шины с частотой в 48Мгц. Выставляя предделительв 48000-1 мы тем самым делим тактовую частоту. В итоге наш таймар начинает "тикать" с частотой 48000000/48000=1000Гц. Т.е. за 1 секунду таймер сосчитает до 1000. Для того чтобы таймер каждую тысячу тиков сбрасывал свое значение нам нужно установить период. При помощи следующей конструкции:
TIM_InitStructure.TIM_Period = 1000;
В процессе счета таймер сравнивает свое текущее значение с установленным в структуре, если эти значения совпадают то происходит прерывание по переполнению. И наша программа уход в прерывание. Для того чтобы объяснить таймеру что нужно срабатывать при переполнении пользуемся методом
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
Следующие два метода включают таймер и разрешают прерывание.
TIM_Cmd(TIM2, ENABLE);
NVIC_EnableIRQ(TIM2_IRQn);
void init_led(void) { GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; GPIO_Init(GPIOE, &GPIO_InitStructure); }
Как настаивать порты было сказано ранее. Не будем на этом заморачиваться)
void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if(state) {GPIO_SetBits(GPIOE,GPIO_Pin_8);state=0;} else {GPIO_ResetBits(GPIOE,GPIO_Pin_8);state=1;} } } Метод прерывания таймера, в данном методе происходит изменения состояния вывода 8 порта E. Исходный код настройки таймера #include "stm32f30x.h" #include "stm32f30x_rcc.h" #include "stm32f30x_tim.h" #include "stm32f30x_gpio.h" uint16_t state=1; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if(state) {GPIO_SetBits(GPIOE,GPIO_Pin_8);state=0;} else {GPIO_ResetBits(GPIOE,GPIO_Pin_8);state=1;} } } GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_InitStructure; void rccinit(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); } void init_led(void) { GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; GPIO_Init(GPIOE, &GPIO_InitStructure); } void init_timer(void) { TIM_InitStructure.TIM_Prescaler = 48000-1; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM2, &TIM_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_EnableIRQ(TIM2_IRQn); } int main(void) { rccinit(); init_led(); init_timer(); while(1) { __NOP(); } }
Комментарии