153. GPIO и ШИМ на ESP32: выводы, электрика, strapping, матрица сигналов, LEDC, MCPWM
Вывод STM32 из раздела 8 — это регистры порта, альтернативная функция
из таблицы и ток до 20 мА. У ESP32 всё то же по смыслу и другое по
устройству: номера выводов с дырками, шесть из них заняты
флеш-памятью, шесть умеют только слушать, пять при сбросе решают судьбу
чипа, а любой сигнал периферии можно вывести почти на любой вывод через
матрицу. ШИМ здесь два разных блока: LEDC для светодиодов и
регуляторов, MCPWM для двигателей. Раздел опирается на три источника:
программу в QEMU (_verify/esp153/), заголовки ESP-IDF и таблицы
даташита ESP32 версии 5.3 (выписка datasheet_dc.txt); аналоговую
часть — ШИМ через RC — считает ngspice.
Как это называется в документации
Оригинал Перевод GPIO pad контактная площадка вывода со своим буфером IO_MUX мультиплексор вывода: прямой путь к «родной» функции GPIO matrix матрица: любой сигнал периферии на любой вывод input-only pins выводы только на вход (34–39), без подтяжек strapping pins выводы, защёлкиваемые при сбросе (0, 2, 5, 12, 15) drive strength (drive capability) сила выхода: четыре ступени тока VIH / VIL, VOH / VOL пороги входа, уровни выхода IOH / IOL ток выхода в единице и в нуле LEDC (LED PWM controller) ШИМ-контроллер общего назначения duty resolution, hpoint разрядность скважности, точка включения в периоде high-speed / low-speed mode два блока LEDC: с аппаратным и программным обновлением MCPWM (motor control PWM) ШИМ для двигателей: пары выходов, мёртвое время, защита operator, generator, comparator оператор, генератор, компаратор MCPWM
153.1 Номера выводов: где дырки
Программа перебирает номера через макросы драйвера:
for (int i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
valid += GPIO_IS_VALID_GPIO(i);
outputs += GPIO_IS_VALID_OUTPUT_GPIO(i);
}
номеров GPIO 40, из них существуют 35, с выходом 29
нет на кристалле: 24 28 29 30 31
только вход: 34 35 36 37 38 39
Номеров 40, существуют 35: 24 и 28–31 на кристалле нет. GPIO20 по заголовку существует, но выведен только у части корпусов, например ESP32-PICO-V3; у обычного чипа и модуля WROOM-32 его нет — там выводов 34, из них 28 с выходом. Шесть последних — только вход, и подтяжек у них нет: под кнопку на GPIO34 нужен внешний резистор. Ещё шесть, 6–11, существуют и умеют быть выходом, но на модуле к ним припаяна флеш-память (раздел 150) — драйвер их не запрещает, а программа, тронув их, перестаёт читать собственный код. Драйвер проверяет только то, что знает:
GPIO2 как выход: ESP_OK
GPIO34 как выход: ESP_ERR_INVALID_ARG
GPIO24 как выход: ESP_ERR_INVALID_ARG
Так что для изделия на WROOM-32 остаются 28 выходов минус 6 флеш-памяти минус strapping-выводы, которым при сбросе нельзя мешать, — около двух десятков свободных.
153.2 Электрика вывода: числа даташита
Таблица 5-3 даташита (3,3 В, 25 °C):
| Параметр | Значение | Что это значит |
|---|---|---|
| VIH | ≥ 0,75 × VDD = 2,475 В | порог единицы на входе |
| VIL | ≤ 0,25 × VDD = 0,825 В | порог нуля |
| вход, максимум | VDD + 0,3 = 3,6 В | 5 В на вход нельзя |
| VOH / VOL | ≥ 0,8 × VDD / ≤ 0,1 × VDD | уровни выхода |
| IOH | 40 мА при VOH ≥ 2,64 В, сила выхода на максимуме | ток «в единице» с одного вывода |
| IOL | 28 мА при VOL = 0,495 В | ток «в нуле» |
| RPU / RPD | 45 кОм | внутренние подтяжки |
| CIN | 2 пФ | ёмкость вывода |
| IIH / IIL | 50 нА | ток утечки входа |
Две оговорки из той же таблицы. Во-первых, 40 мА — на максимальной
силе выхода, а по умолчанию она средняя: ступеней четыре, от 0
(слабая) до 3, по умолчанию 2 (GPIO_DRIVE_CAP_DEFAULT в заголовке).
Программа ставит ступень 3 и читает её обратно — в эмуляторе получается
0, потому что регистров выводов в нём нет (153.8), а на плате прочтётся 3:
сила выхода GPIO2: 0 (ступени 0..3, по умолчанию 2)
Во-вторых, 40 мА — с одного вывода; чем больше выводов в одном домене питания отдают ток одновременно, тем меньше на каждый: даташит называет снижение примерно до 29 мА. Для выходов домена VDD_SDIO (выводы флеш-памяти) предел 20 мА. Правило то же, что в разделе 62: не вешать на выводы то, что можно повесить на транзистор.
Подтяжки 45 кОм — слабые: для I²C они не годятся (раздел 95 просит 1,5–4,7 кОм), для кнопки в шумном месте тоже лучше внешние 10 кОм.
153.3 Strapping: пять выводов при сбросе
При сбросе чип защёлкивает уровни пяти выводов и по ним решает, как загружаться (таблица 3-1 даташита):
| Вывод | Подтяжка внутри | Бит по умолчанию | За что отвечает |
|---|---|---|---|
| GPIO0 | вверх | 1 | 1 — загрузка из флеш-памяти, 0 — режим прошивки |
| GPIO2 | вниз | 0 | вместе с GPIO0 = 0 выбирает прошивку по UART |
| MTDI (GPIO12) | вниз | 0 | напряжение питания флеш-памяти: 0 — 3,3 В, 1 — 1,8 В |
| MTDO (GPIO15) | вверх | 1 | печать журнала ПЗУ в UART0 при старте |
| GPIO5 | вверх | 1 | тактирование SDIO-ведомого |
Загрузка из флеш-памяти — при GPIO0 = 1 независимо от GPIO2; режим
прошивки — GPIO0 = 0 и GPIO2 = 0 (таблица 3-3). Отсюда две кнопки
DevKit: EN дёргает сброс, BOOT притягивает GPIO0 к земле. Защёлки
держат значения до выключения питания, и после сброса выводы свободны
— но что бы ни висело на них снаружи, оно не должно тянуть уровень в
момент сброса. Самый коварный — GPIO12: подтянутый вверх датчиком, он
переведёт питание флеш-памяти на 1,8 В, и модуль с 3,3-вольтовой
микросхемой не загрузится вовсе. Если GPIO12 всё же нужен с подтяжкой вверх,
напряжение флеш-памяти закрепляют в eFuse: espefuse.py set_flash_voltage 3.3V.
Это необратимо, и после этого уровень GPIO12 при сбросе не читается.
153.4 IO_MUX и матрица
У каждого вывода есть несколько «родных» функций через IO_MUX — так
подключены флеш-память и SPI на своих выводах, это самый быстрый путь.
Всё остальное идёт через матрицу GPIO: у периферии пронумерованные
сигналы — в заголовке gpio_sig_map.h 384 имени с номерами до 255, а
256 означает «обычный GPIO», — и любой из них можно назначить почти на
любой вывод:
имён сигналов в gpio_sig_map.h: 384, наибольший номер: 256, SIG_GPIO_OUT_IDX 256
U0RXD_IN_IDX 14
U0TXD_OUT_IDX 14
SPICLK_OUT_IDX 0
I2CEXT0_SCL_IN_IDX 29
LEDC_HS_SIG_OUT0_IDX 71
PWM0_OUT0A_IDX 32
Драйверы делают это сами: ledc_channel_config с gpio_num = 2
соединяет сигнал LEDC_HS_SIG_OUT0 с выводом 2 через матрицу, и
переназначить UART, I²C или ШИМ на другие выводы — вопрос одного
числа в структуре, а не таблицы альтернативных функций. Цена матрицы —
лишняя задержка и предел частоты: SPI через матрицу медленнее, чем
через IO_MUX, и для 40–80 МГц выбирают родные выводы.
153.5 LEDC: частота против разрядности
LEDC — 16 каналов: два режима по 8 каналов и по 4 таймера, таймер до 20 бит, тактирование 80 МГц. Скважность задаётся в тиках таймера, и чем выше частота ШИМ, тем меньше тиков в периоде — разрядность и частота торгуются друг за друга по формуле f_max = 80 МГц / 2^бит. Драйвер считает это сам:
for (size_t i = 0; i < sizeof freqs / sizeof freqs[0]; i++) {
uint32_t bits = ledc_find_suitable_duty_resolution(80000000, freqs[i]);
50 Гц: до 20 бит (шаг 0.0001 %)
1000 Гц: до 16 бит (шаг 0.0015 %)
5000 Гц: до 13 бит (шаг 0.0122 %)
25000 Гц: до 11 бит (шаг 0.0488 %)
100000 Гц: до 9 бит (шаг 0.1953 %)
312500 Гц: до 8 бит (шаг 0.3906 %)
1000000 Гц: до 6 бит (шаг 1.5625 %)
Сервопривод на 50 Гц получает все 20 бит, светодиод на 5 кГц — 13, а ШИМ на 312,5 кГц, которым питают преобразователь, — только 8, то есть шаг 0,4 %. Попросить лишнего нельзя:
ledc_timer_config 5 кГц / 13 бит: ESP_OK
ledc_timer_config 100 кГц / 13 бит: ESP_FAIL
Настройка — два вызова, таймер и канал:
ledc_timer_config_t tc = { .speed_mode = LEDC_HIGH_SPEED_MODE, .timer_num = LEDC_TIMER_0, .duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000, .clk_cfg = LEDC_AUTO_CLK };
ledc_channel_config_t cc = { .gpio_num = 2, .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0,
.duty = 4096, .hpoint = 0 };
Скважность 4096 из 8192 — 50 %. Дальше ledc_set_duty и
ledc_update_duty: первый готовит значение, второй применяет —
поэтому забытый update и есть самая частая ошибка с LEDC. Режим
high-speed обновляет скважность аппаратно на границе периода, low-speed
— программно; у второго есть свой тактовый генератор, работающий в
лёгком сне. Аппаратное плавное изменение ledc_fade доводит
скважность до цели за заданное время без участия процессора.
153.6 ШИМ как ЦАП: цена частоты
Тот же таймер даёт либо точность, либо частоту, и на RC-фильтре это видно. ngspice: 3,3 В, скважность 50 %, фильтр 10 кОм / 100 нФ (постоянная времени 1 мс), три пары с одного таймера:
f=1k ripple_V=0.808228
f=5k ripple_V=0.164858
f=312.5k ripple_V=0.002636
Размах пульсаций при 50 % — U · th(1/(4·f·RC)), это и считает ngspice. 16 бит на 1 кГц дают пульсации 0,81 В — половину шкалы, никакой точности; 13 бит на 5 кГц — 0,16 В; 8 бит на 312,5 кГц — 2,6 мВ, но шаг скважности 0,4 % — те же 13 мВ. Компромисс для ШИМ-ЦАП из раздела 50 на ESP32 выглядит так: 10–12 бит на 20–80 кГц и фильтр второго порядка.
153.7 MCPWM: ШИМ для двигателей
Второй блок — MCPWM, два одинаковых модуля:
SOC_MCPWM_GROUPS (2)
SOC_MCPWM_TIMERS_PER_GROUP (3)
SOC_MCPWM_OPERATORS_PER_GROUP (3)
SOC_MCPWM_COMPARATORS_PER_OPERATOR (2)
SOC_MCPWM_GENERATORS_PER_OPERATOR (2)
SOC_MCPWM_GPIO_FAULTS_PER_GROUP (3)
SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP (1)
В каждом модуле три таймера, три оператора по два генератора — 12 выходов на чип, парами A/B под верхний и нижний ключ полумоста с мёртвым временем, три входа аварии, которые аппаратно гасят выходы, и таймер захвата для датчиков Холла и энкодера. Это то, что в STM32 делает таймер TIM1 с комплементарными выходами (раздел 13): мост, трёхфазный двигатель, синхронный выпрямитель. Для светодиода и сервопривода MCPWM избыточен — там LEDC.
153.8 Что не умеет эмулятор
GPIO2: записали 1 — читаем 0, записали 0 — читаем 0
ledc_get_freq: 5000 Гц, ledc_get_duty: 0
Вывод в режиме INPUT_OUTPUT на кристалле читает собственный уровень
— здесь читает ноль; скважность, только что записанная в канал, читается
нулём. У QEMU для ESP32 нет модели выводов и LEDC: регистры пишутся в
пустоту. Всё, что выше, — проверка логики драйвера и арифметики; уровни
и осциллограммы проверяют на плате. Это граница эмулятора, и полезно
знать её заранее, чтобы не искать в своём коде ошибку, которой нет.
153.9 Грабли
| Симптом | Причина |
|---|---|
gpio_set_direction вернул ESP_ERR_INVALID_ARG |
вывод 34–39 только вход или номера нет на кристалле |
| кнопка на GPIO34 «плавает» | у 34–39 нет внутренних подтяжек |
после gpio_config на GPIO6–11 плата перезагружается |
это выводы флеш-памяти |
| плата не стартует с подключённой периферией | что-то тянет strapping-вывод при сбросе; GPIO12 вверх = 1,8 В на флеш |
| выход «не тянет» светодиод с резистором 30 Ом | сила выхода 2 из 3 и 40 мА только при VOH ≥ 2,64 В |
| датчик на 5 В сжёг вход | вход до VDD + 0,3 = 3,6 В |
ledc_timer_config → ESP_FAIL |
частота × 2^бит больше 80 МГц |
ledc_set_duty ничего не меняет |
забыт ledc_update_duty |
| ШИМ-ЦАП шумит | низкая частота ради разрядности; поднять частоту, фильтр 2-го порядка |
| SPI через матрицу не даёт 80 МГц | родные выводы IO_MUX |
| в эмуляторе GPIO читается нулём | QEMU не моделирует выводы |
153.10 Итоги
- 40 номеров, 35 существуют, 29 с выходом; нет 24, 28–31; GPIO20 — не у всех корпусов (у WROOM-32 выводов 34, с выходом 28); 34–39 только вход без подтяжек; 6–11 — флеш.
- Даташит: VIH 0,75 VDD, VIL 0,25 VDD, вход до 3,6 В, IOH 40 мА (максимальная сила, один вывод), IOL 28 мА, подтяжки 45 кОм; сила выхода 0–3, по умолчанию 2.
- Strapping: GPIO0 = 0 — прошивка; GPIO12 = 1 — флеш на 1,8 В; GPIO15 — журнал ПЗУ.
- Матрица: 384 имени сигналов с номерами до 255 на любой вывод; IO_MUX — прямой путь.
- LEDC: 16 каналов, f_max = 80 МГц / 2^бит: 50 Гц — 20 бит, 5 кГц — 13, 312,5 кГц — 8;
set_duty+update_duty. - ШИМ через RC 1 мс: 0,81 В пульсаций на 1 кГц, 0,16 В на 5 кГц, 2,6 мВ на 312,5 кГц.
- MCPWM: 2 × 3 оператора × 2 генератора = 12 выходов, мёртвое время, аварийные входы.
Полные исходники
main.c — GPIO и ШИМ LEDC на ESP32: что разрешает драйвер.
/* Раздел 153: GPIO и ШИМ LEDC на ESP32 — что разрешает драйвер, что считает сам. */
#include <stdio.h>
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/gpio_num.h"
#include "soc/soc_caps.h"
static const char *TAG = "gpio153";
static void try_output(int pin)
{
esp_err_t e = gpio_set_direction(pin, GPIO_MODE_OUTPUT);
printf("GPIO%-2d как выход: %s\n", pin, esp_err_to_name(e));
}
void app_main(void)
{
/* какие номера вообще существуют и какие умеют быть выходом — по маскам soc_caps */
int valid = 0, outputs = 0;
for (int i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
valid += GPIO_IS_VALID_GPIO(i);
outputs += GPIO_IS_VALID_OUTPUT_GPIO(i);
}
printf("номеров GPIO %d, из них существуют %d, с выходом %d\n", SOC_GPIO_PIN_COUNT, valid, outputs);
printf("нет на кристалле:");
for (int i = 0; i < SOC_GPIO_PIN_COUNT; i++)
if (!GPIO_IS_VALID_GPIO(i)) printf(" %d", i);
printf("\nтолько вход:");
for (int i = 0; i < SOC_GPIO_PIN_COUNT; i++)
if (GPIO_IS_VALID_GPIO(i) && !GPIO_IS_VALID_OUTPUT_GPIO(i)) printf(" %d", i);
printf("\n");
esp_log_level_set("gpio", ESP_LOG_NONE); /* драйвер и так вернёт код ошибки */
try_output(2);
try_output(34);
try_output(24);
/* выход с обратным чтением: режим INPUT_OUTPUT читает свой же уровень */
gpio_config_t io = { .pin_bit_mask = 1ULL << 2, .mode = GPIO_MODE_INPUT_OUTPUT, .pull_up_en = 0, .pull_down_en = 0, .intr_type = GPIO_INTR_DISABLE };
ESP_ERROR_CHECK(gpio_config(&io));
gpio_set_level(2, 1);
int a = gpio_get_level(2);
gpio_set_level(2, 0);
int b = gpio_get_level(2);
printf("GPIO2: записали 1 — читаем %d, записали 0 — читаем %d\n", a, b);
/* сила выхода: четыре ступени */
gpio_drive_cap_t cap;
gpio_set_drive_capability(2, GPIO_DRIVE_CAP_3);
gpio_get_drive_capability(2, &cap);
printf("сила выхода GPIO2: %d (ступени 0..%d, по умолчанию %d)\n", (int)cap, (int)GPIO_DRIVE_CAP_MAX - 1, (int)GPIO_DRIVE_CAP_DEFAULT);
/* LEDC: сколько бит разрешения вмещает частота при тактировании 80 МГц */
printf("LEDC: каналов %d × 2 режима, таймер до %d бит\n", SOC_LEDC_CHANNEL_NUM, SOC_LEDC_TIMER_BIT_WIDTH);
const uint32_t freqs[] = { 50, 1000, 5000, 25000, 100000, 312500, 1000000 };
for (size_t i = 0; i < sizeof freqs / sizeof freqs[0]; i++) {
uint32_t bits = ledc_find_suitable_duty_resolution(80000000, freqs[i]);
printf(" %7lu Гц: до %2lu бит (шаг %.4f %%)\n", (unsigned long)freqs[i], (unsigned long)bits, bits ? 100.0 / (1u << bits) : 0.0);
}
ledc_timer_config_t tc = { .speed_mode = LEDC_HIGH_SPEED_MODE, .timer_num = LEDC_TIMER_0, .duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000, .clk_cfg = LEDC_AUTO_CLK };
printf("ledc_timer_config 5 кГц / 13 бит: %s\n", esp_err_to_name(ledc_timer_config(&tc)));
tc.freq_hz = 100000;
printf("ledc_timer_config 100 кГц / 13 бит: %s\n", esp_err_to_name(ledc_timer_config(&tc)));
tc.freq_hz = 5000;
ledc_timer_config(&tc);
ledc_channel_config_t cc = { .gpio_num = 2, .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0,
.duty = 4096, .hpoint = 0 };
printf("ledc_channel_config GPIO2, скважность 4096/8192: %s\n", esp_err_to_name(ledc_channel_config(&cc)));
printf("ledc_get_freq: %lu Гц, ledc_get_duty: %lu\n", (unsigned long)ledc_get_freq(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0),
(unsigned long)ledc_get_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0));
ESP_LOGI(TAG, "done");
}
Дальше: раздел 154 — АЦП и ЦАП ESP32.