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

analogWrite изнутри: таймеры и частота ШИМ

analogWrite не настраивает таймер — это уже сделала функция init() до setup(). Сама analogWrite только подключает вывод к таймеру и пишет число в регистр сравнения. Разберём, что задано в init(), и перенастроим Timer1 на 31 и 20 кГц.

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

От Arduino к регистрамурок 3 из 835 мин

Цель урока: Знать, какой таймер и в каком режиме стоит за каждым ШИМ-выводом Uno, рассчитывать частоту и скважность analogWrite и менять частоту ШИМ регистрами, не ломая millis().

analogWrite(9, 128) — это не «аналоговое» напряжение, а ШИМ: вывод переключается между 0 и 5 В с коэффициентом заполнения около 50 %. Как устроены режимы Fast PWM и Phase Correct, разобрано в уроке о ШИМ трека «AVR с нуля». Здесь — что делает ядро ArduinoCore-avr 1.8.8: функции init() в wiring.c и analogWrite() в wiring_analog.c.

Таймеры настраивает init()

До setup() функция main() ядра вызывает init() (строки 241–324 wiring.c), и та запускает все три таймера:

ТаймерЧто делает init()Режим по даташитуДелитель
Timer0sbi(TCCR0A, WGM01); sbi(TCCR0A, WGM00); sbi(TCCR0B, CS01); sbi(TCCR0B, CS00); sbi(TIMSK0, TOIE0);Fast PWM, TOP = 0xFF (WGM02:0 = 3)64 (табл. 15-9)
Timer1TCCR1B = 0; sbi(TCCR1B, CS11); sbi(TCCR1B, CS10); sbi(TCCR1A, WGM10);Phase Correct 8 бит, TOP = 0x00FF (режим 1, табл. 16-4)64 (табл. 16-5)
Timer2sbi(TCCR2B, CS22); sbi(TCCR2A, WGM20);Phase Correct, TOP = 0xFF64 (табл. 18-9)

У Timer0 есть вторая работа: прерывание по переполнению отсчитывает millis() — это тема урока 5. Комментарий в wiring.c объясняет выбор: Phase Correct на Timer0 переполнялся бы вдвое реже и millis() вёл бы себя иначе, чем на старом ATmega8.

Что делает analogWrite

// ArduinoCore-avr 1.8.8, cores/arduino/wiring_analog.c, lines 96-285 (abridged)
void analogWrite(uint8_t pin, int val)
{
    pinMode(pin, OUTPUT);
    if (val == 0) {
        digitalWrite(pin, LOW);
    } else if (val == 255) {
        digitalWrite(pin, HIGH);
    } else {
        switch (digitalPinToTimer(pin)) {
        case TIMER0A:
            sbi(TCCR0A, COM0A1);   // connect OC0A to the pin
            OCR0A = val;           // duty
            break;
        case TIMER1A:
            sbi(TCCR1A, COM1A1);
            OCR1A = val;
            break;
        /* ... TIMER0B, TIMER1B, TIMER2A, TIMER2B ... */
        case NOT_ON_TIMER:
        default:
            if (val < 128) digitalWrite(pin, LOW);
            else digitalWrite(pin, HIGH);
        }
    }
}

Функция делает три вещи: переводит вывод в выход (выход сравнения появится на выводе только при DDR = 1, раздел 15.6), ставит бит COMnx1 — неинвертирующий ШИМ, — и пишет значение в регистр сравнения OCRnx. На выводе без таймера ШИМа нет: analogWrite(7, 100) даст постоянный низкий уровень, analogWrite(7, 200) — высокий.

Карта выводов и частоты

Вывод UnoПортВыход таймераТаймер и режимЧастота при 16 МГц
3PD3OC2BTimer2, Phase Correct490,2 Гц
5PD5OC0BTimer0, Fast PWM976,6 Гц
6PD6OC0ATimer0, Fast PWM976,6 Гц
9PB1OC1ATimer1, Phase Correct 8 бит490,2 Гц
10PB2OC1BTimer1, Phase Correct 8 бит490,2 Гц
11PB3OC2ATimer2, Phase Correct490,2 Гц

