STM Урок 5. Библиотека HAL. STM32 CUBE MX. Таймеры



Урок 5

Библиотека HAL. STM32 CUBE MX. Таймеры

 

Сегодня мы попробуем поработать с таймерами также с помощью библиотеки HAL.

У нашего контроллера STM32F407 таймеров много.

Открываем reference manual и перейдём в главу 17 — Advanced-control timers (TIM1&TIM8).

Данные таймеры — это таймеры с расширенным управлением. Таких таймеров всего два

 

image00

 

Далее открываем следующий пункт 19 — General-purpose timers (TIM9 to TIM14).

Это таймеры общего назначения. У них функционал очень ограничен, зато их у нас целых шесть

image02

 

Далее идем в пункт 20.2 TIM6&TIM7 main features

Данные таймеры — базовые. Функционал немного расширен

 

 

image01

Также надо отметить особо таймеры 2 и 5, которые являются 32-битными, что не может не заинтересовать разработчика.

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

 

image04

 

 

Теперь мы научимся делать из предыдущего проекта следующий, чтобы заново не настраивать все в Cube и не потерять наш рукописный код в проекте Keil.

Для этого создадим папку TEST02.

Скопируем туда файлы .mxproject и TEST001.ioc

Исправим имя файла TEST001.ioc на TEST002.ioc

Внутри файлов также везде меняем TEST001 на TEST002 (получится в 5 местах).

Также копируем со старого проекта папки src и inc с содержимым. Всё!

Проект готов. Запускаем TEST002.ioc

Давайте пока воспользуемся базовыми таймерами, а вернее одним из них – таймером 6

В Cube MX ставим TIM6 -> Activated

 

image03

 

Пройдем в мануал и посмотрим (стр. 65-66), на какой шине какой таймер находится — это APB1 и настроим ее в Clock Configuration. Здесь главное не превысить граничную частоту (42 МГц).

 

image05

 

Давайте поставим делитель APB1 prescaler равным 8,частота получится 21 мГц, а для таймера будет 42. Нам этого вполне хватит.

Затем идем в закладку Configuration и настраиваем там таймер.

 

 

Жмем на TIM6

В закладке Parameter Settings выставляем следующие значения

Prescaler (PSC – 16 bit value) – 20999

Counter Period – 499

Trigger Event Selection – Update Event

 

image08

 

В закладке NVIC Settings включаем глобальные прерывания таймера 6

 

image09

 

Жмем Apply

 

 

Генерируем проект Keil5 таким же образом как и на прошлом занятии

Собираем проект в кейле. Открываем main.c – как видим ничего не пропало никуда.

Попробуем прошить и запустить. Удивительно. Все работает.

Далее подключаем таймер.

Открываем STM32F4HAL_User_manual.pdf

Откроем там раздел 61.3.3 Time Base functions

Найдем фукцию, запускающую таймер HAL_TIM_Base_Start

 

image06

 

Вставляем код в main.c

 

MX_GPIO_Init();

  MX_TIM6_Init();

  /* USER CODE BEGIN 2 */

                HAL_TIM_Base_Start(&htim6);

  /* USER CODE END 2 */

 

Также запускаем прерывания от таймера

Для этого есть аналогичная функция  HAL_TIM_Base_Start_IT

 

image07

 

Вставляем ее сюда же

  /* USER CODE BEGIN 2 */

                HAL_TIM_Base_Start(&htim6);

                HAL_TIM_Base_Start_IT(&htim6);

  /* USER CODE END 2 */

 

В файле stm32f4xx_it.c находим функцию прерывания от таймера и добавляем туда код

 

void TIM6_DAC_IRQHandler(void)

{

  /* USER CODE BEGIN TIM6_DAC_IRQn 0 */

 

  /* USER CODE END TIM6_DAC_IRQn 0 */

  HAL_TIM_IRQHandler(&htim6);

  /* USER CODE BEGIN TIM6_DAC_IRQn 1 */

        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);

  /* USER CODE END TIM6_DAC_IRQn 1 */

}

 

Из бесконечного цикла уберем вот эту строку, чтобы не мешала работать таймеру

 

