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
11 лет назад

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

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

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

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

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

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

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

KroshkaRu
KroshkaRu
10 лет назад

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

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);

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

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

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

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

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

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

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

Разобрался.

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

Не совсем конечно по теме но буду рад помощи. прочитал про ШИМ в 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канала.

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

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

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

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

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

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

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

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

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

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

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

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

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