Частоты — по формулам даташита. Fast PWM (раздел 15.7.3):

f=fclkN⋅256=16 000 00064⋅256=976,5625 Гцf = \frac{f_\text{clk}}{N \cdot 256} = \frac{16\,000\,000}{64 \cdot 256} = 976{,}5625\ \text{Гц}

Phase Correct (раздел 15.7.4; для Timer1 — 16.9.4 с TOP = 255):

f=fclkN⋅510=16 000 00064⋅510≈490,196 Гцf = \frac{f_\text{clk}}{N \cdot 510} = \frac{16\,000\,000}{64 \cdot 510} \approx 490{,}196\ \text{Гц}

Соответствие выводов и выходов таймеров — из разделов 14.3.1 и 14.3.3 даташита, номера выводов — из pins_arduino.h. Ту же таблицу для любой платы показывают калькулятор ШИМ Arduino и карта выводов.

Скважность на разных таймерах

Одно и то же число в analogWrite даёт чуть разную скважность. В Fast PWM выход ставится в единицу в начале периода и сбрасывается при совпадении, а при OCR0x = 0 остаётся импульс длиной в один такт таймера (раздел 15.7.3). Значит, высокий уровень длится OCR0x + 1 тактов из 256. В Phase Correct выход меняется при совпадении на подъёме и на спуске, и доля высокого уровня — OCRnx / 255 (оба вывода — расчёт наш по описанию режимов):

analogWriteВыводы 5, 6 (Fast PWM)Выводы 3, 9, 10, 11 (Phase Correct)
12 / 256 = 0,78 %1 / 255 = 0,39 %
128129 / 256 = 50,4 %128 / 255 = 50,2 %
254255 / 256 = 99,6 %254 / 255 = 99,6 %

Отсюда и особый случай для нуля: в Fast PWM OCR0A = 0 не выключил бы светодиод полностью — остался бы импульс в 1/256 периода. В Phase Correct OCRnx = 0 и так даёт постоянный ноль (разделы 15.7.4 и 16.9.4), но ядро не различает таймеры и для 0 и 255 на любом выводе вызывает digitalWrite, которая заодно выключает ШИМ (её turnOffPWM мы видели в уроке 1).

Кто ещё занимает таймеры

  • Timer0 — millis(), micros() и delay(). Его делитель и режим лучше не менять.
  • Timer2 — функция tone(): в Tone.cpp для ATmega328P выбран USE_TIMER2, и tone() переводит Timer2 в режим CTC (строки 183–186). Пока звучит tone(), ШИМ на выводах 3 и 11 работать не будет. И после noTone() он сам не вернётся: disableTimer(2) (строки 426–457) восстанавливает Phase Correct и делитель 64, но пишет TCCR2A = (1 << WGM20) — биты COM сброшены, — и обнуляет OCR2A. ШИМ на выводах 3 и 11 появится только после следующего analogWrite.
  • Timer1 — свободен от ядра, его часто занимают библиотеки. Проверяйте документацию библиотеки, прежде чем перенастраивать.

Своя частота регистрами

31,4 кГц на выводах 9 и 10 одной строкой

Двигатель на 490 Гц пищит. Если оставить у Timer1 режим ядра и убрать делитель, частота станет 16 000 000 / 510 ≈ 31 372 Гц — выше слышимого диапазона (расчёт наш):

void setup() {
  // Timer1 keeps the core's 8-bit phase correct mode; only the prescaler changes.
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | _BV(CS10);  // clk/1
  analogWrite(9, 128);   // still works: 50 % at about 31.4 kHz
}

void loop() {}

analogWrite продолжит работать на выводах 9 и 10: она пишет только биты COM и OCR1x, а делитель не трогает.

20 кГц с разрешением 800 шагов

