Урок 16. Таймеры STM32 в режиме счетчиков. Генерация циклических прерываний от таймеров.

Уроки STM32

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

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

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

 

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

 

Таймеры STM32.

У нашего микроконтроллера STM32F103C8T6 есть 4 таймера:

  • TIM1 – расширенный таймер, ориентированный на управление электродвигателем.
  • TIM2 … TIM4 – таймеры общего назначения.

Все таймеры имеют одинаковую архитектуру. Расширенный таймер отличается наличием дополнительных аппаратных узлов для формирования противофазных сигналов ШИМ. В результате его можно конфигурировать на работу в режиме 6-канального ШИМ и управлять им тремя полумостовыми усилителями мощности.

Но сейчас нас это не интересует. Для нашей задачи - формирования циклических прерываний, все таймеры имеют одинаковую архитектуру.

Таймеры STM32 - многофункциональные устройства. С помощью каждого из них можно реализовать:

  • Счетчик импульсов, а значит и времени с автоматической перезагрузкой.
  • Захват входного сигнала (4 канала).
    • Обнаружение фронта входного сигнала, запоминание времени, генерация события.
    • Измерение временных параметров входного ШИМ-сигнала: периода и длительности импульсов.
    • Интерфейс энкодера. Измерение параметров импульсов энкодера.
  • Сравнение кодов таймера (4 канала).
    • Генерация события по совпадению кода таймера с заданным значением.
    • Формирование ШИМ-сигнала.
    • Формирование одиночных импульсов, режим одновибратора.

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

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

Функциональная схема таймера достаточно сложная. Я выделил только необходимую нам часть.

Функциональная схема TIM1

Собственно отсчет импульсов или времени происходит на 16-ти разрядном счетчике CNT. Когда код счетчика достигает значения регистра перезагрузки, счетчик сбрасывается в 0. Таким образом, счетчик считает по циклу от 0 до значения регистра перезагрузки.

Частота сигнала тактирования таймера может быть уменьшена с помощью 16-ти разрядного предделителя PSC.

Перезагрузка счетчика формирует событие (прерывание). Частота его появления также может быть уменьшена счетчиком повторов (8 разрядов). Коэффициент деления задается в регистре повторов.

Код счетчика используется другими узлами таймера, например, для формирования ШИМ. Но об этом в других уроках.

В качестве источника тактирования могут быть выбраны:

  • Внутренние синхросигналы шин APB1 иAPB2, про которые мы говорили в уроке 5 (система тактирования микроконтроллера).
    • Для таймера TIM1 используется синхросигнал шины APB2;
    • Для таймеровTIM2- TIM4 используется синхросигнал шины APB1.
  • Внешнее тактирование, режим 1. Используется выходной сигнал другого таймера или внешний сигнал входов захвата.
  • Внешнее тактирование, режим 2.  Используется вывод микроконтроллера ETR.
  • Особый режим синхронизации – интерфейс подключения энкодера.

Сейчас мы будем использовать только внутренний источник тактирования.

 

Режимы счета таймера.

При использовании таймера в качестве счетчика импульсов можно выбрать один из режимов:

  • прямой счет;
  • обратный счет;
  • двунаправленный.

При прямом счете содержимое счетчика с каждым импульсом тактирования увеличивается на 1. Когда оно достигает значения регистра перезагрузки, то счетчик сбрасывается. Таким образом,таймер считает по циклу от 0 до значения перезагрузки. В момент перезагрузки формируется прерывание.

В режиме обратного (реверсивного) счета с каждым входным импульсом содержимое счетчика уменьшается на 1. При достижении 0 в счетчик загружается значение регистра перезагрузки и реверсивный счет продолжается. Таймер считает по циклу от значения перезагрузки до 0. В момент перезагрузки формируется прерывание.

Двунаправленный режим означает, что счетчик считает в прямом направлении от 0 до значения перезагрузки, а затем переходит в реверсивный режим и счет ведется до 0. При изменении направления счета и сбросе генерируется прерывание.

 

Установка конфигурации таймера с помощью STM32CubeMX.

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

Создадим проект Lesson16_1. Настроим конфигурацию системы тактирования. Обратим внимание на то, что частота тактирования таймеров на шинах APB1 и APB2 задана 72 мГц.

STM32CubeMX

Настроим выводы:

  • PC13 – активный выход;
  • PB13 – активный выход;
  • PB12 – вход с подтягивающим резистором.

Установка выводов в STM32CubeMX

Теперь будем конфигурировать таймер 1. В нашем микроконтроллере он самый многофункциональный.

Открываем вкладку Timers ->TIM1.

Выбираем в качестве источника тактирования внутреннее тактирование: Clock Source -> Internal Clock.

Выбор источника тактирования

Ниже появилось поле Parameter Settings.

Установка параметров

Давайте подробно разберем, что в нем.

Prescaler (PSC).

Это регистр предделителя. Предделитель делит частоту тактирования таймера, поступающую на основной счетчик. По сути, он, вместе с входной частотой,  определяет разрешающую способность таймера.

