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

ШИМ ESP32: контроллер LEDC, каналы и таймеры

ШИМ у ESP32 делает контроллер LEDC: 16 каналов, которые выводятся на любой GPIO, разрешение до 20 бит и аппаратное плавное изменение яркости. Но каналы делят между собой всего восемь таймеров, и из-за этого смена частоты на одном выводе может незаметно поменять её на другом. Разбираем по даташиту Espressif и документации ESP-IDF и Arduino-ESP32.

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

ESP32 формирует ШИМ двумя блоками. Для светодиодов и большинства других задач используют контроллер LED PWM (LEDC) — по даташиту Espressif это 16 независимых генераторов ШИМ с разрешением до 20 бит. Для двигателей есть отдельный блок MCPWM с собственными таймерами и модулем захвата событий. Здесь — про LEDC. Что такое ШИМ и скважность — в статье о ШИМ.

Каналы, группы и таймеры

ПараметрESP32
Каналы16, в двух группах по 8
Таймеры8: по 4 в группе, счётчик до 20 бит
Выводылюбые GPIO через матрицу, кроме GPIO34–39 (только вход)
Разрешение1–20 бит
Аппаратные функцииплавное изменение скважности, фаза, дизеринг

Ключ к пониманию LEDC — разделение ролей. По документации Arduino-ESP32 канал отвечает за скважность и вывод, а таймер — за частоту и разрешение. Несколько каналов одной группы могут работать от одного таймера. Значит, у ESP32 может быть 16 разных скважностей, но не больше 8 разных частот одновременно, по 4 на группу.

Группы по ESP-IDF различаются поведением. Высокоскоростная группа меняет настройки таймера бесперебойно — на следующем переполнении, без сбоя импульса. В низкоскоростной изменения применяет программа, и драйвер делает это сам. В Arduino-ESP32 каналы 0–7 относятся к одной группе, 8–15 — к другой.

Частота и разрешение

Частота и разрешение связаны: чем выше частота, тем меньше ступеней скважности. ESP-IDF приводит ориентиры для такта APB 80 МГц: на 5 кГц доступно до 13 бит — 8192 ступени, а наибольшая частота — 40 МГц при разрешении 1 бит, то есть только меандр. Произведение частоты на 2^n не может превышать частоту такта таймера. Подобрать разрешение и делитель для своей частоты поможет калькулятор LEDC.

Источники такта по ESP-IDF:

ИсточникЧастотаОсобенность
APB_CLK80 МГцобе группы, наибольшие частоты ШИМ
REF_TICK1 МГцобе группы, не зависит от смены частоты процессора
RC_FAST_CLKоколо 8 МГцтолько низкоскоростная группа, работает в Light-sleep

Скважность задаётся числом от 0 до 2^n, где n — разрешение. ESP-IDF предупреждает о тонкости ESP32: если таймер работает с наибольшим возможным для частоты разрешением, значение 2^n задавать нельзя — внутренний счётчик переполнится.

Arduino-ESP32

В актуальном ядре Arduino-ESP32 работа с ШИМ упрощена — канал выбирается автоматически:

const int LED = 2;

void setup() {
  ledcAttach(LED, 5000, 13);   // 5 kHz, 13-bit resolution
}

void loop() {
  ledcWrite(LED, 4096);        // about 50 % duty
  delay(1000);
  ledcFade(LED, 0, 8191, 2000); // hardware fade to full brightness in 2 s
  delay(3000);
}

Что важно по документации Arduino-ESP32:

  • ledcAttach(pin, freq, resolution) — подключает вывод; разрешение у ESP32 от 1 до 20 бит. ledcAttachChannel делает то же с явным номером канала.
  • ledcWrite(pin, duty) — задаёт скважность числом от 0 до 2^n.
  • ledcChangeFrequency(pin, freq, resolution) — меняет частоту таймера, а значит, у всех каналов, которые его делят.
  • ledcFade — аппаратное плавное изменение скважности без участия процессора.
  • analogWrite(pin, value) — совместимая с Arduino функция со значением 0–255; частоту и разрешение для вывода меняют analogWriteFrequency и analogWriteResolution.

Главная ловушка — общие таймеры. Если подключить два вывода с одинаковыми частотой и разрешением, они займут один таймер, и ledcChangeFrequency на первом изменит частоту и второго. Документация советует либо сразу задавать разные частоты, либо разносить выводы по группам — например, каналы 0 и 8.

ESP-IDF