Режим 14 Timer1 (табл. 16-4) — Fast PWM с вершиной в регистре ICR1. Частота по разделу 16.9.3 — fclk / (N · (1 + TOP)): для 20 кГц без делителя TOP = 16 000 000 / 20 000 − 1 = 799 (расчёт наш).

void setup() {
  TCCR1B = 0;                                     // stop Timer1: init() left it running
  DDRB |= _BV(DDB1);                              // pin 9 (OC1A) as output
  TCCR1A = _BV(COM1A1) | _BV(WGM11);              // non-inverting OC1A; WGM11 of mode 14
  ICR1 = 799;                                     // TOP: 16 MHz / (1 + 799) = 20 kHz
  OCR1A = 199;                                    // (199 + 1) / 800 = 25 %
  TCNT1 = 0;
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);   // WGM13:12 of mode 14, clk/1: start
}

void loop() {}

ICR1 и OCR1A записываются, пока таймер остановлен, и только последняя строка запускает счёт: таймер сразу стартует с нужными вершиной и скважностью. После такой настройки не вызывайте analogWrite(9, …): функция запишет в OCR1A число от 1 до 254, которое теперь означает долю от 800, а значения 0 и 255 выключат ШИМ через digitalWrite. Делитель и вершину под любую частоту подберёт калькулятор таймера.

Сколько стоит одна смена скважности

analogWrite при каждом вызове выполняет pinMode и digitalPinToTimer — те же поиски по таблицам, что в уроке 1, — и только потом пишет регистр. Регистровая версия — одна запись: OCR0A лежит в области ввода-вывода по адресу 0x27, и для него достаточно команды OUT (1 такт), а 16-битный OCR1A — в расширенной области (0x88–0x89), куда пишут две команды STS по 2 такта (разделы 8.5 и 37). Для плавной регулировки в цикле это разница между десятками тактов и одним-четырьмя.

Нагрузка на ШИМ-выводе

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

Задание

  1. В скетче выполнено TCCR2B = (TCCR2B & 0xF8) | _BV(CS21); — делитель Timer2 стал 8. Какая теперь частота ШИМ на выводах 3 и 11? Изменится ли millis()?
  2. Посчитайте скважность для analogWrite(6, 64) и analogWrite(10, 64).
  3. После analogWrite(5, 100) скетч пишет PORTD &= ~_BV(PORTD5);. Пропадёт ли ШИМ на выводе 5? Почему?
Ответ и решение
  1. Timer2 остаётся в Phase Correct: 16 000 000 / (8 · 510) ≈ 3921,6 Гц (расчёт наш). millis() не изменится — время считает Timer0.

  2. Вывод 6 — Timer0, Fast PWM: (64 + 1) / 256 ≈ 25,4 %. Вывод 10 — Timer1, Phase Correct: 64 / 255 ≈ 25,1 %.

  3. Нет. Пока установлен бит COM0B1, выход сравнения OC0B подменяет обычную функцию порта, и бит PORTD5 на выводе не виден (раздел 15.6). Остановить ШИМ можно, сбросив COM0B1 в TCCR0A — так делает digitalWrite.

Что дальше

В уроке 4 разберём analogRead(): делитель 128, опорное напряжение и почему analogReference() ничего не пишет в регистры. Все регистры таймеров ATmega328P с битами — на страницах Timer0, Timer1 и Timer2. Чем Phase Correct отличается от Fast PWM, коротко объясняет термин «Phase Correct PWM», а про делители — термин «Предделитель таймера».

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

Почему на выводах 5 и 6 частота ШИМ почти в два раза выше?

Выводы 5 и 6 работают от Timer0, а его ядро переводит в режим Fast PWM: таймер считает только вверх, и период равен 256 тактам таймера. Timer1 и Timer2 ядро ставит в Phase Correct PWM, где счёт идёт вверх и вниз и период равен 510 тактам. При делителе 64 и 16 МГц это 976,6 и 490,2 Гц.

Почему analogWrite(5, 0) и analogWrite(9, 0) обрабатываются особо?