else HAL_GPIO_WritePin(GPIOD, PIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

 

Прошиваем, смотрим.

 

Теперь попробуем разнообразить

Файл main.h из старого проекта положим в папку inc и подключим его в главном модуле main.c

 

/* Includes ——————————————————————*/

#include «stm32f4xx_hal.h»

 

/* USER CODE BEGIN Includes */

#include «main.h»

/* USER CODE END Includes */

 

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

 

#ifndef MAIN_H_

#define MAIN_H_

 

uint8_t tim6_counter;

 

#endif /* MAIN_H_ */

 

Проинициализируем ее в функции main

 

  /* USER CODE BEGIN 2 */

                HAL_TIM_Base_Start(&htim6);

                HAL_TIM_Base_Start_IT(&htim6);

                tim6_counter=0;

  /* USER CODE END 2 */

 

Проэкстерналим в файле stm32f4xx_it.c

 

#include «stm32f4xx_hal.h»

#include «stm32f4xx.h»

#include «stm32f4xx_it.h»

 

/* USER CODE BEGIN 0 */

extern uint8_t tim6_counter;

/* USER CODE END 0 */

 

Изменим код в функции прерывания в этом же файле

 

void TIM6_DAC_IRQHandler(void)

{

  /* USER CODE BEGIN TIM6_DAC_IRQn 0 */

 

  /* USER CODE END TIM6_DAC_IRQn 0 */

  HAL_TIM_IRQHandler(&htim6);

  /* USER CODE BEGIN TIM6_DAC_IRQn 1 */

        switch(tim6_counter)

        {

                case 0:

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);

                        break;

                case 1:

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);

                        break;

                case 2:

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET);

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);

                        break;

                case 3:

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);

                        break;

        }

        if(tim6_counter<3) tim6_counter++;

        else tim6_counter=0;

  /* USER CODE END TIM6_DAC_IRQn 1 */

В файле main.c уберем запуск таймера

                HAL_TIM_Base_Start(&htim6);

                HAL_TIM_Base_Start_IT(&htim6);

 

А в бесконечном цикле код изменим следующим образом

 

  while (1)

  {

  /* USER CODE END WHILE */

 

  /* USER CODE BEGIN 3 */

                if(HAL_GPIO_ReadPin (GPIOA, GPIO_PIN_0)==GPIO_PIN_SET)

                {

                        HAL_TIM_Base_Start(&htim6);

                        HAL_TIM_Base_Start_IT(&htim6);

                }

                else

                {

                        tim6_counter=0;        

                        HAL_TIM_Base_Stop(&htim6);

                        HAL_TIM_Base_Stop_IT(&htim6);

                        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

                }

  }

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

 

Купить плату можно здесь STM32F4-DISCOVERY

 

Смотреть ВИДЕОУРОК

 

STM32 HAL. STM32 CUBE MX. Таймеры

