Что такое широтно-импульсная модуляция (ШИМ)?
ШИМ (широтно-импульсная модуляция) — это способ управления средним значением сигнала путём изменения длительности (ширины) импульсов при фиксированной частоте. Проще говоря, ШИМ даёт возможность плавно увеличивать или уменьшать напряжение в пределах которые нам необходимы. Теперь разберёмся как это работает и пройдемся по основным понятиям.
1) Коэффициент заполнения
Коэффициент заполнения это величина, показывающая, какую долю времени за один период ШИМ-сигнал находится в активном (высоком) состоянии. Он измеряется в процентах и рассчитывается как отношение времени высокого уровня к полному периоду сигнала. Например, при коэффициенте заполнения 25% сигнал будет находиться во включённом состоянии одну четверть времени, а остальное — выключен. Изменяя коэффициент заполнения, можно плавно управлять средней мощностью, подаваемой на нагрузку, что делает этот параметр ключевым при регулировке яркости, скорости или силы тока в различных устройствах.
Рассмотрим на графике для наглядности. T — период сигнала t вкл — время высокого уровня t выкл — время низкого уровня. D — коэффициент заполнения. Допустим период T равен 4 мс. Высокий уровень 1 мс. ТОгда наш коэффициент заполнения
D=1/4=0,25 или 25% Соответственно если время импульса будет 2 мс, наш коэффициент заполнения будет равен D=2/4=0,5 или 50%
2) Скважность
Скважность это величина обратная коэффициенту заполнения. Их часто путают но это не одно и тоже. Скважность это отношение полного периода сигнала к длительности импульса (высокого уровня). Она всегда больше либо равна 1. Скважность и коэффициент заполнения часто путают, но это разные вещи. Допустим если импульс длиться 1 мс а период равен 4мс, то скважность будет равна 4.
3) Разрешение
Разрешение ШИМ определяет, насколько точно можно регулировать коэффициент заполнения, то есть насколько плавно можно изменять среднее значение выходного сигнала. Оно зависит от разрядности таймера, который используется для генерации ШИМ-сигнала.Разрешение измеряется в битах.Чем выше разрешение, тем более плавным и точным будет управление. Например 8 бит это от 0 до 255 уровней заполнения,10 бит это 1024 уровня, 16 бит 65536 уровней и т.д.Но: чем выше разрешение, тем меньше максимальная частота ШИМ, так как счётчик должен пересчитать большее количество значений за тот же период.
Режимы работы ШИМ
Обычный режим
Обычный режим работы ШИМ в STM32 реализуется с использованием таймера в режиме счёта вверх (up-counting) и одного из двух режимов широтно-импульсной модуляции — PWM Mode 1 или PWM Mode 2. Этот режим часто применяется для управления яркостью светодиодов, скоростью вращения моторов и других задач, где требуется генерация прямоугольных импульсов с регулируемой скважностью. В режиме PWM Mode 1 выходной сигнал остаётся в высоком уровне, пока значение счётчика меньше заданного значения в регистре сравнения (CCR), а затем переключается в низкий уровень до конца периода, определяемого регистром автоперезагрузки (ARR). Это обеспечивает прямую зависимость ширины импульса от значения CCR и позволяет просто управлять мощностью, подаваемой на нагрузку. Такой режим часто называют «обычным» ШИМ и он является аналогом Fast PWM в микроконтроллерах AVR. В режиме PWM Mode 2 выходной сигнал находится в низком уровне, пока значение счётчика меньше CCR, и переходит в высокий уровень после его превышения до конца периода. Этот режим можно использовать, когда требуется инверсная логика ШИМ, например, для управления активным низким входом устройства.
С фазовой коррекцией
Режим широтно-импульсной модуляции с фазовой коррекцией в STM32 реализуется с помощью центрированного режима счёта таймера (center-aligned mode). В этом режиме счётчик таймера сначала увеличивается от нуля до заданного значения автоперезагрузки (ARR), а затем убывает обратно до нуля, формируя симметричный цикл. Благодаря такой структуре импульсы ШИМ становятся симметричными относительно середины периода, что позволяет снизить уровень электромагнитных помех и обеспечивает равномерное распределение энергии. Этот режим особенно полезен в системах управления электродвигателями, преобразователях питания и других приложениях, где важна стабильность и чистота сигнала.
Дополнительные режимы
Кроме стандартных, STM32 предоставляет и более гибкие или специализированные режимы работы ШИМ, которые применяются в более сложных сценариях:
- Центрированный (фазокорректный) режим (Center-Aligned PWM) — обеспечивает симметричную форму импульсов за счёт двунаправленного счёта таймера. Используется в электроприводах и силовой электронике для снижения помех.
- Одиночный импульс (One-Pulse Mode) — формирует один ШИМ-импульс при запуске или внешнем событии. Полезен для точного позиционирования или генерации одиночных сигналов.
- Комплементарный ШИМ с dead-time — доступен в расширенных таймерах (например, TIM1, TIM8). Позволяет формировать пару сигналов с задержкой между переключениями. Используется в управлении полумостами и силовыми ключами.
- ШИМ с использованием DMA или прерываний — позволяет динамически изменять скважность сигнала без участия основного процессора, например, при воспроизведении сложных форм сигналов.
- Принудительные режимы вывода (Forced Output) — позволяют программно установить выход в фиксированное состояние, независимо от работы таймера. Применяются для отладки или аварийных сценариев.
Программная реализация ШИМ
Рассмотрим один из возможных вариантов программной реализации ШИМ для STM32. Работать будем с платой STM32F4 Discovery с STM32F407VGT6.
Хочу заметить, что это необходимо лишь для лучшего понимания работы ШИМ в обычном режиме, чаще всего если у вас есть возможность использовать аппаратный ШИМ, то лучше так и поступить, т.к. это использует меньше ресурсов микроконтроллера и чаще всего вы получите более качественный сигнал.
Для реализации программного ШИМ будем использовать таймер 2. Он находится на шине APB1.
Используем внутренний генератор, без предделителей, таким образом получив на шине APB1 16 МГЦ.
Выставляем во вкладке таймеры ( в нужном нам таймере 2) настройку Clock Source в Internal CLock.
Тем самым задавая частоту шины APB1 для нашего таймера.Далее разрешаем прерывания.
Перейдём в настройки таймера на вкладку Parameter Settings и рассмотрим параметры его конфигурации. Выставляем следующие значения:
Prescaler 15 — Предделитель. (помним про + 1)
Counter Period (ARR) 99 — определяет максимально возможное значение, до которого будет считать таймер. (помним про + 1)
Поставим PD12 на выход чтобы светодиод привязанный к этому выходу мог загореться.
Расчёт
Дано:
- Входная частота таймера: 16 МГц (16,000,000 Гц)
- Предделитель (Prescaler): 15
- Период счётчика (ARR): 99
Частота таймера = 16000000/16*100=1000Гц
Логика работы следующая. Нам нужно чтобы светодиод плавно загорался в течении секунды, затем гас и затем снова загорался в течении секунды. Мы подобрали такие параметры частоты, предделителя и счётчика, чтобы наш период Т получился 0.01с т.е. 10мс.
Коэффициент заполнения равен нулю. Наш локальный счетчик тоже равен нулю. Таймер вызывает прерывание 100 раз за период. Каждый раз увеличивая счетчик на 1. Когда счётчик досчитывает до 100, коэффициент заполнения увеличивается на 1, а счётчик сбрасывается в ноль. Затем цикл повторяется, каждый раз увеличивая коэффициент заполнения на 1, пока он не станет равен 100% и наш светодиод будет гореть в полную силу. Затем коэффициент заполнения сбрасывается в ноль , светодиод гаснет и цикл повторяется.
Код программы
После того как выставили все настройки файла ioc генерируем код. Затем добавим работу с таймером. Запустим наш таймер, очистим флаг чтобы таймер не сработал сразу же.
/* USER CODE BEGIN 2 */ __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF); HAL_TIM_Base_Start_IT(&htim2); /* USER CODE END 2 */
Добавим переменные уровня заполнения и счётчика
/* USER CODE BEGIN PV */ volatile uint8_t duty_cycle = 0; volatile uint16_t counter = 0; /* USER CODE END PV */
Вся работа будет происходить в цикле вектора прерывания
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) // Проверяем, что это TIM2 { if (duty_cycle >= counter) { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET); } counter++; if (counter == 100) { duty_cycle++; counter = 0; } } }
Как я уже писал выше счётчик коэффициента заполнения будет доходить до сотни за 1 секунду и светодиод будет плавно загораться до полного свечения, затем резко тухнуть. Дальше цикл будет повторяться вновь. Это показывает что наш ШИМ работает правильно и мы на практике реализовали программный ШИМ. Теперь перейдём к аппаратному ШИМу.
Аппаратный ШИМ.
Аппаратный ШИМ реализуется с использованием таймеров. Какие конкретно таймеры поддерживают аппаратный ШИМ нужно смотреть в даташите на ваш микроконтроллер. Таймеры вашего микроконтроллера имеют специальные режимы для генерации ШИМ-сигналов без участия центрального процессора, что позволяет минимизировать нагрузку на ядро и обеспечить точный контроль параметров сигнала.
Слегка поменяем задачу. В основном цикле будем увеличивать яркость светодиода на 10% каждую секунду. Это покажет что с помощью ШИМ можно управлять любым необходимым вам устройством меняя коэффициент заполнения. Для нашего примера хорошо подойдёт таймер 4 с каналом номер 2, т.к. он выходит на светодиод на плате. ( использую ту же плату STM32F4 Discovery ). Тактовая частота та же, 16 МГц. Дальше переходим во вкладку таймеры, и находим 4ый таймер, 2 канал. Выбираем PWM Generation CH2.
Настройки и расчёты те же. 16 МГц частота. Prescaler — 15 Counter Period 99. Затем опускаемся ниже в настройках, там очень важный параметр Pulse. Это ширина импульса, она и определяет наш коэффициент заполнения. Изначально она равно нулю.
Разрешаем прерывания таймера
И добавляем в коде строку запуска.
/* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2); /* USER CODE END 2 */
Ставим пин PD13 на канал нашего таймера.
Затем в основном цикле программы увеличиваем наш коэффициент Pulse на 10% каждую секунду.
for (uint32_t pulse = 0; pulse <= 100; pulse += 10) { __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, pulse); HAL_Delay(1000); }
Таким образом увеличивается коэффициент заполнения и наш светодиод будет гореть ярче до полной мощности.
Широтно-импульсная модуляция — это мощный и универсальный инструмент управления, широко применяемый в микроконтроллерах STM32. Она позволяет точно регулировать мощность, яркость или скорость с минимальными затратами ресурсов. Хотя программная реализация ШИМ помогает лучше понять принцип его работы, в реальных проектах всегда предпочтительнее использовать аппаратный ШИМ — он надёжен, точен и не загружает центральный процессор.