从 STM32 转 ESP32 的第一个坎,几乎都倒在 GPIO 上:明明配置一模一样,按键却毫无反应,或者一上电就疯狂触发中断——别急着怀疑硬件,差异藏在芯片架构里。
一、ESP32 的 GPIO 不是 STM32 的 GPIO
STM32 的 GPIO 像一栋固定楼层的写字楼:USART1 的 TX 永远在 PA9,I2C1 的 SCL 永远在 PB6,最多通过 AF 重映射到几个备选脚。而 ESP32 把整栋楼改成了"任意可调度的工位"——这就是 GPIO 交换矩阵(GPIO Matrix)。
通过 GPIO Matrix + IO MUX,ESP32 可以把几乎任何外设信号(UART/SPI/I2C/PWM/I2S)连接到任意一个支持复用的 GPIO 上。代价是绕道矩阵后,部分高速信号的时序会略劣于 IO MUX 直连。
二、内部结构:一张图看懂三层路由
三、三个关键参数,记住它们
|
34 个
物理 GPIO 数量
|
45 kΩ
内部上拉典型值
|
3.3 V
IO 工作电压
|
注意 45 kΩ 只是典型值,实测在 10 kΩ 到 100 kΩ 之间漂移——所以 I2C、按键这种对上拉敏感的场合,老老实实外置 4.7 kΩ 或 10 kΩ 电阻,别依赖内部上拉。
四、和 STM32 的核心差异表
| 维度 | STM32 (F4) | ESP32 |
|---|---|---|
| 引脚复用 | AF0~AF15 固定 | 矩阵任意路由 |
| 中断分组 | EXTI 16 条线 | 每脚独立 |
| ISR 位置 | Flash 可执行 | 需 IRAM_ATTR |
| 5V 容忍 | 部分支持 | 不支持 |
| 输入专用脚 | 无 | GPIO34-39 |
| 睡眠唤醒 | EXTI/WKUP | 仅 RTC GPIO |
五、中断的"两步注册":最容易翻车的地方
STM32 习惯里你只要 配置 NVIC + 实现 HAL_GPIO_EXTI_Callback 就行。ESP32 走的是 FreeRTOS 风格——必须先装"总服务",再为每个脚挂"个人处理器",少一步都不响。
5.1 ESP-IDF 标准模板
#define BTN_GPIO GPIO_NUM_0
static void IRAM_ATTR
btn_isr(void *arg) {
// 仅做最轻操作
BaseType_t hp = pdFALSE;
xQueueSendFromISR(q, &arg, &hp);
if (hp) portYIELD_FROM_ISR();
}
void app_main(void) {
gpio_config_t io = {
.pin_bit_mask = 1ULL << BTN_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1,
.intr_type = GPIO_INTR_NEGEDGE,
};
gpio_config(&io);
// ① 装总服务(全局一次)
gpio_install_isr_service(0);
// ② 挂个人处理器
gpio_isr_handler_add(
BTN_GPIO, btn_isr, NULL);
}
gpio_install_isr_service() 直接 add handler,结果按键完全没反应,串口也不报错——这是 ESP32 新手的头号"静默 bug"。5.2 为什么必须 IRAM_ATTR
ESP32 的代码默认存放在外部 SPI Flash 中,运行时通过 cache 映射执行。一旦 cache 未命中,CPU 会触发 Flash 访问,而 ISR 期间访问 Flash 会直接 panic 死机。
解决办法:给 ISR 函数加 IRAM_ATTR 属性,强制把它链接到内部 RAM,确保任何时候都能直接执行。这是 ESP32 中断编程最特别的一条规则,没有之一。
attachInterrupt() 时,回调函数也需要加 IRAM_ATTR(Arduino-ESP32 2.x 起强制要求)。六、Strapping 引脚:开机时千万别拉
GPIO0、GPIO2、GPIO5、GPIO12、GPIO15 这五个脚在芯片复位瞬间会被采样,用于决定启动模式和 Flash 电压。如果你在外面强制拉低 GPIO0,板子会进入下载模式而不是正常运行——所以这些脚做按键输入时,别在上电瞬间被外设拉死。
七、新手最常踩的三个坑
坑 1:用 GPIO34-39 做输出。 它们物理上没有输出驱动,配置成 OUTPUT 也没用——这六个脚是纯输入。
坑 2:对 GPIO34-39 启用内部上拉。 这六个脚连内部上下拉电路都没有,必须外置电阻,否则浮空乱跳。
坑 3:ISR 里调用 printf 或 vTaskDelay。 任何会阻塞、占用 Flash 或申请锁的 API 都不能进 ISR,正确做法是只丢一个事件到 FreeRTOS Queue,由后台任务处理。
一图胜千言
《ESP32 Wi-Fi 入门:Station/AP/SmartConfig 三模式》