Урок 19. Установка конфигурации таймеров STM32 с помощью библиотек CMSIS, HAL и LL. Логика работы прерывания таймера.

Уроки STM32

Научимся задавать конфигурацию таймеров в режиме счетчиков через CMSIS, HAL и LL библиотеки. Разберемся, как работать с прерываниями таймеров.

Предыдущий урок     Список уроков     Следующий урок

Разработаем проект, функционально повторяющий наш первый проект из урока 3. Напомню, что мы реализовали программу, которая мигала светодиодом, подключенным к  выводу PC13.

 

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

Сделаем 3 варианта проектов с использованием библиотек CMSIS, HAL и LL.

В предыдущем уроке я рассказывал о действиях, которые необходимо выполнить для нормальной работы прерываний от периферийных устройств. Достаточно подробно описал, что надо сделать, кроме предпоследнего пункта. А именно, я не рассказал, как настроить периферийное устройство для формирования запроса прерывания.

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

Для начала разберемся, как работают прерывания по таймерам.

 

Логика работы прерываний таймеров STM32.

Для примера возьмем самый сложный таймер – TIM1.

Вот диаграмма работы таймера в режиме прямого счета со значением регистра перезагрузки равным 36.

Диаграмма работы счетчика

Счетчик считает входные импульсы. Когда его значение достигает 36, то он сбрасывается и начинает отсчет заново.

Обратите внимание на сигнал Update event (UEV). Это внутренний сигнал микроконтроллера. Он формируется в момент сброса или перезагрузки счетчика.

UEV – сигнал аппаратного события по перезагрузке таймера. Я бы назвал его состоянием события. Т.е. событие в данный момент есть или нет, без запоминания. На диаграмме видно, что сигнал сформировался при перезагрузке, а после первого же тактового импульса стал неактивным.

Но сигнал события перевел в активное состояние бит Update interrupt flag (UIF). Это уже бит в регистре состояния таймера, доступном для программного обращения. UIF устанавливается  аппаратным сигналом UEV. А сбрасываться он может только программно, как правило, после завершения обработки прерывания.

Если прерывание запрещено, не важно, глобально, в регистре разрешения прерываний NVIC или  в регистре разрешений прерываний таймера, то оно будет ждать своего времени. Бит UIF будет оставаться в активном состоянии, пока его не сбросит программа. И при появлении необходимых разрешений произойдет прерывание, даже если перезагрузка таймера была давно и с нынешним состоянием алгоритма программы никак не связана. Этот флаг лучше сбрасывать при инициализации таймера для исключения ложных, забытых прерываний.

И еще есть бит Update interrupt enable (UIE), с помощью которого можно запретить прерывания по перезагрузке таймера. Расположен он в регистре разрешения прерываний таймера. UIE дублирует функции бита из регистра разрешения прерываний NVIC.

Итак:

  • UEV – сигнал аппаратного события. Появляется при перезагрузке таймера.
  • UIF – бит регистра состояния таймера (флаг прерывания).
    • Становится активным по UEV.
    • Инициирует прерывание, при разрешении в регистрах конфигурации.
    • Сбрасывается только программно после обработки прерывания.
  • UIE – бит разрешения прерывания. Необходим для формирования запроса прерывания от таймера.

 

Регистры таймера.

Подробно можете посмотреть форматы регистров таймера в документации на микроконтроллер. Надеюсь, когда-нибудь переведу ее на русский язык и создам на сайте справочные страницы по STM32. Сейчас совсем нет времени. Большая часть информации должна быть понятна без перевода. То, что нам сейчас необходимо я поясню.

Из урока 16 мы должны помнить основные регистры таймера, определяющие временные параметры.

Схема счетчика

TIMx_CNT

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

TIMx_PSC

Регистр предделителя.

TIMx_ARP

Регистр перезагрузки.

TIMx_RCR

Регистр счетчика повторов.

TIMx_CR1

Регистр управления. Задает режим работы таймера.

Формат регистра управления

  • Биты 9-8 (CKD) задают значение делителя частоты для внутренних нужд таймера.
  • Бит 7 (ARPE) определяет режим записи в регистр перезагрузки: буферизирован или нет.
  • Биты 6-5  (CMS) определяют режим для двунаправленного счета (01-11) или разрешают счет только в одном направлении (00).
  • Бит 4 (DIR) задает направление счета: прямой или реверсивный (0 или 1).
  • Битом 3 (OPM) можно задать режим одиночного импульса. Если он равен 1, то при перезагрузке счетчик останавливается.
  • Бит 2 (URS) меняет логику формирования события UEV. При 0, к естественному формированию события перезагрузки таймера добавляются, программная инициация события с помощью бита UG и перезагрузка от ведомого таймера.
  • Бит 1 (UDIS) управляет разрешением перезагрузки счетчика. Если задать его равным 1, то автоматическая перезагрузка счетчика происходить не будет.
  • Бит 0 (CEN) разрешает работу счетчика. Если он не установлен, то счетчик полностью отключен.

TIMx_DIER

Регистр разрешения прерываний.

В нем нас интересует бит 0 (UIE) – разрешение прерывания по перезагрузке. Прерывание разрешено при состоянии бита равном 1.

TIMx_SR

Регистр состояния. Содержит флаги прерываний.

Нам интересен бит 0 (UIF) – флаг прерывания по переполнению.

TIMx_EGR

Регистр генерации событий. Позволяет искусственно формировать события.

Для нас может быть интересен бит 0 (UG) –генерация перезагрузки. Производится записью в него 1. Сбрасывается аппаратно.

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

TIMx_SMCR

Регистр управления ведомым режимом таймера.

Нас в нем интересуют только младшие 3 бита (SMS). Для того чтобы задать тактирование таймера от шины APB, надо записать в них нули.

 

Работа с таймером в режиме счетчика с прерыванием через библиотеку CMSIS.

Теперь давайте свяжем воедино информацию последних 3 уроков и напишем программу мигающую светодиодом с частотой 2 раза в секунду. Работать с таймером и прерываниями будем через регистры CMSIS.

Создаем проект Lesson19_1.

С помощью STM32CubeMX задаем:

  • тактирование – на максимальную частоту 72 мГц;
  • PC13 – активный выход.

Открываем проект в Atollic TrueStudio.

Начинаем с конфигурации таймера. Нам надо задать:

  • режим – прямой счет;
  • предделитель – 719, что соответствует периоду тактовых импульсов счетчика 10 мкс;
  • значение перезагрузки – 24999, в результате период будет равен 250 мс.

В файле main.c начинаем заполнять блок конфигурации таймера.

/* USER CODE BEGIN SysInit */

// конфигурация таймера 1

Надо разрешить тактирование таймера 1. Он подключен к шине APB2.

// конфигурация таймера 1

RCC -> APB2ENR |= RCC_APB2ENR_TIM1EN;

Задаем для таймера внутреннее тактирование, шина APB. Сбрасываем 3 младших бита регистра TIMx_SMCR.

TIM1->SMCR &= ~ TIM_SMCR_SMS;

Устанавливаем режим работы таймера.

TIM1 -> CR1 = TIM_CR1_CEN;

Получилось, что в регистр управления необходимо записать слово только с единицей в одном разряде. Я сделал загрузку всего слова, т.е. не только установил 1 в младшем разряде, а и записал во все остальные разряды нули.

Регистр предделителя и перезагрузки.

TIM1->PSC = 719;
TIM1->ARR = 24999;

Разрешаем прерывания в регистре разрешения таймера.

TIM1->DIER |= TIM_DIER_UIE;

И в регистре контроллера прерываний.

NVIC_EnableIRQ (TIM1_UP_IRQn);

В конце файла stm32f1xx_it.c размещаем функцию обработки прерывания.

/* USER CODE BEGIN 1 */

void TIM1_UP_IRQHandler(void) {

    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    TIM1->SR &= ~ TIM_SR_UIF;
}

В ней инверсия состояния вывода PC13 и сброс флага прерывания таймера UIF.

Осталось в файл stm32f1xx_it.h добавить прототип функции обработки прерывания.

/* Exported functions prototypes ---------------------------------------------*/
void TIM1_UP_IRQHandler(void);

Все. Компилируем, загружаем, проверяем.

Проект можно загрузить по ссылке:

  Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!  

Теперь понять, что делают функции библиотек HAL и LL будет значительно проще.

 

Управление таймером с помощью библиотеки HAL.

Прежде просмотрите справочную информацию по HAL-функциям работы с таймером в режиме счетчика.

Создаем проект Lesson19_2 с установками аналогичными предыдущему проекту Lesson19_1.

Открываем его в Atollic TrueStudio.

В файле Inc/stm32_hal_conf.h разрешаем работу драйвера таймеров. Открываем от комментариев строку:

#define HAL_TIM_MODULE_ENABLED

В файле main.c начинаем заполнять блок конфигурации таймера.

/* USER CODE BEGIN SysInit */

// конфигурация таймера 1

Разрешаем тактирование таймера. К какой шине подключен таймер, функция определяет сама.

__HAL_RCC_TIM1_CLK_ENABLE();

Для установки конфигурации используем функцию HAL_TIM_Base_Init().

Посмотрите внимательно ее описание. Все просто.

Создаем структуру типа TIM_HandleTypeDef. Через эту структуру задаются параметры таймера.

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim1;

В связи с тем, что мы будем использовать HAL-функции и в файле для обработчиков прерываний, то определяем структуру, как глобальную переменную. Т.е. размещаем ее определение до функции main().

В блоке ”конфигурация таймера 1” заполняем структуру инициализации.

htim1.Instance = TIM1;
htim1.Init.Prescaler = 719;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 24999;
htim1.Init.RepetitionCounter = 0;

И вызываем функцию инициализации.

HAL_TIM_Base_Init(&htim1);

С помощью функции HAL_TIM_ConfigClockSource() задаем внутреннее тактирование таймера, от шины  APB.

Для этого определяем структуру типа TIM_HandleTypeDef, задаем источник тактирования и вызываем функцию установки конфигурации тактирования.

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);

Разрешаем работу таймера и прерывания функцией HAL_TIM_Base_Start_IT().

HAL_TIM_Base_Start_IT(&htim1);

И в регистре контроллера прерываний.

NVIC_EnableIRQ (TIM1_UP_IRQn);

В конце файла stm32f1xx_it.c размещаем функцию обработки прерывания.

/* USER CODE BEGIN 1 */

void TIM1_UP_IRQHandler(void) {

    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_TIM_IRQHandler(&htim1);
}

Завершаем обработку прерывания функцией HAL_TIM_IRQHandler.

Структура htim1 определена в файле main.c. Поэтому надо объявить ее и в файле stm32f1xx_it.c.

/* External variables --------------------------------------------------------*/
/* USER CODE BEGIN EV */
extern TIM_HandleTypeDef htim1;

Проект можно загрузить по ссылке:

  Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!  

Все. Компилируем, загружаем в плату, проверяем.

 

Настройка таймера и прерываний от него через функции библиотеки LL.

Делаем то же самое, только с помощью библиотеки LL.

Опять советую просмотреть справочную информацию по функциям библиотеки LL, ориентированным на работу с таймерами.

Создаем проект 19_3 с установками аналогичными предыдущим проектам этого урока. Открываем его в Atollic TrueStudio.

Можно установить конфигурацию таймера в STM32CubeMX и в расширенных настройках указать, что необходимо использовать библиотеку LL. Тогда STM32CubeMX не только настроит таймер, но и создаст необходимую конфигурацию проекта. В ней автоматически будут сформированы пути к заголовочным и исходным файлам библиотеки LL.

Но давайте сделаем по-другому. Так, как мы задавали конфигурацию портов GPIO в уроке 9. Тогда мы копировали нужный h-файл библиотеки LL в папку Drivers\STM32F1xx_HAL_Driver\INC\  и подключали его в main.h.

Таким способом можно использовать встроенные функции библиотеки. Они полностью определены в заголовочном файле. Нет необходимости подключать к проекту c-файл с телами функций. Все есть в h-файле.

Для определения встроенных функций используется ключевое слово _INLINE. Применение таких функций значительно ускоряет работу программы. Вызов встроенной функции компилятор заменяет кодом тела функции. Т.е. фактически вызова не происходит. Похоже на директиву #define.

Большинство функций LL-библиотеки встроенные. Это логично. Библиотека ориентирована на простые обращения к регистрам.

Итак. По аналогии с действиями в уроке 9 копируем драйвер stm32f1xx_ll_tim.h в папку Drivers\STM32F1xx_HAL_Driver\INC\. Туда же копируем драйвер stm32f1xx_ll_bus.h. Он необходим для управления тактированием таймера от шины APB.

