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

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

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

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

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

Понравилась статья? Поделись с друзьями!

STM32F3. Использование таймера.: 18 комментариев
  1. Я бы лучше в обработчике сделал так:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *