ШИМ 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_CLK | 80 МГц | обе группы, наибольшие частоты ШИМ |
| REF_TICK | 1 МГц | обе группы, не зависит от смены частоты процессора |
| 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, поэтому ограничения по таймерам действуют и здесь.
Источники
- Espressif. ESP32 Series Datasheet, v5.3 — разд. 4.8.8 — LEDC: 16 независимых генераторов ШИМ, разрешение до 20 бит, 8 таймеров с 20-разрядными счётчиками и дробными делителями, регулируемая фаза, дизеринг, автоматическое плавное изменение скважности; выводы — любые GPIO через матрицу; разд. 4.8.9 — отдельный ШИМ для двигателей (MCPWM); GPIO34–GPIO39 — только вход
- 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 сохраняет его ценой большего тока
- 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
Нашли ошибку?
Смотрите также
- ESP32ESP32, S2, S3, C2, C3, C5, C6, H2, P4: ядра, радио, USB, GPIO — сравнение и выбор
- ШИМ ESP32: LEDCРазрешение по частоте, делитель A + B/256, фактическая частота, duty, код ESP-IDF
- ЦАП на ШИМПульсации после RC, установление, разрешение
- ШИМPWM: среднее напряжение через коэффициент заполнения
- Коэффициент заполненияD = τ / T и скважность S = 1 / D
- Глубокий сон ESP32Режимы сна, токи, пробуждение по таймеру и выводам, RTC-память, расчёт батареи