Подключаем заголовочные файлы в main.h.

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "stm32f1xx_ll_tim.h"
#include "stm32f1xx_ll_bus.h"

В файле main.c начинаем заполнять блок конфигурации таймера.

/* USER CODE BEGIN SysInit */

// конфигурация таймера 1

Разрешаем тактирование таймера TIM1.

LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

Задаем внутреннее тактирование.

LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);

Устанавливаем режим таймера.

LL_TIM_EnableCounter(TIM1);  // разрешить счетчик 1
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);  // прямой счет
LL_TIM_EnableUpdateEvent(TIM1);  // разрешить перезагрузку

Устанавливаем временные параметры.

LL_TIM_SetPrescaler(TIM1, 719);  // предделитель = 719
LL_TIM_SetAutoReload(TIM1, 24999);  // перезагрузка при 24999
LL_TIM_SetRepetitionCounter(TIM1, 0);  // без повтора

Разрешаем прерывания в регистре разрешения таймера.

LL_TIM_EnableIT_UPDATE(TIM1);  // разрешение прерывания

И в регистре контроллера прерываний.

NVIC_EnableIRQ (TIM1_UP_IRQn);

В конце файла stm32f1xx_it.c размещаем функцию обработки прерывания.

/* USER CODE BEGIN 1 */

void TIM1_UP_IRQHandler(void) {

    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    LL_TIM_ClearFlag_UPDATE(TIM1);  // сброс флага прерывания
}

В ней инверсия состояния вывода PC13 и сброс флага прерывания таймера.

Осталось в файл stm32f1xx_it.h добавить прототип функции обработки прерывания.

/* Exported functions prototypes ---------------------------------------------*/
void TIM1_UP_IRQHandler(void);

Вот проект полностью:

  Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!  

Все. Можно проверять.

Мне больше нравится работать с библиотекой LL. Благодаря тому, что мы используем встроенные LL-функции быстродействие программы не уступает обращению к CMSIS-регистрам. А сами функции информативнее и понятнее, чем биты CMSIS.

 

В следующем уроке начнем работать с последовательным интерфейсом UART.

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 4 дня

Эдуард

50
Комментарии: 1293Публикации: 147Регистрация: 13-12-2015

