Урок 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

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

не в сети 21 час

Эдуард

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

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

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