Счетчик предделителя считает входные импульсы от 0 до значения этого регистра. При равенстве кода счетчика и регистра счетчик сбрасывается и начинает считать заново. В момент сброса формируется импульс тактирования основного счетчика таймера. Таким образом, значение регистра предделителя определяет коэффициент деления частоты входного сигнала.

Счетчик и регистр предделителя 16-ти разрядные. Т.е. максимальный коэффициент деления 65536.

Надо помнить, что реальный коэффициент деления на 1 больше, чем значение регистра предделителя. Например:

Значение регистра предделителя Коэффициент деления
0 1
999 1000
65535 (максимальное значение) 65536

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

Counter mode.

Режим счетчика, определяет в какую сторону считать.

counter mode

  • Up – прямой счет.
  • Down – реверсивный счет.
  • Center Aligned mode 1 – двунаправленный счет, прерывание генерируется в момент, когда счетчик считает в обратную сторону и доходит до 0.
  • Center Aligned mode 2 – двунаправленный счет, прерывание генерируется, когда счетчик считает в прямом направлении и достигает значения перезагрузки.
  • Center Aligned mode 3 – двунаправленный счет, прерывание генерируется, в обоих случаях - при достижении 0 и значения перезагрузки.

Counter Period (Auto Reload Register).

Регистр перезагрузки. Его значение задает период работы таймера. Конечно, на время периода влияет еще режим счета.

Счетчик 16-ти разрядный. Значит, для однонаправленного счета период может длиться от 1 до 65536 длительностей импульсов предделителя. Реальная длительность периода на 1 больше значения регистра перезагрузки. Все как для регистра предделителя.

Internal Clock Division (CKD).

Делитель входной частоты для внутренних нужд таймера.

Internal Clock Division (CKD)

Частота используется при фильтрации внешних сигналов, формировании “мертвого времени” ШИМ и т.п. Сейчас это нам не интересно.

Repetition Counter (RCR).

Регистр счетчика повторов. Присутствует не во всех таймерах. Счетчик повторов считает импульсы событий на выходе таймера и при достижении значения регистра повторов сбрасывается и формирует реальное событие. Т.е. он делит частоту генерации событий (прерываний) таймера.

Счетчик 8-ми разрядный. Коэффициент деления на 1 больше значения регистра повторов и может быть в диапазоне 1 - 256. Регистр буферизирован, можно изменять его значения в любой момент.

Auto-reload preload.

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

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

Auto-reload preload

Вкладка NVIC Settings позволяет выбрать нужный тип прерывания, связанного с таймером.

Выбор прерывания

 

Пример конфигурации таймера и реализации программы.

Сделаем практическую задачу. Установим конфигурацию таймера 1, обеспечивающую циклические прерывания с периодом 0,5 секунд. В обработчике прерывания будем инвертировать состояние светодиода. В результате получим мигающий светодиод, но с использованием таймера и прерывания.

Частота тактирования у нас 72 мГц. Превратим ее с помощью предделителя в круглое значение.

Например, если задать 720 – 1 = 719, то частота после предделителя будет 72 000 000 / 720 = 100 000 Гц, или период 10 мкс.

Если в регистр перезагрузки задать значение 50 000, то получим требуемый период 0,5 секунд.

Конфигурация проекта

Во вкладке NVIC Settings выберем прерывание по перезагрузке счетчика.

Выбор прерывания

Создаем проект и открываем его в Atollic TrueStudio.

В папке Src проекта создан файл stm32f1xx_it.c. Он существовал и во всех предыдущих проектах. Просто мы на него до времени не обращали внимания.

Это файл обработчиков прерываний. Хороший стиль размещать функции обработки прерываний в нем.

В самом конце файла появилась функция:

void TIM1_UP_IRQHandler(void) {

    /* USER CODE BEGIN TIM1_UP_IRQn 0 */
    /* USER CODE END TIM1_UP_IRQn 0 */

    HAL_TIM_IRQHandler(&htim1);

    /* USER CODE BEGIN TIM1_UP_IRQn 1 */
    /* USER CODE END TIM1_UP_IRQn 1 */
}

Это и есть обработчик прерывания таймера 1. Код, который мы поместим в функцию, будет вызываться с периодом 0,5 секунд.

Вызовем в обработчике прерывания функции инверсии состояния для обоих светодиодов.

void TIM1_UP_IRQHandler(void) {

    /* USER CODE BEGIN TIM1_UP_IRQn 0 */

    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);

Мы установили конфигурацию таймера, но не запустили его. Сделаем это HAL-функцией в файле main.c.

/* Initialize all configured peripherals */

MX_GPIO_Init();
MX_TIM1_Init();

/* USER CODE BEGIN 2 */

HAL_TIM_Base_Start_IT(&htim1); // запуск таймера

Функция запускает таймер в режиме генерации прерываний.

Все. Компилируем, загружаем, проверяем. Оба светодиода мигают раз в секунду.

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

 

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

