Top.Mail.Ru

STM32F3 и Timer. Настройка и использование таймера.

Часто возникает необходимость точного отсчета времени, то есть, например, нужно опрашивать состояние какого-либо датчика через равные промежутки времени. Можно, конечно, разместить всю работу с датчиком в главном цикле программы, вычислить точное количество операций контроллера, рассчитать время... Откровенно говоря, это не самый лучший вариант ) В данной ситуации на помощь может прийти таймер, которых в контроллерах STM32 очень много, причем как базовых, так и более продвинутых.

Вот как раз об этом и пойдет сегодня речь. Стоит оговориться, что статья будет очень краткая - сразу же будем писать пример программы - а причиной краткости является то, что по большому счету особых отличий в использовании таймеров в STM32F10x и STM32F3 нет, а для STM32F10x этот вопрос мы обсуждали довольно-таки подробно тут и тут.

Собственно, поэтому давайте сразу же переходить к программированию. Возьмем любой из базовых таймеров микроконтроллера STM32F3, произведем его минимальную настройку и попытаемся сгенерировать прерывания через равные промежутки времени. Максимально простой пример )

Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также небольшая подборка по таймерам из нового курса:

Итак, из 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);
	}
}


/***************************************************************************************/

Прошив программу в контроллер, наблюдаем мигающий синий светодиод, следовательно программа функционирует верно. В принципе, на этом все на сегодня, такая вот получилась краткая статейка )

Подписаться
Уведомить о
guest

20 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Nemo
Nemo
10 лет назад

Я бы лучше в обработчике сделал так:

void TIM2_IRQHandler()
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOE, GPIO_Pin_8);
}

Работает также,но кода меньше)

Сергей
Сергей
10 лет назад

С инициализацией таймера вроде разобрались ... А вот, интересно, как трактовать режим захвата ? Просчитать какой нибудь импульс ,для примера, например от часов ....

Евгений
Евгений
9 лет назад

А как узнать с какой частотой тактируется у меня таймер? По частоте шины с которой он связан? Я так понимаю она еще и изменяемая?

KroshkaRu
KroshkaRu
9 лет назад

обнаружил, что первые строчки должны выглядеть примерно так

TIM_TimeBaseInitTypeDef timer;
GPIO_InitTypeDef GPIO_InitStructure;
//*******************************************************************/
void initAll()
{
// Тактирование - куда ж без него
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

// На этом выводе у нас синий светодиод (STM32F3Discovery)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);

иначе ругается при компиляции,

А в целом, статья очень полезная!!!

Дмитрий
Дмитрий
9 лет назад

Подскажите пожалуйста! В Keil создаю новый сишный файл,пишу в нем функции, при компиляции валят ошибки ругается на то, что не видит новый созданный файл. В проекте этот файл лежит в той же папке где лежит main.

Дмитрий
Дмитрий
9 лет назад

Расскажите как правильно подключать файлы к проекту. Спасибо.

Дмитрий
Дмитрий
9 лет назад

Разобрался.

Паша
Паша
9 лет назад

Не совсем конечно по теме но буду рад помощи. прочитал про ШИМ в STM32 посмотрел пример для stm32f103 переделал под STM32F303VCT6.

void initAll() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // тактирую TIM1 на шине APB2.
gpio.GPIO_Mode = GPIO_Mode_AF;
// согласно даташиту PE13 альтернатива TIM1канал3
gpio.GPIO_Pin = GPIO_Pin_13;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOE, &gpio);

TIM1->CCER | = TIM_CCER_CC3E; // канал 3 ( CC3E ) для ШИМа.
TIM1->CCMR2 | = ( TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 ); // режим ШИМ
TIM1->PSC=10; // делитель
TIM1->ARR=1000; // период
TIM1->CCR3=200; // скважность
TIM1->CR1|= TIM_CR1_CEN; // запускаем таймер
}
int main() {
initAll();
while(1) { }
}

Все компилируется. но на PE13 ноль и никакого ШИМа. Пробовал перекинуть на PE9 канал1.
TIM1->CCER|=TIM_CCER_CC1E; // 1 канал для ШИМ
TIM1->CCMR2 | = ( TIM_CCMR2_OC1M_0 | TIM_CCMR2_OC1M_1 | TIM_CCMR2_OC1M_2 ); // а здесь у меня вылезла ошибка.
Указывая что можно только OC3M или OC4M что для меня не совсем понятно. Ведь OCxM где x-номер канала как я понял. А у таймера 4канала.

Паша
Паша
9 лет назад

Рано я взял TIM1 - это advanced timer и он я предполагаю настраивается по другому, поправьте если я ошибаюсь. Так же там есть какой то нехороший регистр BDTR который по умолчанию отключает таймер.

Дмитрий
Дмитрий
9 лет назад

Лучше возьмите базовые таймеры (TIM6 или TIM7).

Паша
Паша
9 лет назад

Дмитрий на них ШИМ не построишь, а на тот момент я хотел добиться именно этого.

Илья
9 лет назад

Здравствуйте, спасибо за ваши уроки - очень помогли. Вы не подскажите в чём разница между Tim1_CH1 и Tim1_CH1N. По даташиту для stm32f3 непонятно. Есть необходимость завести на ногу PE8 шим сигнал, у него альтернативная функция Tim1_CH1N. И шим на ней не работает. На PA8, где просто Tim1_CH1 все работает

Aleksey
Aleksey
Ответ на комментарий  Илья
9 лет назад

Эти выводы выводят один и тот же сигнал в противофазе для мостовых схем с целью управления трехфазными двигателями например от HDD. То есть на один вывод выводится плюс на другой минус.

Александр
6 лет назад

Здравствуйте.
Разве может таймер работать на 72МГц при максимальной частоте шины APB1 36МГц??

20
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x