В ESP-IDF настройка идёт в два шага, и порядок важен: сначала таймер ledc_timer_config с частотой, разрешением и источником такта, затем канал ledc_channel_config с выводом, таймером и скважностью. ESP-IDF советует именно такой порядок — тогда сигнал на выводе сразу появляется с нужной частотой. Скважность меняют парой ledc_set_duty и ledc_update_duty, частоту — ledc_set_freq, а плавное изменение — функциями ledc_set_fade_with_time и ledc_fade_start после установки службы ledc_fade_func_install.

ШИМ и сон

По ESP-IDF драйвер LEDC не запрещает чипу засыпать. По умолчанию в Light-sleep выход ШИМ не работает. Режим LEDC_SLEEP_MODE_KEEP_ALIVE сохраняет сигнал во сне, если таймер тактируется от совместимого источника — RC_FAST_CLK, — но тогда в сне растёт ток: источник такта и домен питания LEDC не выключаются. В глубоком сне ШИМ не работает — подробнее о режимах в статье «Глубокий сон ESP32».

Другие периферийные блоки и отличия чипов — в разделе ESP32, общий расчёт частоты и скважности — в калькуляторе ШИМ.

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

Сколько каналов ШИМ у ESP32?

16 — по даташиту Espressif у контроллера LEDC шестнадцать независимых генераторов ШИМ. Они делятся на две группы по 8 каналов, и у каждой группы по 4 таймера, то есть всего 8 таймеров. Скважность у всех 16 каналов может быть своей, а разных частот одновременно — не больше восьми.

Почему при смене частоты на одном выводе ESP32 она меняется и на другом?

Потому что выводы делят таймер. По документации Arduino-ESP32 каналы одной группы с одинаковыми частотой и разрешением используют общий таймер, а ledcChangeFrequency меняет частоту у всех каналов этого таймера. Чтобы частоты были независимыми, задайте их разными сразу при подключении или разнесите каналы по группам — например, 0 и 8.

Какие выводы ESP32 поддерживают ШИМ?

Любые GPIO, способные работать на выход: по даташиту сигналы LEDC выводятся на любой вывод через матрицу GPIO. Исключение — GPIO34–GPIO39: они работают только на вход. Осторожнее со strapping-выводами вроде GPIO0, 2, 12 и 15: внешняя нагрузка на них может помешать загрузке.

Работает ли analogWrite на ESP32?

Да, в Arduino-ESP32 есть analogWrite, совместимый с Arduino: значение от 0 до 255. Частоту и разрешение для конкретного вывода меняют функциями analogWriteFrequency и analogWriteResolution. Под капотом это тот же контроллер LEDC, поэтому ограничения по таймерам действуют и здесь.

Источники

  1. Espressif. ESP32 Series Datasheet, v5.3 — разд. 4.8.8 — LEDC: 16 независимых генераторов ШИМ, разрешение до 20 бит, 8 таймеров с 20-разрядными счётчиками и дробными делителями, регулируемая фаза, дизеринг, автоматическое плавное изменение скважности; выводы — любые GPIO через матрицу; разд. 4.8.9 — отдельный ШИМ для двигателей (MCPWM); GPIO34–GPIO39 — только вход
  2. Espressif. ESP-IDF Programming Guide (stable), ESP32: LED Control (LEDC) — две группы по 8 каналов: высокоскоростная с автоматическим бесперебойным изменением и низкоскоростная, где изменение запускает программа; настройка: таймер (частота и разрешение), затем канал (таймер и GPIO); источники APB_CLK 80 МГц, REF_TICK 1 МГц, RC_FAST_CLK около 8 МГц (только низкоскоростная группа, работает в Light-sleep); 5 кГц — до 13 бит, 40 МГц — при 1 бите; duty от 0 до 2^n, при наибольшем разрешении значение 2^n задавать нельзя; аппаратное плавное изменение ledc_set_fade_with_time; в Light-sleep выход по умолчанию не работает, LEDC_SLEEP_MODE_KEEP_ALIVE сохраняет его ценой большего тока
  3. Espressif. Arduino-ESP32 Documentation: LED Control (LEDC) — у ESP32 16 каналов, 2 группы, по 4 таймера в группе; каналы 0–7 — группа 0, 8–15 — группа 1; каналы с одинаковыми частотой и разрешением в одной группе делят таймер, и ledcChangeFrequency меняет частоту у всех; ledcAttach, ledcAttachChannel, ledcWrite, ledcChangeFrequency, ledcFade; разрешение у ESP32 1–20 бит; у ESP32 источник такта по умолчанию LEDC_AUTO_CLK; analogWrite — значение 0–255, analogWriteResolution и analogWriteFrequency

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