3、GPIO 基础与 LED 控制:ESP32 引脚功能、数字输出、点亮板载 LED、编写 Blink 程序

好,咱们正式开始动手了。这一章,我带你从最基础的东西开始——GPIO。说白了,就是让芯片的引脚听话,输出高电平或者低电平。你想想看,智能插座的核心不就是控制继电器通断吗?那背后就是 GPIO 在干活。

3.1 ESP32 的引脚功能概览

ESP32 芯片有 34 个 GPIO 引脚,编号从 GPIO0 到 GPIO33。但要注意,不是所有引脚都能随便用。我在项目里就吃过这个亏——选了 GPIO6 去接 LED,结果怎么都不亮,查了半天才发现它被内部 Flash 占用了。

这里我列个常用引脚表,你写代码时对照着看:

引脚编号 功能说明 注意事项
GPIO0 普通 IO / 启动模式选择 低电平进入下载模式,慎用
GPIO1 UART0 TX(调试串口) 用作普通 IO 时会影响串口输出
GPIO2 普通 IO / 板载 LED 大部分开发板板载 LED 接这个脚
GPIO3 UART0 RX(调试串口) 同上,慎用
GPIO4 普通 IO 常用,无特殊限制
GPIO5 普通 IO 常用,无特殊限制
GPIO6~GPIO11 连接内部 Flash 不要用于普通 IO!
GPIO12 普通 IO / 电压选择 上电时高电平会降低 Flash 电压,小心
GPIO13~GPIO19 普通 IO 大部分可用,注意 GPIO14 是 JTAG 相关
GPIO21~GPIO23 普通 IO 常用,无特殊限制
GPIO25~GPIO27 普通 IO / DAC 也可用作模拟输出
GPIO32~GPIO33 普通 IO 常用,无特殊限制
⚠️ 避坑指南: 我曾经在 GPIO6 上接了一个按键,结果程序死活跑不起来。后来才发现,ESP32 的 Flash 是通过 SPI 挂在 GPIO6~GPIO11 上的。你千万别用这些脚做普通 IO,否则系统直接罢工。

3.2 数字输出:让引脚输出高/低电平

GPIO 的数字输出,说白了就是让引脚输出 3.3V(高电平)或者 0V(低电平)。在 Arduino 框架下,只需要两个函数:

  • pinMode(pin, OUTPUT) —— 把引脚设置为输出模式
  • digitalWrite(pin, HIGH/LOW) —— 输出高电平或低电平

嗯,就这么简单。但有个细节我想提醒你:ESP32 的 GPIO 输出电流有限,大概 40mA 左右。直接驱动 LED 没问题,但要是想驱动继电器或者电机,必须加三极管或者 MOS 管驱动。我刚开始做智能插座时,直接拿 GPIO 去推继电器,结果芯片烧了……

💡 我的习惯: 凡是驱动电流超过 10mA 的负载,我都会加一级驱动电路。安全第一,别省那几毛钱。

3.3 点亮板载 LED

大部分 ESP32 开发板(比如 NodeMCU-32S、ESP32-DevKitC)都有一颗板载 LED,通常接在 GPIO2 上。这颗 LED 是低电平点亮,也就是说:

  • digitalWrite(2, LOW) —— LED 亮
  • digitalWrite(2, HIGH) —— LED 灭

为什么会这样?因为厂家设计时,LED 的正极通过电阻接了 3.3V,负极接到 GPIO2。GPIO 输出低电平时,电流从 3.3V 流过 LED 再到 GPIO,LED 就亮了。你想想看,这其实挺常见的——很多开发板都这么干。

不过,不同开发板的板载 LED 引脚可能不一样。有的接 GPIO2,有的接 GPIO16,甚至有的接 GPIO5。我建议你拿到板子后,先查一下原理图或者看板子上的丝印。实在不行,拿万用表量一下——这是我个人的习惯,稳当。

3.4 编写 Blink 程序

好,咱们直接上代码。Blink 程序就是让 LED 一亮一灭,循环往复。这是嵌入式世界的 "Hello World"。

// Blink 程序 —— 点亮板载 LED
// 适用于大部分 ESP32 开发板

#define LED_PIN 2  // 板载 LED 通常接 GPIO2

void setup() {
  pinMode(LED_PIN, OUTPUT);  // 设置引脚为输出模式
}

void loop() {
  digitalWrite(LED_PIN, LOW);   // 点亮 LED(低电平有效)
  delay(1000);                  // 等待 1 秒
  
  digitalWrite(LED_PIN, HIGH);  // 熄灭 LED
  delay(1000);                  // 等待 1 秒
}

这段代码的逻辑很简单:

  1. setup() 里设置引脚模式,只执行一次。
  2. loop() 里不断循环:亮 1 秒,灭 1 秒。
  3. delay(1000) 就是让程序暂停 1000 毫秒。

你上传代码后,如果看到板载 LED 以 1 秒为周期闪烁,那就说明成功了。如果没亮,先检查一下 LED 引脚对不对。我遇到过好几次,学生拿着 GPIO2 的代码往 GPIO16 的板子上烧,结果跑来问我为什么灯不亮……

🔑 关键点: Blink 程序虽然简单,但它验证了三件事:开发环境配置正确、代码能烧录、GPIO 输出功能正常。这是后续所有项目的基础。

3.5 扩展:控制外部 LED

板载 LED 玩熟了,咱们试试外接 LED。你需要准备:

  • 一个 LED(颜色随意,我习惯用红色)
  • 一个 220Ω 电阻(限流用,别省)
  • 两根杜邦线

接线方式:

  1. LED 长脚(正极)接 220Ω 电阻,电阻另一端接 GPIO4
  2. LED 短脚(负极)接 GND

代码只需要改一行:把 LED_PIN 从 2 改成 4。注意,外部 LED 是高电平点亮,所以 digitalWrite(4, HIGH) 是亮,LOW 是灭。

#define LED_PIN 4  // 外部 LED 接 GPIO4

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_PIN, HIGH);  // 点亮(高电平有效)
  delay(500);
  digitalWrite(LED_PIN, LOW);   // 熄灭
  delay(500);
}

你看,代码几乎一样,只是引脚号和电平逻辑变了。这就是 GPIO 的灵活性——同一个函数,换个引脚就能控制不同的设备。

💡 一个小技巧: 我写代码时习惯把引脚定义放在文件最前面,用 #define 或者 const int。这样以后改硬件时,只需要改一行,不用满篇找数字。这个习惯帮我省了不少事。

3.6 本章小结

这一章咱们干了三件事:

  • 搞清楚了 ESP32 哪些引脚能用,哪些是坑
  • 学会了用 pinModedigitalWrite 控制数字输出
  • 亲手写了一个 Blink 程序,让 LED 闪了起来

嗯,别小看这个 Blink。它背后藏着嵌入式开发的核心逻辑:初始化硬件、循环执行任务、控制外设。咱们后面做智能插座,无非是把 LED 换成继电器,把延时换成定时器,把手动控制换成远程指令——本质是一样的。

下一章,咱们会深入按键输入和中断。到时候你就能用按键控制 LED 了,那才是真正的交互。先把这个 Blink 跑通,咱们下章见。