Перейти к содержанию

Fast PWM

В режиме Fast PWM счётчик таймера бежит от нуля до TOP и сбрасывается, а вывод переключается при совпадении со значением сравнения. Это самый быстрый ШИМ, который умеет AVR: частота вдвое выше, чем у Phase Correct при тех же настройках. Именно в нём Arduino запускает Timer0.

Обновлено Редакция GAW

Fast PWM (быстрый ШИМ, single-slope PWM) — режим ШИМ таймеров AVR, в котором счётчик считает только вверх: от BOTTOM (0) до TOP, после чего в следующем такте сбрасывается. В неинвертирующем режиме выход OCnx устанавливается на BOTTOM и сбрасывается, когда счётчик совпадает с регистром сравнения OCRnx; в инвертирующем — наоборот. По даташиту Microchip, из-за однонаправленного счёта частота Fast PWM может быть вдвое выше, чем у Phase Correct PWM.

Частота и разрядность

fPWM=fclk_I/ON⋅(1+TOP)f_\text{PWM} = \frac{f_\text{clk\_I/O}}{N \cdot (1 + \text{TOP})}

N — делитель частоты таймера: 1, 8, 64, 256 или 1024. У 8-битного Timer0 TOP = 0xFF при WGM02:0 = 3 или TOP = OCR0A при WGM02:0 = 7. У 16-битного Timer1 Fast PWM включают режимами WGM13:0 = 5, 6, 7 (фиксированные 8, 9, 10 бит), 14 (TOP = ICR1) и 15 (TOP = OCR1A); разрядность — от 2 бит (TOP = 0x0003) до 16. Чем больше TOP, тем тоньше шаг скважности и ниже частота. Подобрать делитель и TOP под нужную частоту помогает калькулятор ШИМ Arduino.

Особенности

  • Флаг TOV ставится на каждом TOP; в обработчике переполнения удобно обновлять значение сравнения.
  • Крайние значения. OCRnx = BOTTOM даёт не ноль, а узкий импульс на каждом периоде; OCRnx = MAX — постоянный уровень.
  • Буферизация. OCRnx обновляется на TOP, поэтому период не рвётся. При TOP = ICR1 регистр ICR1 не буферизован: если записать значение меньше текущего счёта, таймер пропустит совпадение и досчитает до 0xFFFF.
  • Меандр. Режим переключения OC0A на каждом совпадении (COM0A1:0 = 1) работает, только если WGM02 = 1, то есть TOP = OCR0A; он даёт частоту до f_clk_I/O / 2 при OCR0A = 0 — как в режиме CTC, но с двойной буферизацией.

Arduino

Ядро ArduinoCore-avr в функции init() включает Timer0 в Fast PWM с делителем 64 — комментарий в коде объясняет, что в Phase Correct таймер переполнялся бы вдвое реже, а на его переполнениях держится millis(). На 16 МГц выводы 5 и 6 Arduino Uno выдают 976,5625 Гц, а Timer1 и Timer2 работают в 8-битном Phase Correct. Подробный разбор analogWrite() на уровне регистров — в уроке «analogWrite и таймеры», а режимы всех таймеров AVR — на странице «Таймеры AVR».

Частые вопросы

Как посчитать частоту Fast PWM?

f = f_clk / (N · (1 + TOP)), где N — делитель (1, 8, 64, 256 или 1024), TOP — верхнее значение счёта. Для Timer0 в режиме с TOP = 0xFF это f_clk / (N · 256). Arduino на 16 МГц ставит N = 64, поэтому выводы 5 и 6 дают 16 000 000 / (64 · 256) = 976,5625 Гц.

Чем Fast PWM отличается от Phase Correct PWM?

Направлением счёта. В Fast PWM счётчик идёт только вверх и сбрасывается, поэтому частота вдвое выше при тех же TOP и делителе. В Phase Correct он считает вверх и вниз, и импульс симметричен относительно середины периода — его центр не сдвигается при изменении скважности. Microchip рекомендует Fast PWM для регулирования мощности, выпрямления и ЦАП, а режимы с двунаправленным счётом — для управления двигателями.

Почему при OCR0A = 0 на выходе Fast PWM не ноль?

Потому что выход в неинвертирующем режиме устанавливается на BOTTOM и сбрасывается при совпадении. При OCR0A = BOTTOM совпадение наступает сразу, и, как пишет Microchip, на выходе остаётся узкий импульс длиной в такт таймера на каждом периоде. Поэтому analogWrite(pin, 0) в Arduino не включает ШИМ, а просто выставляет низкий уровень.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet Complete (DS40002061B, 2020), разд. 15.7.3 и 16.9.3 «Fast PWM Mode» — Timer0: Fast PWM при WGM02:0 = 3 (TOP = 0xFF) или 7 (TOP = OCR0A); однонаправленный счёт от BOTTOM до TOP; неинвертирующий режим: OC0x сбрасывается при совпадении и устанавливается на BOTTOM; частота может быть вдвое выше, чем у Phase Correct; TOV0 ставится на каждом TOP; N — 1, 8, 64, 256 или 1024; OCR0A = BOTTOM — узкий импульс каждые MAX + 1 тактов, OCR0A = MAX — постоянный уровень; COM0A1:0 = 1 переключает OC0A на каждом совпадении только при WGM02 = 1 (для OC0B недоступно), меандр до f_clk_I/O / 2 при OCR0A = 0; Timer1: WGM13:0 = 5, 6, 7, 14, 15, разрядность 8, 9, 10 бит или по ICR1/OCR1A, от 2 бит (TOP = 0x0003) до 16 бит; R = log(TOP + 1) / log 2; ICR1 не буферизован, OCR1A буферизован и обновляется на TOP
  2. Arduino. ArduinoCore-avr 1.8.8 (commit 86df345): cores/arduino/wiring.c, wiring_analog.c, variants/standard/pins_arduino.h — Timer0 в режиме fast hardware PWM (WGM01, WGM00) с делителем 64 (CS01, CS00), разрешено прерывание по переполнению TOIE0; комментарий: phase-correct PWM переполнял бы Timer0 вдвое реже; Timer1 и Timer2 — 8-битный phase correct PWM; analogWrite(): при val = 0 — digitalWrite(LOW), при 255 — digitalWrite(HIGH); выводы 5 и 6 — TIMER0B и TIMER0A

Нашли ошибку?