STM32F4 — analogWrite() — получение PWM/ШИМ сигнала

В прошлой статье была заложен фундамент в виде объявления необходимых define-ов, функции pinMode() и некоторых вспомогательных функций. В этой статье речь пойдет о генерировании PWM сигнала и написании функции analogWritte().

Поставим себе задачу. К контроллеру подключен диод. Начальный уровень сигнала PWM — 0 (скважность импульса = 0). При нажатии на кнопку увеличиваем скважность на 25%.

Решим задачу на Arduino. Кнопку вешаем на 4й пин и подтягиваем к земле резистором, диод на 5й пин — там можно генерировать PWM.

ШИМ сигнал генерируется с помощью таймеров. Задача сводится к тому, что необходимо определенное время выдерживать уровень сигнала. При работе с Arduino рядовой пользователь даже не догадывается о всей сложности задачи. Кто-то может сказать, что используя всё готовое сложно чему-то научиться. С одной стороны этот человек будет прав, с другой, мне кажется — разработка должна подразделяться на уровни: низкий уровень, на котором взаимодействие с контроллером идет на прямую, и высокий уровень — где разработчик не обращает внимание на особенности контроллера. Если человек решает какую-то свою задачу, для чего ему задаваться вопросом «Как сделать PWM?», этот человек должен тратить свое время на продумывание алгоритма решения его задачи, он должен говорить контроллеру что делать, абстрагируясь от особенностей аппаратной платформы, на которой он это реализовывает. Кстати, именно из-за такого моего отношения, нежелания углубляться в детали, качество кода и подробность описания статей оставляет желать лучшего.

Теперь же попробуем реализовать задачу на контроллере STM. Для начала необходимо узнать, где мы можем использовать PWM. Открываем документацию  и ищем на каких пинах в качестве альтернативного режима можно использовать таймер (у меня STM32F407VG): ссылка на этом сайте, STM32F407VG.

Чтобы не рыться в дебрях документации позаимствуем таблицу с сайта посвященному STM32F4 Discovery:

Таймер Канал 1 Канал 2 Канал 3 Канал 4
PP1 PP2 PP3 PP1 PP2 PP3 PP1 PP2 PP3 PP1 PP2 PP3
TIM1  A8 E9 A9 E10 A10 E13 A11 E14
TIM2  A0 A5 A15 A1 B3  A2 B10  A3 B11
TIM3  A6 B4 C6 A7 B5 C7 B0 C8 B1 C9
TIM4  B6 D12 B7 D13 B8 D14 B9 D15
TIM5  A0 H10 A1 H11  A2 H12 A3 PI0
TIM8  C6 I5 C7 I6 C8 I7 C9 I2
TIM9 A2 E5 A3 E6
TIM10  B8 F6
TIM11  B9 F7
TIM12  B14 H6 B15 H9
TIM13 A6 F8
TIM14 A7 E9

Диод вешаем на D15, в качестве кнопки для увеличение ШИМ, будем использовать пользовательскую кнопку на A0. За ШИМ на D15  пине отвечает TIM4.

Пример работы программ:

Arduino STM
GIF
GIF

 

Нам пришлось отдельно настраивать таймер, указывать период ШИМа (в Arduino ШИМ восьмиразрядный и максимальное значение сигнала — 255) и по отдельности настраивать каждый канал таймера. Такие настройки необходимо совершать с каждым таймером, чтобы тот мог работать с соответствующими ему пинами. Также для изменения сигнала мы обращаемся не к пину, а к каналу таймера — это ужасно неудобно. Давайте дополним код, написанный в прошлой статьи функцией analogRead() .

Для начала необходимо сделать настройку таймеров. Для этого создадим функцию v_configTimerPWM(), которой будем отдавать настраиваемый таймер и его номер.

Теперь нам нужна функция, которая бы в зависимости от имени пина возвращала таймер отвечающий за него — v_getAF_Timer():

Для связывания пина с альтернативной функцией используется GPIO_PinSource вместо GPIO_Pin. Нужно создать функцию, которая бы в зависимости от нашего названия пина (D5, A0, A1, C2 …) возвращался его GPIO_PinSource :

Далее — функция, которая будет связывать пин с его альтернативной функцией — v_pinAFconfig():

Теперь допишем режим PWM в функцию pinMode().

Также необходимо включить таймера и вызвать их настройку. В функцию v_init() дописываем:

И самое главное — функция analogWrite():

Теперь проверяем:

Все работает! Результат такой же, как и на GIF-ке выше. Давайте попробуем сделать что-то покрасивее: плавное переливание цветов RGB-диода (RGB нет 😥 буду использовать три разных диода), кнопка на A0 будет служить в качестве кнопки Play/Pause. Для проверки выше описанного функционала будем использовать три пина, которыми управляют разными таймеры — A5 (TIM2),  A6 (TIM3), E5 (TIM9).

 

К сожалению камера плохо передает изменение цвета, но четко видно переливание с зеленого на синий. Обратите внимание, что в коде, на разные пины я подавал разное по максимальной величине значения (разные масштабирующие коэффициенты)  — A5 — синий цвет, B6 — красный, E5 — зеленый. Хоть я и указал наибольшее значение для красного, его яркость все равно слишком мала.

На этом все, с каждым разом мы все больше и больше упрощаем себе жизнь при работе с STM. Напоминаю, что упрощение отбирает у нас возможность тонкой настройки параметров.

Полный код файлов vstm.h и  vstm.c здесь не выкладываю, самая свежая версия находится в репозитории.

Статьи серии про STM:

  1. STM32F4 — Вводная статья. Библиотека VSTM
  2. STM32F4 — pinMode(), digitalRead() и digitalWrite()
  3. STM32F4 — analogWrite() — получение PWM/ШИМ сигнала