39 комментариев на “STM Урок 5. Библиотека HAL. STM32 CUBE MX. Таймеры
  1. Aneg:

    Как сделать так, что бы прерывание возникало при нажатии на кнопку?

  2. Valera:

    Спасибо огромное за изложенный материал , но в данном посте не могу найти     stm32f4xx_it.c  в исходниках .

  3. Сергей:

    а имеет ли смысл вызывать два раза функцию 

     HAL_TIM_Base_Start(&htim6);

     HAL_TIM_Base_Start_IT(&htim6); ?

    кажется вполне достаточно одного вызова

    HAL_TIM_Base_Start_IT(&htim6);

    Не даны пояснения, в чем разница между двумя этими функциями

     

  4. Siroeshka:

    У меня прерывание (использую таймер 6) срабатывает сразу как только запускается таймер.Как сделать так чтобы прерывание срабатывало только по периоду(То есть запускается таймер, проходит период и запускается прерывание)?

  5. Михаил:

    Здравствуйте! Спасибо за Ваши уроки, очень помогают в освоении STM32!

    Вопрос следующий- нужно ли очищать флаги прерываний или HAL делает это сам? У меня возникает проблема с ложным вызовом прерывания от кнопки, хотя кнопка подтянута и программно и резистором к + питания, возможно прерывания от других модулей дают такой эфект? Спасибо! 

    • Здравствуйте! Флаги очищать не надо. Если Вы откроете автообработчик включенных в Cube глобавльных прерываний, то увидите там сброс флага.

  6. Михаил:

    Спасибо, понятно! А вот что вызывает ложное срабатывание на внешнем прерывании я так и не разобрался, у меня 2 USARTA 1 I2C, SDIO и EXTI. Обработчик написан только для EXTI и раз в час на нем происходит ложный вызов. При двойной проверки внутри обработчика на низкий уровень пина проблема вроде-как решилась. Может у кого то была подобная проблема? И Как грамотно распределить приоритеты прерываний? я их не трогал — везде 0.

  7. Alex:

    делаю так как написано.

    выбивает ошибку:

    ../Src/main.c(247): error:  #159: declaration is incompatible with previous "_Error_Handler"  (declared at line 150)

    что не так?

    хотя прикреплённый архив с исходниками запускается нормально

     

  8. Alex:

    сразу же отвечу:

    _Error_Handler(__FILE__, __LINE__); переименовал в Error_Handler(); —

    ещё небыло декларации, сделал:

    void SystemClock_Config(void);
    void Error_Handler(void);
    static void MX_GPIO_Init(void);
    static void MX_TIM6_Init(void);

     

    А также сыпало ошибки в других файлах, … Error: L6200E: Symbol tim6_counter multiply defined …

    решение — в файле main.h делаем extern uint8_t tim6_counter; , а в файле stm32f4xx_it.c — просто uint8_t tim6_counter;

    Источник — http://forum.easyelectronics.ru/viewtopic.php?f=35&t=24247

     

  9. Андрей:

    Если мы откроем reference manual и перейдём в главу 17, а именно на страницу 519, то увидим диаграммы. Из которых следует что если мы хотим разделить частоту шины на 2, то prescaler нужно установить 2, если на 4, то 4. а если 21000, то 21000. И единицу отнимать не нужно.

    А вот в Counter Period единицу отнимать нужно. Это видно на диаграмме и написано в reference manual. Цитата: "If the repetition counter is used, the update event (UEV) is generated after upcounting is repeated for the number of times programmed in the repetition counter register plus one (TIMx_RCR+1)."

    • Денис:

      Только знакомлюсь с микроконтроллерами, не подскажите, где именно в  reference manual такое написано, просмотрел с 518 страницы, что-то не увидел. А в диаграммах пока не очень разбираюсь.

      • Денис:

        С Counter Period понятно, что надо -1, т.к. счет с 0 начинается. А вот с предделителем действительно интересно, почему везде его на -1 уменьшают.

    • Александр:

      В документе AN4776 «STM32 Timer cookbook» можно найти истину:
      /* Set the Timer prescaler to get 8MHz as counter clock */
      Prescaler = (uint16_t) (SystemCoreClock / 8000000) — 1;

      в HAL таким же образом нужно отнимать единицу, он сам нигде это не делает.

  10. Сергей:

    Здравствуйте. Подскажите, пожалуйста, в каких случаях срабатывают прерывания TIM2_IRQHandler? Т.е. если я использую данный таймер с прерываниями (как у Вас в статье), необходимо ли мне производить доп. проверку в функции TIM2_IRQHandler или у таймеров только по одному событию формируется прерывание?
    Например, как в АЦП, есть общие прерывания (HAL_ADC_IRQHandler), а есть отдельная функция HAL_ADC_ConvCpltCallback(), которую более предпочтительнее использовать. Есть ли аналогично в таймерах? Спасибо.

    • Здравствуйте!
      Не нужно никаких проверок. Урок уже сравнительно не новый, поэтому используется такая обработка. Лучше использовать коллбэки и ничего в этом файле не проверять. Если Вы посмотрите дальнейшие уроки, то мы давно уже так и делаем.

      • Сергей:

        Подскажите, а какая функция коллбэк для таймеров? Для АЦП HAL_ADC_ConvCpltCallback(), а какая для таймера?

      • Сергей:

        Нашёл у Вас в 21-м уроке функцию HAL_TIM_PeriodElapsedCallback(). Подскажите, а в чём преимущества перед обычным обработчиком прерывания? И как определять какой таймер вызвал данную колбэк функцию, сравнивать в данной функции указатели?

  11. Владимир:

    Большое спасибо на уроки!
    Скажите пожалуйста, никак не могу написать свой ГУИ для управления светодиодами и опроса кнопок.
    Юзаю libUSB для C#

    Какие ID должны быть в посылках со стороны ПК на МК ?

  12. Kalin:

    Здравствуйте, nordstream, спасибо большое за ваши видео, которые я пробовал пару раз, чтобы сделать это упражнение, без успеха дает мне ряд ошибок, когда я создаю переменную uint8_t, вы можете мне помочь. спасибо

    • Здравствуйте
      подключите библиотеку stdint.h

      • Trackout:

        Та же ошибка с unit8_t. Искал в ваших исходниках, где подключается данная библиотека — не нашел ни в одном из файлов — stdint.h нет нигде. Команда подключения библиотеки #include «stdint.h» тоже не исправляет ошибку. Хотя, видно, что файл stdint.h присутствует в Keil в раскрывающимся списке файла main.c. Видимо, вы что-то недорассказали в этом уроке.

  13. Егор:

    это не урок, а лишь представление о том, что нужно тыкнуть, чтобы что-то работало. Урок состоит в объяснении принципов работы. А тут если в Кубе лень нажать пару портов на выход и написании дергании ножек и из-за этого заниматься ******** с перекидыванием файлов из одной папки в другу и переименовыванием названий…это сильно!

    • Я старался по максимуму на первых порах объяснять что и почём. Если Вы считаете, что это для Вас «низко», то переходите сразу к последним урокам.
      И в следующий раз прошу воздерживаться от слов-паразитов в комментариях, глобальная публичная сеть всё-таки, а не базар.

  14. Здравствуйте! Извините, только начал изучать stm,
    можно ли изменить sounter period во время работы программы?

  15. Александр:

    Для глобального счётчика надо подписывать модификатор volatile, у меня без него оптимизатор ломал остановку таймера в цикле по условию. (F103/CubeIDE)

  16. Александр:

    Надо в main.h добавить директиву #include «stdint.h»

  17. Bogdan:

    C новым годом !
    Спасибо за великолепные уроки
    повторил на Stm32f103c8t6
    В качестве таймера TIM2 HAL_TIM_Base_Start_IT(&htim2);

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

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

*