Причины для двух таймеров разные. В Fast PWM на Timer0 (выводы 5 и 6) значение OCR0x = 0 не даёт постоянного нуля: по даташиту на выходе остаётся узкий импульс длиной в один такт таймера каждый период. У Timer1 в Phase Correct (выводы 9 и 10) OCR1x = 0 и так даёт постоянный низкий уровень (разд. 16.9.4), так что для analogWrite(9, 0) особый случай не обязателен. Ядро всё равно обрабатывает 0 и 255 одинаково для всех выводов: не включает ШИМ, а вызывает digitalWrite с LOW или HIGH, которая заодно отключает таймер от вывода.

Можно ли изменить частоту analogWrite, не потеряв millis()?

Да, если не трогать Timer0. Делитель Timer1 (выводы 9 и 10) и Timer2 (выводы 3 и 11) можно поменять одной записью в TCCR1B или TCCR2B, и analogWrite продолжит работать — он пишет только биты COM и регистр сравнения. Timer0 отвечает за millis(), micros() и delay(): сменив его делитель, вы измените и ход времени.

Источники

  1. Arduino. ArduinoCore-avr 1.8.8: cores/arduino/wiring_analog.c, wiring.c, variants/standard/pins_arduino.h, Tone.cpp — тег 1.8.8 — коммит 86df345 от 21 мая 2026 (GitHub API, tags и commits); wiring_analog.c, стр. 96–285: analogWrite — pinMode(OUTPUT), значения 0 и 255 через digitalWrite, иначе бит COMnx1 и запись в OCRnx, на выводе без таймера — LOW ниже 128 и HIGH от 128; wiring.c, стр. 241–324: init() — Timer0 в Fast PWM (WGM01, WGM00) с делителем 64 и прерыванием TOIE0, Timer1 — TCCR1B = 0, делитель 64 (CS11, CS10), 8-битный Phase Correct (WGM10), Timer2 — делитель 64 (CS22) и Phase Correct (WGM20); pins_arduino.h, стр. 196–232: выводы 3 — TIMER2B, 5 — TIMER0B, 6 — TIMER0A, 9 — TIMER1A, 10 — TIMER1B, 11 — TIMER2A; Tone.cpp, стр. 115–124: tone() на ATmega328P использует Timer2; стр. 183–186: tone() ставит Timer2 в CTC; стр. 426–457 и 480–495: noTone вызывает disableTimer(2) — TCCR2A = (1 << WGM20) без битов COM, делитель 64, OCR2A = 0
  2. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — разд. 14.3.1 и 14.3.3: OC0A — PD6, OC0B — PD5, OC1A — PB1, OC1B — PB2, OC2A — PB3, OC2B — PD3; разд. 15.6: при установленных битах COM0x1:0 выход сравнения подменяет функцию порта, но направление задаёт DDR; разд. 15.7.3: Fast PWM, частота fclk/(N·256), OCR0x = 0 — узкий импульс в каждом периоде, OCR0x = MAX — постоянный уровень; разд. 15.7.4: Phase Correct, частота fclk/(N·510); табл. 15-9: CS02:0 = 011 — делитель 64; разд. 16.9.3: Fast PWM Timer1, частота fclk/(N·(1 + TOP)); разд. 16.9.4: Phase Correct Timer1, fclk/(2·N·TOP), OCR1x = BOTTOM — постоянный низкий уровень, OCR1x = TOP — постоянный высокий; табл. 16-4: режим 1 — Phase Correct 8 бит, TOP = 0x00FF, режим 14 — Fast PWM, TOP = ICR1; табл. 16-5: CS12:0 = 001 — без делителя, 011 — делитель 64; табл. 18-9: CS22:0 = 100 — делитель 64; разд. 29.1: 40 мА на вывод
  3. AVR-LibC 2.3.2: avr/io.h, iom328p.h (github.com/avrdudes/avr-libc) — имена регистров и битов TCCR1A, TCCR1B, ICR1, OCR1A, COM1A1, WGM11, WGM12, WGM13, CS10; 16-битные регистры доступны как одна переменная

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