В данном примере попробуем настроить самый простой таймер 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();
}
}
Комментарии