7 комментариев на «Урок 19. Установка конфигурации таймеров STM32 с помощью библиотек CMSIS, HAL и LL. Логика работы прерывания таймера.»

  1. Выскажу свое мнение. Подача уроков в корне неверная. Напоминает наши ВУЗы, где сначала пол года теории, и студенты без практики ничего не воспринимают.
    Автор, сначала заинтересуйте читателя реальными проектами «в железе»: подайте несколько простых уроков (помигать светодиодами, подключить кнопку, экранчик 20*4 и т.п.). И «в процессе» подключения той же кнопки можно сделать 2 урока:
    — на первом обработать кнопку программно.
    — на втором — задействовать таймер и рассказать, какие мы молодцы, освободили процессорное время и т.п.
    Впустую описывать регистры контроллера и таймеры — никому не интересно, это в таком же сухом виде можно в даташите посмотреть. Нельзя за 19 уроков не продвинуться в практической реализации вообще никуда.
    Создавайте какой-то реальный проект, а регистры, таймеры, классы C++ — описывайте тогда, когда они применяются на практике.
    Думаю, конечно, что автор меня не услышит. Но фактическое количество комментариев под уроками ясно дает понять, что текущий стиль изложения «не заходит».

    0
    • Здравствуйте!
      Категорично спорить не буду.
      Не получается осознано сделать даже несложные проекты без минимума знаний. Для STM32 этот минимуму не очень маленький. А делать проекты по принципу подключил, загрузил и вуаля, я не буду.
      Насчет того, что вся информация есть в даташитах не соглашусь. Я забил в поисковой строке типы данных для STM32. Ничего определенного не получил.
      Да, сейчас идут уроки без реальных проектов. Но они учат чисто практическим действиям. Теории у меня в уроках не много. И не думаю, что они похожи на вузовские уроки.
      Собираюсь рассказать еще об UART, и после этого уже подключать дисплеи, двигатели и т.п.
      В чем я полностью принимаю упреки — это в непоследовательности материала. Но я не пишу тщательно распланированный фундаментальный труд. Все происходит по остаточному принципу, есть время — напишу урок.
      И добавлю, что я всегда рад объективной критике. Я не понимаю людей, у которых в работе находишь ошибку, а они вместо благодарности смотрят на тебя волком.

      0
  2. Полностью согласен с Евгением… Очень долгое время порог вхождения стм был очень высокий. Портянка кода на страницу только инициализация. Кого хочешь отпугнет. Опять же… В сети достаточно разного уровня уроков. А по той же FREERTOS просто информационный голод. ИМХО

    0
  3. Всем привет. Я сам не однократно думал на тему как же всё таки эффективно преподносить материал для аудитории так, чтобы всем сразу было понятно и приятно всё сразу понять. В итоге пришел к выводу что идеальной подачи информации для всех не существует. Есть две группы людей. Первая те кто сами хотят во всём разобраться (они из любого материала берут то, что им нужно), вторая группа это те кто думают, что им кто-то быстро должен рассказать так, чтобы они сразу всё поняли. Второй группе нужен именно репетитор, который по обратной связи поймет, что не понятно слушателю и мгновенно подкорректирует свой рассказ в нужную сторону. Материал о программировании микроконтроллеров достаточно разносторонний и включает в себя много специфической терминологии. Достаточно произнести хотя бы одно слово, которое не понятно слушателю. И весь дальнейший материал непонятен. Большую роль так же играет количество начальных знаний самого слушателя в области электротехники, программирования и т. д. и т. п.
    Если бы преподавателю в ВУЗе платили бы не за количество академических часов, а за качественно усвоенный материал каждым студентом, то вы можете догадываться сколько получали бы преподаватели. Вот и получается что создавать универсальный урок, который бы влез во всех одинаково достаточно сложно. Для кого-то урок будет нудным, для кого-то слишком непонятным. Лично я благодарен автору за его труд и потраченное время. На мой взгляд только индивидуальные уроки принесут самый хороший результат в области обучения этой сферы. Автору зачёт. Разумная критика имеет место быть.

    0
    • Здравствуйте!
      Спасибо за обсуждение этой темы.
      Я подхожу к вопросу полезности уроков немного проще. Надо решить конкретную задачу. Например, сформировать прерывание по таймеру. И я стараюсь осветить все вопросы, связанные с реализацией этой задачи.
      Разработка программ и состоит из решения конкретных локальных задач.
      По моему мнению, именно полного, комплексного представления конкретных тем и не хватает в интернете.
      Наберите в поисковой строке «Типы данных STM32». Есть типы данных для реализаций Си, общие таблицы, понятия. Но кому-то необходимо использовать тип double, и где узнать его формат?
      По поводу того, что уроки скучные я не согласен. Да, пока в результате не выходят рабочие проекты. Просто сейчас идет подача материала на уровне компонентов микроконтроллера. Неужели не интересно учиться работать с ними! Без этих знаний невозможно разрабатывать программы. Со временем будут рабочие проекты. Но пока не хватает информации для этого.
      Вам больше нравится общепринятый подход – Загрузите программу в микроконтроллер и вуаля! Но это не обучение программированию, а реализация готовых проектов.

      0
      • Эдуард, кому нужна профессиональная подача материала пусть обращаются к профессионалам за деньги. А для тех. Для тех кто хочет бесплатной информации в интернете твоих занятий вполне достаточно. А вообще , если ты хочешь, советую тебе открыть новую ветку о том, что ты можешь персонально обучать программированию для микроконтроллеров за отдельную плату. Я знаю очень много людей, которые на самом начале изучения хотели бы иметь персонального тренера. Для того чтобы задать целую кучу вопросов и понять нужно ли им вообще дальше продолжать изучать программирование для микроконтроллеров или не стоит даже начинать. не у каждого может получится это правильно и хорошо делать. Но ты можешь просто попробовать. И понять нужно ли тебе вообще преподавать персонально или нет. Это всего лишь мой совет. Иногда за 1 час простого собеседования в режиме разговора ты можешь донести до человека очень много информации, на которую он может потратить ни одну неделю, а иногда даже целый месяц. Это я сам на себе испытал. И мне не совсем жалко немного денег для того чтобы в течение часа ты мне мог объяснить достаточно большой объем информации.

        1
  4. Здравствуйте, Эдуард! Прошу Вас сделать возможной оплату к материалам сайта с банковской карты аналогично поддержке проекта.

    0

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

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