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

ШИМ на таймерах Timer0 и Timer2

Таймер умеет не только вызывать прерывания, но и сам переключать вывод при совпадении счётчика с регистром сравнения. Так получается ШИМ — сигнал с постоянной частотой и изменяемой долей единицы, которым регулируют яркость, скорость двигателя и напряжение.

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

AVR с нуляурок 6 из 825 мин

Цель урока: Настроить аппаратный ШИМ на выводах OC0A и OC2A ATmega328P, рассчитать его частоту и коэффициент заполнения и менять яркость светодиода записью в OCR.

ШИМ (широтно-импульсная модуляция) — прямоугольный сигнал с постоянной частотой, у которого меняется доля периода с высоким уровнем — коэффициент заполнения. Светодиод на ШИМ мигает так быстро, что глаз видит среднюю яркость, а двигатель и фильтр усредняют напряжение. Делать ШИМ программно, переключая вывод в прерывании, можно, но таймеры ATmega328P умеют это аппаратно.

Выводы ШИМ

У каждого таймера два блока сравнения A и B, и у каждого — свой вывод OCnx (разделы 14.3.1 и 14.3.3 даташита):

ТаймерВыводПортВывод Uno
Timer0OC0APD66
Timer0OC0BPD55
Timer2OC2APB311
Timer2OC2BPD33
Timer1OC1A, OC1BPB1, PB29, 10

Сигнал таймера попадает на вывод, только если вывод настроен выходом битом DDR (раздел 15.6).

Режим Fast PWM

В режиме Fast PWM (WGM02:0 = 3) счётчик Timer0 считает от 0 (BOTTOM) до 0xFF и начинает заново (раздел 15.7.3). Биты COM0A1:0 = 10 включают прямой ШИМ: вывод OC0A сбрасывается в ноль при совпадении TCNT0 с OCR0A и ставится в единицу в начале периода (табл. 15-3). Частота:

fPWM=fclkN⋅256f_\text{PWM} = \frac{f_\text{clk}}{N \cdot 256}

При 16 МГц и делителе 64 это 16 000 000 / (64 · 256) ≈ 976,6 Гц. Единица держится от начала периода до совпадения, то есть OCR0A + 1 тактов из 256, и коэффициент заполнения равен (OCR0A + 1) / 256 — это наш вывод из описания режима. Крайние значения даташит описывает отдельно: OCR0A = 0xFF даёт постоянную единицу, а OCR0A = 0 — узкий импульс в каждом периоде, а не полный ноль.

В режимах ШИМ регистр OCR0A буферизован: новое значение вступает в силу только в вершине или в начале счёта (раздел 15.5), поэтому его можно менять в любой момент без искажённых импульсов.

Пример: плавная яркость светодиода

Светодиод с резистором 330 Ом подключим к выводу 6 Uno (PD6, OC0A):

#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdint.h>
#include <util/delay.h>

int main(void)
{
    DDRD |= _BV(DDD6);                          /* OC0A (PD6, pin 6) output */

    TCCR0A = _BV(COM0A1)                        /* non-inverting PWM on OC0A */
           | _BV(WGM01) | _BV(WGM00);           /* mode 3: fast PWM, TOP = 0xFF */
    TCCR0B = _BV(CS01) | _BV(CS00);             /* clk/64: 976.6 Hz at 16 MHz */

    for (;;) {
        for (uint8_t d = 0; d < 255; d++) {     /* fade in */
            OCR0A = d;
            _delay_ms(4);
        }
        for (uint8_t d = 255; d > 0; d--) {     /* fade out */
            OCR0A = d;
            _delay_ms(4);
        }
    }
}

Процессор после настройки только меняет число в OCR0A — сам сигнал таймер выдаёт без его участия.

Режим Phase Correct

В режиме Phase Correct (WGM02:0 = 1) счётчик идёт от 0 до 0xFF и обратно, а вывод переключается при совпадении на подъёме и на спуске (раздел 15.7.4). Импульс получается симметричным относительно середины периода, а частота — вдвое ниже:

fPWM=fclkN⋅510f_\text{PWM} = \frac{f_\text{clk}}{N \cdot 510}

При 16 МГц и делителе 64 — около 490,2 Гц. Крайние значения здесь честные: OCR0A = 0 даёт постоянный ноль, OCR0A = 0xFF — постоянную единицу. Даташит рекомендует двухнаклонные режимы для управления двигателями. Чтобы переключить пример на Phase Correct, достаточно заменить в TCCR0A пару _BV(WGM01) | _BV(WGM00) на _BV(WGM00).

Timer2 и другие делители

