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

Частота ШИМ Arduino: таймеры, выводы и смена делителя

Выберите плату, таймер и делитель — калькулятор покажет частоту ШИМ analogWrite() на выводах этого таймера, частоты остальных таймеров, строку кода для смены делителя и предупредит, если изменение сломает millis(), delay() или tone().

Исходные данные

МГц

16 МГц у плат на 5 В

Результат

Частота ШИМ
490,2 Гц
период 2,04 мс; ШИМ с коррекцией фазы, 8 бит
Выводы
D9, D10меняются все выводы таймера
По умолчанию в Arduino
490,2 Гцделитель 64
Таймер 0
976,56 ГцD5, D6
Таймер 2
490,2 ГцD3, D11

Скетч

void setup()
{
    TCCR1B = (TCCR1B & 0b11111000) | 0x03;   /* timer 1, prescaler 64 */
    analogWrite(9, 128);   /* 50 % on D9 */
}

Расчёт выполнен по формулам и данным из источников, приведённых на этой странице, и не заменяет проверку: параметры компонентов у разных производителей и даже партий различаются. Перед применением сверяйте результат с документацией на конкретные детали.

Функция analogWrite() в Arduino включает аппаратный ШИМ одного из таймеров микроконтроллера. Его частота не настраивается из Arduino API: её задают режим и делитель, которые ядро записывает в регистры таймеров при старте в функции init() (wiring.c). Всем таймерам ставится делитель 64, таймер 0 работает в режиме быстрого ШИМ, остальные — в режиме с коррекцией фазы.

Частоты по умолчанию

ПлатаТаймерВыводыРежимЧастота при 16 МГц
Uno, Nano (ATmega328P)05, 6быстрый976,56 Гц
19, 10с коррекцией фазы490,20 Гц
23, 11с коррекцией фазы490,20 Гц
Mega 256004, 13быстрый976,56 Гц
1, 2, 3, 4, 511, 12; 9, 10; 2, 3, 5; 6, 7, 8; 44, 45, 46с коррекцией фазы490,20 Гц
Leonardo, Micro (ATmega32U4)03, 11быстрый976,56 Гц
1, 39, 10; 5с коррекцией фазы490,20 Гц
46, 13с коррекцией фазы и частоты490,20 Гц

Выводы таймеров — из файлов pins_arduino.h плат. Частоты считаются по формулам даташита ATmega328P (разделы 15.7.3 и 15.7.4):

fбыстрый=fCLKN⋅256,fфаза=fCLKN⋅510,f_\text{быстрый} = \frac{f_\text{CLK}}{N \cdot 256}, \qquad f_\text{фаза} = \frac{f_\text{CLK}}{N \cdot 510},

где N — делитель. Таймер 4 ATmega32U4 считает от 0 до OCR4C и обратно, его частота — fCLK/(2 · N · TOP) при TOP = 255 после сброса, то есть те же 490 Гц.

Как изменить частоту

Частоту меняют, записывая другой делитель в младшие биты CS регистра TCCRnB и не трогая остальные биты, — калькулятор выдаёт эту строку. Делители у таймеров разные: у 0, 1, 3, 4, 5 — 1, 8, 64, 256 и 1024; у таймера 2 есть ещё 32 и 128 (таблица 18-9 даташита ATmega328P), а у таймера 4 ATmega32U4 — 15 ступеней от 1 до 16384 (таблица 15-14). Скважность при этом по-прежнему задаёт analogWrite() значением 0–255.

Делитель меняется сразу для всех выводов таймера: на Uno выводы 9 и 10 всегда работают с одной частотой. Учитывайте и то, что ещё занимает таймеры в ядре Arduino:

  • Таймер 0 отсчитывает время для millis(), micros() и delay(), ядро считает, что он переполняется каждые 64 · 256 тактов (wiring.c). Изменение делителя ускоряет или замедляет их во столько же раз.
  • tone() берёт таймер 2 на Uno и Mega и таймер 3 на Leonardo (Tone.cpp). Пока звучит тон, ШИМ на выводах этого таймера не работает. После noTone() таймер 2 возвращается к настройкам Arduino с делителем 64, а таймер 3 — нет.

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

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

Какая частота ШИМ у analogWrite на Arduino Uno?

На выводах 5 и 6 (таймер 0, быстрый ШИМ) — 976,56 Гц, на выводах 3, 9, 10 и 11 (таймеры 1 и 2, ШИМ с коррекцией фазы) — 490,2 Гц. Ядро Arduino ставит всем таймерам делитель 64, а при 16 МГц это даёт 16 000 000 / (64 · 256) и 16 000 000 / (64 · 510).

Как сделать ШИМ 31 кГц на Arduino?

Поставить делитель 1 таймеру 1 (выводы 9 и 10) или таймеру 2 (выводы 3 и 11): TCCR1B = (TCCR1B & 0b11111000) | 0x01. В режиме с коррекцией фазы это 16 000 000 / 510 = 31 372,5 Гц — выше слышимого диапазона, поэтому так убирают писк двигателей и катушек. Таймер 0 для этого не трогают: от него зависят millis() и delay().

Почему после изменения ШИМ на выводах 5 и 6 сломался delay()?

Таймер 0, который управляет выводами 5 и 6 (на Mega — 4 и 13, на Leonardo — 3 и 11), одновременно отсчитывает время для millis(), micros() и delay(). Ядро считает, что он переполняется каждые 64 · 256 тактов. Если поставить делитель 8, переполнений станет в 8 раз больше, и delay(1000) продлится 125 мс.

Источники

  1. Arduino. ArduinoCore-avr, коммит 11b9130 (github.com/arduino/ArduinoCore-avr) — wiring.c, init(): делитель 64 у всех таймеров, таймер 0 — быстрый ШИМ и счёт millis(), таймеры 1, 2, 3, 4, 5 — ШИМ с коррекцией фазы, таймер 4 ATmega32U4 — с коррекцией фазы и частоты; variants/*/pins_arduino.h — выводы таймеров; Tone.cpp — tone() на таймере 2 (Uno, Mega) или 3 (Leonardo), disableTimer()
  2. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 15.7.3 — быстрый ШИМ f = f_clk / (N · 256); 15.7.4 — с коррекцией фазы f = f_clk / (N · 510) (эталон тестов); таблицы 15-9, 16-5 — делители 1, 8, 64, 256, 1024; таблица 18-9 — делители таймера 2: 1, 8, 32, 64, 128, 256, 1024
  3. Microchip (Atmel). ATmega16U4/32U4 datasheet (Atmel-7766J) — 14.8.5 — f = f_clk / (2 · N · TOP); 15.12.10 — OCR4C = 0xFF после сброса; таблица 15-14 — 15 делителей таймера 4 от 1 до 16384

Где пригодится

Встроить калькулятор на свой сайт

Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах

<iframe src="https://gaw.ru/embed/arduino-pwm/" title="Частота ШИМ Arduino: таймеры, выводы и смена делителя" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/arduino-pwm/">Частота ШИМ Arduino: таймеры, выводы и смена делителя</a> — GAW.ru</p>
Автоподстройка высоты рамки

Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.

<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>

Обновлено .

Нашли ошибку в расчёте?