GPIO 基础与 LED 控制:数字输出、GPIO 模式设置、LED 闪烁实验、PWM 调光
各位同学,咱们今天聊点实在的。GPIO 这东西,说白了就是芯片的「手脚」。ESP32 有 34 个 GPIO 引脚,但并不是每个都能随便用。我刚开始玩 ESP32 的时候,就踩过这个坑——照着网上的图接了个传感器,结果死活读不到数据,查了半天才发现那个引脚默认被焊死了,用作 Flash 通信。
所以这一章,咱们从最基础的开始:怎么让一个 LED 亮起来,怎么让它闪烁,最后再玩点高级的——用 PWM 调光。你把这些搞明白了,后面接传感器、控制电机,思路都是一样的。
1. GPIO 数字输出模式
GPIO 引脚可以配置成输入或输出。输出模式下,你可以让引脚输出高电平(3.3V)或低电平(0V)。ESP32 的工作电压是 3.3V,所以输出高电平时,引脚上就是 3.3V。
配置一个 GPIO 为输出模式,在 Arduino 框架下只需要一行代码:
pinMode(2, OUTPUT); // 将 GPIO2 设置为输出模式
然后你就可以用 digitalWrite() 来控制它了:
digitalWrite(2, HIGH); // 输出高电平,LED 亮
digitalWrite(2, LOW); // 输出低电平,LED 灭
嗯,这里要注意:ESP32 的 GPIO2 在大多数开发板上都连接了一个板载 LED。你拿到的板子如果不一样,先查一下原理图,别上来就焊。
2. LED 闪烁实验
闪烁实验就是让 LED 交替亮灭。核心逻辑很简单:亮一会儿,灭一会儿,循环。但这里有个细节——延时函数 delay() 会阻塞 CPU,在延时期间你啥也干不了。
我做过一个项目,用 delay() 控制 LED 闪烁,同时还想用按键切换模式。结果按键按下去要等几百毫秒才有反应,用户体验极差。后来改用 millis() 非阻塞延时,才解决了这个问题。
先看阻塞版本的代码:
void setup() {
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, HIGH);
delay(1000); // 亮 1 秒
digitalWrite(2, LOW);
delay(1000); // 灭 1 秒
}
这个代码能跑,但不够好。咱们看看非阻塞版本怎么写:
const int ledPin = 2;
unsigned long previousMillis = 0;
const long interval = 1000;
int ledState = LOW;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
// 这里可以干别的事,比如读按键、处理传感器数据
}
你想想看,用 millis() 之后,CPU 不再被 delay() 卡住。每次 loop() 跑一圈,检查一下时间到了没,到了就翻转状态,没到就继续干别的。这才是嵌入式开发的正确姿势。
3. GPIO 模式设置详解
ESP32 的 GPIO 模式比 Arduino 的 UNO 复杂一些。除了简单的 INPUT 和 OUTPUT,还有这些模式:
| 模式 | 说明 | 典型用途 |
|---|---|---|
| OUTPUT | 推挽输出,可输出高/低电平 | 驱动 LED、蜂鸣器 |
| INPUT | 高阻抗输入 | 读取按键、传感器数字信号 |
| INPUT_PULLUP | 内部上拉电阻(约 45kΩ) | 按键输入,省去外部上拉电阻 |
| INPUT_PULLDOWN | 内部下拉电阻(约 45kΩ) | 某些传感器需要默认低电平 |
| OPEN_DRAIN | 开漏输出 | I2C 总线、多设备共享线路 |
我曾经在一个产品里用了 INPUT_PULLDOWN,结果发现 ESP32 的部分引脚不支持下拉模式。查了数据手册才知道,只有 RTC 域的 GPIO 才支持下拉。所以,用之前先查手册,别想当然。
4. PWM 调光原理
PWM 的全称是 Pulse Width Modulation,脉冲宽度调制。说白了,就是让引脚快速地在高电平和低电平之间切换,通过调整高电平占一个周期的时间比例(占空比),来改变 LED 的亮度。
人眼有视觉暂留效应,当切换频率超过约 50Hz 时,你看到的就不是闪烁,而是稳定的亮度。ESP32 的 PWM 频率可以设到 40MHz,但驱动 LED 一般用 1kHz 到 5kHz 就够了。
ESP32 的 PWM 和 Arduino UNO 不太一样。UNO 只有 6 个 PWM 引脚,而且频率固定。ESP32 的任意 GPIO 都可以输出 PWM,你需要先配置一个 LEDC 通道:
const int ledPin = 2;
const int freq = 5000; // 5kHz
const int ledChannel = 0; // 通道 0
const int resolution = 8; // 8 位分辨率,0-255
void setup() {
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(ledPin, ledChannel);
}
void loop() {
// 从暗到亮
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
// 从亮到暗
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
}
这里 resolution = 8 表示占空比用 8 位表示,范围 0-255。0 是全灭,255 是全亮。如果你想更细腻的调光,可以设成 10 位(0-1023)或 12 位(0-4095)。
5. 知识结构图
下面这张图把本章的核心知识点串起来了,你一看就明白 GPIO 输出、闪烁控制和 PWM 调光之间的关系:
6. 实战建议与避坑指南
- 限流电阻不能省:LED 直接接 GPIO 会烧引脚。串联一个 220Ω 到 1kΩ 的电阻,具体值根据 LED 的额定电流算一下。我一般用 330Ω,通用性比较好。
- 引脚选择要谨慎:GPIO6-11 默认用于 Flash 通信,别乱用。GPIO34-39 只能输入,不能输出。GPIO0 在启动时拉低会进入下载模式。
- PWM 频率别太高:驱动 LED 用 1kHz-5kHz 就够了。频率太高,LED 驱动电路的开关损耗会增加,而且人眼也看不出区别。
- 多通道 PWM:ESP32 有 16 个 LEDC 通道,可以独立控制 16 路 PWM。但要注意,通道和引脚不是一一对应的,一个通道可以绑定多个引脚,输出相同的 PWM 信号。
好了,这一章的内容就到这儿。你先把 LED 闪烁跑起来,再试试 PWM 调光。代码量不大,但背后的概念——GPIO 模式、电平控制、时序控制、PWM 原理——是嵌入式开发的基石。搞懂了这些,后面学什么传感器都顺手。