3、ESP32 GPIO基础:从按键到LED的输入输出实战
各位同学,今天我们进入一个非常核心的环节——GPIO。说白了,就是ESP32的那些金属引脚,怎么让它们听话,怎么让它们感知外部世界。
我个人习惯把GPIO比作芯片的「手脚」。有的负责「摸」(输入),有的负责「动」(输出)。今天我们就用按键和LED,把这两件事彻底搞明白。
3.1 认识ESP32的GPIO引脚
ESP32芯片有34个GPIO引脚,但不是所有都能随便用。我刚开始做项目时,就踩过这个坑——选了一个内部已经接死的引脚,折腾半天没反应。
这里我整理了一张常用引脚表,建议你保存下来:
| 引脚编号 | 功能说明 | 注意事项 |
|---|---|---|
| GPIO0 | 下载模式选择 | 低电平进入下载模式,慎用 |
| GPIO2 | 板载LED | 很多开发板默认接了这个 |
| GPIO4、GPIO5 | 普通IO | 我最常用的两个引脚 |
| GPIO12 | 启动电压选择 | 上拉会改变Flash电压,别乱用 |
| GPIO13-GPIO19 | 普通IO | 大部分可安全使用 |
| GPIO34-GPIO39 | 仅输入 | 不能输出,只能读取 |
3.2 两个核心函数:digitalWrite 和 digitalRead
Arduino框架下,GPIO操作被封装得非常简单。你只需要记住两个函数:
- pinMode(pin, mode) —— 设置引脚模式(输入/输出)
- digitalWrite(pin, value) —— 写数字信号(HIGH/LOW)
- digitalRead(pin) —— 读数字信号(返回HIGH/LOW)
嗯,这里要注意:pinMode 必须在 setup() 里先调用,否则引脚默认状态是不确定的。我见过新手直接写 digitalWrite,结果LED死活不亮——其实就是忘了设模式。
3.3 实战:按键控制LED
好,理论说完了,我们直接上代码。这个例子的逻辑很简单:
- 按键按下 → GPIO读取到低电平 → LED点亮
- 按键松开 → GPIO读取到高电平 → LED熄灭
你想想看,这不就是最基础的「输入决定输出」吗?
硬件连接
- LED正极 → GPIO2(板载LED)
- 按键一端 → GPIO4
- 按键另一端 → GND
- GPIO4内部上拉(代码里设置)
完整代码
// 引脚定义
const int ledPin = 2; // 板载LED
const int buttonPin = 4; // 按键输入
void setup() {
Serial.begin(115200);
// 设置引脚模式
pinMode(ledPin, OUTPUT); // LED为输出
pinMode(buttonPin, INPUT_PULLUP); // 按键为输入,启用内部上拉
Serial.println("按键控制LED已启动");
}
void loop() {
// 读取按键状态
int buttonState = digitalRead(buttonPin);
// 按键按下时,引脚为低电平
if (buttonState == LOW) {
digitalWrite(ledPin, HIGH); // 点亮LED
Serial.println("按键按下,LED点亮");
} else {
digitalWrite(ledPin, LOW); // 熄灭LED
}
delay(50); // 简单消抖
}
INPUT_PULLUP启用内部上拉电阻,这样按键悬空时引脚为高电平- 按键接地,按下时引脚被拉低到GND
delay(50)是为了消除机械按键的抖动
3.4 我踩过的坑:按键抖动
第一次做这个实验时,我发现LED有时候会「闪一下」或者「乱跳」。为什么呢?
其实机械按键在按下和松开的瞬间,金属触点会弹跳几次,产生多个高低电平变化。如果不处理,程序会误认为按了好多次。
我曾经在一个门禁项目中,因为没做消抖处理,导致按键一次触发三次开门信号。嗯,后来被客户投诉了。
最简单的消抖方法就是加一个 delay(20-50ms),等电平稳定后再读取。更专业的做法是用定时器或状态机,但初学者先用delay就够了。
3.5 进阶:用按键切换LED状态
上面的例子是「按下亮,松开灭」。但实际项目中,我们更常用「按一下切换状态」。比如按一次开灯,再按一次关灯。
这里需要引入一个「状态变量」来记住当前LED是亮还是灭:
bool ledState = false; // 记录LED状态
int lastButtonState = HIGH; // 记录上一次按键状态
void loop() {
int currentButtonState = digitalRead(buttonPin);
// 检测按键从松开到按下的变化(下降沿)
if (lastButtonState == HIGH && currentButtonState == LOW) {
// 切换LED状态
ledState = !ledState;
digitalWrite(ledPin, ledState);
Serial.println(ledState ? "LED已打开" : "LED已关闭");
delay(50); // 消抖
}
lastButtonState = currentButtonState;
}
bool 类型来存状态,比用 int 更省内存,代码也更清晰。另外,ledState = !ledState 这种取反写法,比 if...else 简洁多了。
3.6 总结一下今天的内容
- GPIO引脚分输入和输出,用
pinMode设置 digitalWrite输出高低电平,digitalRead读取外部电平- 按键需要内部上拉(
INPUT_PULLUP)或外部上拉电阻 - 机械按键有抖动,必须做消抖处理
- 用状态变量可以实现「按一次切换一次」的效果
说实话,GPIO是嵌入式开发最基础也最重要的部分。你想想看,所有传感器、执行器,最终都是通过GPIO和芯片交互的。把今天这个实验做熟,后面学I2C、SPI、PWM都会轻松很多。
下一章我们会讲PWM调光,到时候用呼吸灯效果来展示。嗯,那个更有意思。