Основной цикл у нас пустой. Программа просто крутится в нем.

while (1) {

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
}

По отношению к нему светодиоды мигают в фоновом режиме, сами по себе. Если мы будем выполнять в цикле какие-либо действия, это никак не скажется на равномерном мигании светодиодов. Единственное условие – надолго не запрещать прерывания.

 

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

 

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

2

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

не в сети 1 день

Эдуард

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

Один комментарий на «Урок 16. Таймеры STM32 в режиме счетчиков. Генерация циклических прерываний от таймеров.»

  1. Большое Вам спасибо за ваш труд.
    У меня уточнение, для получения периода 0,5 секунды на мой взгляд в регистр перезагрузки необходимо задать значение 49999 (50000 — 1). Прав ли я?

    0
  2. Здравствуйте. Небольшая непонятка возникла. В кубе для подсчета импульсов режим работы канала input capture direct mode? Или какой-то другой?
    Спасибо!

    0
  3. Здравствуйте, Эдуард! Здоровья Вам и успехов!
    Вопрос такой. Если нужно разные процессы запускать от разных таймеров, и при этом с разными периодами от одного таймера, то где и каким образом это прописывать?

    0
    • Здравствуйте!
      Не совсем вас понял.
      Можно организовать вызов процесса с минимальным периодом от одного таймера, а остальные процессы отсчитывать от него. Можно сделать программные таймеры. Много вариантов.

      0
  4. Я так понимаю, использовать цикл с каим-ниубдь delay не лучшая практика? А периодическое поведение программы лучше делать при помощи счётчиков, они для этого и предназанчены, так что ли?

    0
  5. Здравствуйте, Эдуард!
    «Основной цикл у нас пустой. Программа просто крутится в нем.»
    Что значит крутится, если работают только прерывания? какова необходимость в данном случае прописывать пустой цикл
    while (1)?

    0
    • Здравствуйте!
      Должна же программа где-то работать между прерываниями. В микроконтроллере есть счетчик команд, который показывает, по какому адресу выбирается команда из памяти. Во время прерывания этот счетчик «перескакивает» на обработчик прерывания, а в остальное время крутится по циклу, где ничего не происходит.

      0
  6. Тогда скажите, Эдуард, возможно ли в принципе, чтобы программа состояла только из обработки прерываний? По идее, при редких внешних событиях это позволило бы вводить режим жесткой экономии энергии при отключении «кручения». Или так не получится? В AVR я так делал.

    0
  7. Спасибо большое. Статья помогла сделать простенький таймер для H743его. Написано очень просто и понятно (как раз для меня).

    0
  8. Эдуард, добрый день! Подскажите, как сделать следующее:
    Необходимо настроить, чтобы 1 таймер вызывал 2 прерывания.
    Одно прерывание допустим раз в 1 ms.
    А второе прерывание зависит от переменной, и оно раньше первого (например через 100, 300, и т.п us)

    До этого на avr делал без проблем, настраивал 2 прерывания на таймер. Одно по переполнению, это как раз 1 ms, а второе по совпадению. И второе прерывание генерилось в зависимости от числа.

    А как на STM32 ума не приложу.
    прерывание на 1 ms нормально работает, а вот со вторым проблема.
    Использую HAL

    0
    • Здравствуйте!
      Я эту тему еще не затрагивал в уроках.
      В CubeMX Выбираете для канала режим Output Compare или Output Compare No Output.
      Mode Toggle On Match.
      Pulse – значение для сравнения.
      Разрешаете прерывание TIM1 capture compare interrupt.
      Примерно так.

      1
      • Спасибо! Если будет по таймерам урок, в котором более подробно будет рассказано, то это было бы очень здорово

        0
  9. В бесконечном цикле у меня есть конечный автомат в виде оператора switch с переключением case кнопкой.Мне нужно чтобы при каждом переключении менялось частота мигания светодиода, например на выходе PC13. Вопрос — как вызвать из любого case функцию мигания построенной по изложенному в этой статье материалу и как обеспечить изменение частоты в каждом случае. Я понимаю, что изменение частоты происходит с помощью,например,изменения Counter Period.Но как сделать это в случае с switch пока не могу придумать.
    Для AVR я создавал функцию мигания с прерыванием по СТС
    и вызывал ее с нужным мне значением совпадения переменной из любых case. Пробую научиться работать с
    stm32 но пока идет тяжело.

    0
    • Здравствуйте!
      Если частота мигания светодиода невысокая, сделайте эту задачу программно. Например, в прерывании 1 мс в файле stm32f1xx_it.c.
      /**
      * @brief This function handles System tick timer.
      */
      void SysTick_Handler(void)
      {
      /* USER CODE BEGIN SysTick_IRQn 0 */
      // системный тик 1 мс
      В каждом вызове прерывания прибавляйте 1 к счетчику, сравнивайте с переменной и сбрасывайте счетчик при превышении им значения переменной. В этот момент инвертируйте состояние вывода.
      Останется только задавать значение переменной сравнения счетчика.

      0

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

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

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".