Timer2 устроен так же, как Timer0, и регистры называются так же с цифрой 2: TCCR2A, TCCR2B, OCR2A. Отличается набор делителей: у Timer2 их семь — 1, 8, 32, 64, 128, 256 и 1024 (табл. 18-9), что даёт больше вариантов частоты. 16-битный Timer1 даёт ШИМ с вершиной до 0xFFFF и разрешением до 16 бит — его режимы разобраны в статье «Таймеры AVR».

Безопасность

ШИМ не делает вывод мощнее: ток в импульсе такой же, как при постоянной единице, и абсолютный максимум 40 мА на вывод остаётся в силе (раздел 29.1). Светодиод — только через резистор, двигатель, мощный светодиод или нагреватель — только через транзисторный ключ с диодом для индуктивной нагрузки.

Задание

Нужен ШИМ частотой около 8 кГц на выводе 11 Uno (OC2A) в режиме Fast PWM при 16 МГц.

  1. Какой делитель Timer2 выбрать и какой будет точная частота?
  2. Запишите настройку TCCR2A и TCCR2B и значение OCR2A для заполнения около 25 %.
Ответ и решение
  1. Нужен делитель N ≈ 16 000 000 / (256 · 8000) ≈ 7,8. Ближайший у Timer2 — 8: частота 16 000 000 / (8 · 256) = 7812,5 Гц.

  2. Вывод PB3 — выход, режим 3 и прямой выход, делитель 8 (CS22:0 = 010):

DDRB |= _BV(DDB3);                               /* OC2A (PB3, pin 11) */
TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);  /* fast PWM, non-inverting */
TCCR2B = _BV(CS21);                              /* clk/8 */
OCR2A = 63;                                      /* (63 + 1) / 256 = 25 % */

Что дальше

В уроке 7 научимся читать аналоговое напряжение и свяжем его с яркостью из этого урока. Частоту и разрешение ШИМ для разных делителей и вершин посчитает калькулятор ШИМ, а какие частоты выдаёт analogWrite на разных платах — калькулятор ШИМ Arduino. Регистры с битами — на страницах Timer0 и Timer2 атласа. Коротко о понятиях — термины «ШИМ», «Коэффициент заполнения», «Фазовый ШИМ» и «Сравнение по выходу».

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

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

В Fast PWM счётчик идёт только вверх от 0 до вершины и сбрасывается, поэтому частота вдвое выше: fclk/(N·256) против fclk/(N·510). В Phase Correct счётчик идёт вверх и вниз, импульс симметричен относительно середины периода, и даташит рекомендует этот режим для управления двигателями.

Почему при OCR0A = 0 светодиод всё равно слабо светится?

В режиме Fast PWM с прямым выходом вывод устанавливается в единицу в начале каждого периода и сбрасывается при совпадении. При OCR0A = 0 даташит описывает это как узкий импульс в каждом периоде — один такт таймера из 256. Чтобы погасить вывод полностью, отключите ШИМ битами COM0A1:0 или используйте режим Phase Correct, где OCR0A = 0 даёт постоянный ноль.

Можно ли подключить двигатель прямо к выводу ШИМ?

Нет. Абсолютный максимум тока вывода ATmega328P — 40 мА, а двигатель потребляет сотни миллиампер и даёт выбросы напряжения при выключении. Двигатель включают через транзисторный ключ или драйвер с диодом, а вывод микроконтроллера управляет только затвором или базой.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — разд. 1.1 и 14.3.1, 14.3.3: OC0A — PD6, OC0B — PD5, OC2A — PB3, OC2B — PD3; разд. 15.7.3: Fast PWM (WGM02:0 = 3 или 7), сброс OC0x при совпадении и установка в BOTTOM, частота fclk/(N·256), OCR0A = 0 — узкий импульс, OCR0A = MAX — постоянный уровень; разд. 15.7.4: Phase Correct (WGM02:0 = 1 или 5), частота fclk/(N·510), крайние значения дают постоянный уровень; разд. 15.6: выход OC0x виден на выводе только при DDR = 1; табл. 15-3 и 15-8: биты COM0A1:0 и WGM; разд. 15.5: двойная буферизация OCR0x в режимах ШИМ; разд. 18.11.1–18.11.2, табл. 18-9: TCCR2A, TCCR2B, делители Timer2 1, 8, 32, 64, 128, 256, 1024
  2. AVR-LibC 2.3.2: avr/io.h, заголовок iom328p.h (github.com/avrdudes/avr-libc) — имена битов COM0A1, WGM01, WGM00, CS01, CS00, COM2A1, WGM21, WGM20, CS21 и регистров OCR0A, OCR2A
  3. ArduinoCore-avr, variants/standard/pins_arduino.h (github.com/arduino/ArduinoCore-avr) — выводы Uno: 3 — PD3, 5 — PD5, 6 — PD6, 11 — PB3

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