STM32 с нуля. Timer. Настройка и использование таймеров.

STM32 с нуля. Timer. Настройка и использование таймеров.

Компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе. Поэтому я создал новую рубрику, посвященную работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Также вот глобальная рубрика по STM32 - ссылка.

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

Мало того, что таймеры обладают такими широкими возможностями, так их еще несколько у каждого контроллера. И даже не два и не три, а больше. В общем, нахваливать все это можно бесконечно. Давайте уже разбираться, что и как работает. Итак, микроконтроллер STM32F103CB имеет:

  • 3 таймера общего назначения (TIM2, TIM3, TIM4).
  • 1 более продвинутый таймер с расширенными возможностями (TIM1).
  • 2 WDT (WatchDog Timer).
  • 1 SysTick Timer.

Собственно таймеры общего назначения и таймер TIM1 не сильно отличаются друг от друга, так что ограничимся рассмотрением какого-нибудь одного из них. К слову я остановил свой выбор на TIM4. Без особой причины, просто так захотелось 🙂 Таймеры имеют 4 независимых канала, которые могут использоваться для:

  • Захвата сигнала.
  • Сравнения.
  • Генерации ШИМ.
  • Генерации одиночного импульса.

Таймеры 16-битные (то есть могут считать до 65535), умеют работать с инкрементальными энкодерами и датчиками Холла, несколько таймеров можно синхронизировать между собой. Есть прерывания на разные события, а именно:

  • Переполнение.
  • Захват сигнала.
  • Сравнение.
  • Событие-триггер.

При наступлении любого из этих событий таймеры могут генерировать запрос к DMA (DMA – прямой доступ к памяти, уже скоро мы будем разбираться и с ним). Теперь немного подробнее о каждом из режимов работы таймеров.

Режим захвата сигнала. Очень удобно при работе таймера в этом режиме измерять период следования импульсов. Смотрите сами: приходит импульс, таймер кладет свое текущее значение счетчика в регистр TIM_CCR. По-быстрому сохраняем это значение в какую-нибудь переменную. Сидим, ждем следующий импульс. Импульс пришел, таймер снова фиксирует значение счетчика в TIM_CCR, и нам остается только вычесть из этого значения то, которое мы предварительно сохранили. Это, наверное, самое простое использование этого режима таймера, но очень полезное. Отлавливать можно как передний фронт импульса, так и задний, так что возможности довольно велики. Пример использования режима можно найти тут.

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

Режим генерации ШИМ. Ну тут все уже понятно из названия - в этом режиме таймер генерирует ШИМ! Наверно нет смысла что-то писать тут еще сейчас. Скоро будет пример как раз с ШИМ, там и поковыряем поподробнее.

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

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

Надо бы потихоньку написать программу какую-нибудь тестовую. Но сначала посмотрим, что есть в библиотеке Standard Peripheral Library.

Итак, за таймеры несут ответственность файлы - stm32f10x_tim.h и stm32f10x_tim.c. Открываем первый и видим, что структура файла повторяет структуру файла для работы с GPIO, который мы рассматривали в предыдущей статье. Здесь описаны структуры и поля структур, которые нужны для конфигурирования таймеров. Правда здесь уже не одна, а несколько структур (режимов, а соответственно и настроек-то у таймеров побольше, чем у портов ввода-вывода). Все поля структур снабжены комментариями, вот, например:

Здесь будем задавать режим работы таймера. А вот еще:

Здесь выбираем канал таймера, ничего неожиданного 🙂 В общем все довольно прозрачно, если что спрашивайте!

С первым файлом понятно. А в файле stm32f10x_tim.c – готовые функции для работы с таймерами. Тоже все в целом ясно. Мы уже использовали библиотеку для работы с GPIO, теперь вот работаем с таймерами, и очевидно, что для разной периферии все очень похоже. Так что давайте создавать проект и писать программу. Итак, создаем новый проект, добавляем в него все необходимые файлы:

  • необходимо отметить, что в поле TIM_Prescaler нужно записывать значение, на единицу меньшее, чем то, которое мы хотим получить.

В этой программе мы смотрим, что было на выходе до момента генерации прерывания – если ноль, выставляем единицу на 0.5 мс. Если была единица – ставим ноль на 2.5 мс. Компилируем и запускаем отладку!

Небольшое, но очень важное отступление. Наш пример, конечно, будет работать и для теста он вполне сгодится, но все-таки в "боевых" программах нужно следить за оптимальностью кода как с точки зрения его объема, так и с точки зрения производительности и расхода памяти. В данном случае нет никакого смысла использовать структуру timer, а также вызывать функцию TIM_TimeBaseInit() каждый раз при смене периода. Правильнее менять всего лишь одно значение в одном регистре, а именно в регистре TIMx->ARR (где х - это номер таймера). В данном примере код трансформируется следующим образом:

Итак, продолжаем, на пути у нас очередные грабли! А именно ошибка:

  • ..\..\..\SPL\src\stm32f10x_tim.c(2870): error: #20: identifier "TIM_CCER_CC4NP" is undefined

Не так страшно как может показаться, идем в файл stm32f10x.h, находим строки:

И смело дописываем:

Вот теперь все собирается, можно отлаживать. Включаем логический анализатор. В командной строке пишем: la portb&0x01 и наблюдаем на выходе:

Видим, что все работает правильно! В следующей статье будем изучать режим генерации ШИМ, оставайтесь на связи 🙂

📎📎📎📎📎📎📎📎📎📎