STM32 и CubeIDE: ШИМ и светодиод

Самый наглядный способ продемонстрировать работу ШИМ — плавное изменение яркости светодиода. Начнём с настройки выводов микроконтроллера в конфигураторе CubeIDE.

Для тех, кто ещё не знаком с этим понятием есть специальный урок про ШИМ.

ШАГ 1. Настраиваем контакты программатора и отладчика.

Для этого в разделе System Core / SYS в поле Debug выбираем Serial Wire.

STM32, CubeIDE и светодиод

Этот шаг позволяет нам избежать проблем с загрузкой программы на микроконтроллер. Мы делаем его начиная с первого урока про stm32 и CubeIDE.

ШАГ 2. Выбираем режим контакта PA10

Нажимаем правую кнопку мыши на конакте PA10 и выбираем странный пункт TIM1_CH3. Выбор этого пункта означает, что мы подключаем данный контакт к каналу №3 аппаратного таймера №1.

STM32, CubeIDE и светодиод

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

ШАГ 3. Настройка таймера

Теперь нам необходимо настроить канал №3 таймера №1 на генерацию именно ШИМ сигнала. Для этого раскроем раздел timers в левой колонке конфигуратора. В появившемся блоке настроек находим пункт Channel 3 и выбираем в нём:

PWM Generation CH3

Далее, в том же блоке настроек, но чуть ниже указываем величину предделителя (Prescaler) и счётчика (Counter period).

STM32, CubeIDE и светодиод

Для установки этих параметров будем использовать следующие рассуждения.

Мы управляем светодиодом, и чтобы не было видно мерцания частота ШИМ должна быть не менее 100 Гц. Пусть будет 250Гц, что соответствует периоду T = 4мс. Чтобы градаций яркости совсем не было заметно, выберем разрешение ШИМ равным 500.

Для обеспечения таких параметров, на вход генератора ШИМ необходимо подать сигнал с частотой 250Гц*500 = 125кГц. Это нужно для того, чтобы в течение каждого периода счётчик таймера смог отсчитывать импульс нужной длины: 0,1,2,…,499 частей периода.

С другой стороны, таймер №1 в STM32F103 подключён к шине APB2, а значит он тактируется её частотой. Шина — это такой аппаратный канал передачи данных между разными устройствами внутри микроконтроллера: процессором, памятью, интерфейсами I2C, SPI и пр.

Мы ничего не меняли в проекте, поэтому изначально APB2 работает на частоте 8МГц, значит и на входе счётчика таймера будет 8МГц. А нам надо 125кГц! Что делать? Для этого нам и понадобится предделитель! Мы просто поделим частоту шины на 64 и получим, что хотим.

И самое последнее. В поле Counter period мы запишем разрешение ШИМ сигнала — 500. Этот параметр говорит счётчику таймера на сколько частей необходимо делить период сигнала.

Важный нюанс. При выставлении предделителя и периода счётчика следует вычитать единицу!

Не забываем сохранить конфигурацию. Только после сохранения CubeIDE автоматически обновит исходный код согласно внесённым в конфигураторе изменениям. Готово! Переходим к программе.

Программа

Чтобы изменить коэффициент заполнения ШИМ сигнала нам необходимо каждый раз перенастраивать таймер, но уже не в конфигураторе, а в самой программе. Для этого мы опишем новую функцию, которую назовём setPWM. Сначала объявим её в самом начале файла main.c.

/* USER CODE BEGIN PFP */
void setPWM(uint16_t pwm_value);
/* USER CODE END PFP */

А затем реализуем её в блоке USER CODE BEGIN 4.

/* USER CODE BEGIN 4 */
void setPWM(uint16_t value)
{
    TIM_OC_InitTypeDef sConfigOC;

    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = value;
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3); // таймер №1, канал №3
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
}
/* USER CODE END 4 */

Также необходимо запустить ШИМ на 3-м канале первого таймера. Делаем это так:

  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
  /* USER CODE END 2 */

Это была подготовка. Теперь напишем программу, которая будет плавно менять яркость светодиода от 0 до 100%, а затем от 100% до нуля. Сделаем это следующим образом. Пусть изначально яркость равна 0. На каждом шаге суперцикла будем прибавлять единицу к яркости, пока не достигнем максимального значения. В момент, когда яркость достигла 500, меняем направление отсчёта и начинаем вычитать по единице, пока не достигнем 0. И так далее.

Объявим пару переменных, в которых будем хранить текущее значение коэффициента заполнения (pwm_value) и направление отсчёта (step).

/* USER CODE BEGIN PV */
uint16_t pwm_value = 0;
int8_t step = 0;
/* USER CODE END PV */

Основной код добавим в суперцикл while:

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
	  if(pwm_value == 0) step = 1;
	  if(pwm_value == 500) step = -1;
	  pwm_value += step;
	  setPWM(pwm_value);
  	  HAL_Delay(5);
  }
  /* USER CODE END 3 */

Последнее, что нам нужно сделать — подключить светодиод к ножке PA10. Воспользуемся простейшей схемой с резистором на 200 Ом, такой же, как в уроке про светодиод на Ардуино.

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

На следующем уроке будем управлять куда более серъёзной нагрузкой — двигателем постоянного тока!


Изменено:

STM32 и CubeIDE: ШИМ и светодиод: 18 комментариев

  1. Очень жду! «На следующем уроке будем управлять куда более серъёзной нагрузкой — двигателем постоянного тока!»

  2. Частота ЦП и периферии какая?
    Читатель по прескейлерам должен угадывать?

  3. Не работает этот код. Всё в точности скопировал отсюда и ничего нет на выходе. Светодиод не загорается. Не подскажете в чём может быть проблема?

  4. int8_t step = 0;
    step = -1;
    Это косяк, int8_t не может быть отрицательным, однако этот пример работает.

  5. Здравствуйте! В настройках канала таймера есть опция «PWM Generation No Output»
    Очень прошу рассказать о ней, для чего она предназначена и как ею правильно пользоваться?

    • Как и в случае остальных функций таймеров, будет срабатывать определённое прерывание, но при этом ни на какие ноги сигнал выходить не будет «No Output».

  6. А можно узнать, получится ли данным способом управлять светодиодом только уже который есть на плате?

    • Просто сделал подобным образом, только для пина относящемуся к светодиоду установленному на плате и к сожалению не работает, хотя программа выполняется в суперцикле без ошибок

    • На плате stm32f407g-disk1 штатный светодиод задействовал, конечно на таймере4. А вот почему не работает HAL_TIM_PWM_ConfigChannel так и не понял, пока заменил изменением регистра сравнения с помощью CMSIS.

  7. А можно какие-то комментарии насчёт структуры функции? Мне вот совсем не понятно что за параметры в ней.

    • Посмотрел в отладчике, регистр занят. Нужно вначале прописать HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);

Добавить комментарий для Адрей Отменить ответ

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.