2、GPIO基础与数字传感器:数字输入输出原理,按键消抖,LED控制,人体红外传感器(PIR)实战
各位同学,欢迎来到第二章。上一章我们聊了ESP32的整体架构,今天咱们来点实在的——直接上手操作GPIO。
GPIO,全称是General Purpose Input Output,通用输入输出口。说白了,它就是芯片和外界打交道的“手脚”。你想让LED亮?GPIO输出高电平。你想知道按键按没按?GPIO读输入电平。就这么简单。
但简单归简单,坑也不少。我刚开始做项目时,就因为在GPIO上栽过跟头,导致整块板子重启。嗯,今天咱们就把这些坑一个个填平。
2.1 数字输入输出原理
先讲原理。ESP32的GPIO引脚,内部其实是一个数字逻辑门。输出模式下,你写1,引脚就是3.3V;写0,引脚就是0V。输入模式下,你读引脚,得到的就是外部电平——高电平是1,低电平是0。
这里有个关键点:ESP32的GPIO容忍电压是3.3V。千万别直接接5V,否则芯片会烧。我见过有人把5V传感器直接怼到GPIO上,结果冒烟了。切记!
另外,有些GPIO引脚有特殊功能。比如GPIO6-11默认连接了Flash,你如果把它们当普通IO用,程序会跑飞。我踩过这个坑,当时调试了一整天,最后发现是引脚冲突。
| 引脚 | 特殊功能 | 建议 |
|---|---|---|
| GPIO6-11 | 连接Flash | 不要用作普通IO |
| GPIO0 | 下载模式选择 | 上拉或悬空,避免低电平 |
| GPIO2 | 下载时上拉 | 注意外部电路影响 |
2.2 按键消抖——一个被忽视的细节
按键,看起来最简单。按下,读0;松开,读1。对吧?
但实际不是这样。机械按键在按下和松开的瞬间,会产生一连串的抖动信号。你想想看,一个按键按下去,实际上会在几毫秒内产生几十次电平跳变。如果你直接读,程序会认为你按了几十次。
为什么会这样?因为金属触点接触时会有弹性反弹。这个现象叫“抖动”。
消抖的方法有两种:硬件消抖和软件消抖。
硬件消抖:在按键两端并联一个电容(通常0.1uF),利用电容的充放电特性滤除抖动。优点是简单,缺点是增加元件成本。
软件消抖:检测到电平变化后,延时10-20ms再读一次。如果电平稳定,才认为是有效按键。我个人习惯用软件消抖,因为灵活,而且不增加硬件成本。
核心要点:软件消抖的延时时间取决于按键的机械特性。一般10-20ms足够,但劣质按键可能需要50ms。我曾经在一个项目中用了廉价按键,抖动长达80ms,最后不得不调整延时参数。
来看代码:
// 软件消抖示例
#define BUTTON_PIN 0
#define DEBOUNCE_DELAY 20 // 毫秒
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(115200);
}
void loop() {
int reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == LOW) {
Serial.println("按键按下!");
}
}
}
lastButtonState = reading;
}
这段代码的核心逻辑是:只有电平稳定超过20ms,才认为按键状态发生了变化。你想想看,如果抖动只有5ms,它根本不会触发状态更新。
2.3 LED控制——从闪烁到呼吸
LED控制是GPIO输出的经典案例。最简单的就是点亮和熄灭:
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // 点亮
digitalWrite(LED_PIN, LOW); // 熄灭
但如果你想让LED有呼吸效果,就需要PWM(脉冲宽度调制)。ESP32的LEDC外设可以生成PWM信号,频率和占空比都可调。
我建议你使用LEDC库,它比analogWrite更灵活:
// LEDC呼吸灯示例
#define LED_PIN 2
#define LEDC_CHANNEL 0
#define LEDC_TIMER 8 // 8位分辨率,0-255
#define LEDC_FREQ 5000 // 5KHz
void setup() {
ledcSetup(LEDC_CHANNEL, LEDC_FREQ, LEDC_TIMER);
ledcAttachPin(LED_PIN, LEDC_CHANNEL);
}
void loop() {
// 渐亮
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(LEDC_CHANNEL, dutyCycle);
delay(10);
}
// 渐暗
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(LEDC_CHANNEL, dutyCycle);
delay(10);
}
}
小技巧:LEDC的通道数量有限(ESP32有16个通道),但可以复用。如果你需要控制多个LED,记得合理分配通道。另外,PWM频率不要低于1KHz,否则人眼能感觉到闪烁。
2.4 人体红外传感器(PIR)实战
PIR传感器,全称Passive Infrared Sensor,被动红外传感器。它检测人体发出的红外辐射,当有人经过时,输出高电平。
常用的型号是HC-SR501。它有三个引脚:VCC(5V)、GND、OUT(数字输出)。注意,它的输出是3.3V,可以直接接ESP32的GPIO。
但有个坑:HC-SR501上电后需要30-60秒的稳定时间。在这段时间内,它会频繁触发。我曾经以为传感器坏了,换了三个才发现是稳定时间的问题。
接线很简单:
- VCC → ESP32 5V(注意不是3.3V)
- GND → GND
- OUT → GPIO4(任意数字引脚)
代码实现:
#define PIR_PIN 4
void setup() {
pinMode(PIR_PIN, INPUT);
Serial.begin(115200);
Serial.println("PIR传感器初始化中,请等待30秒...");
delay(30000); // 等待传感器稳定
Serial.println("初始化完成");
}
void loop() {
int pirState = digitalRead(PIR_PIN);
if (pirState == HIGH) {
Serial.println("检测到人体移动!");
// 这里可以触发LED亮、蜂鸣器响等动作
delay(1000); // 避免重复触发
} else {
// 无人状态
}
}
实战经验:PIR传感器的检测距离和灵敏度可以通过背后的两个电位器调节。顺时针旋转,检测距离变远(最远约7米),灵敏度变高。但注意,过高的灵敏度会导致误触发,比如宠物经过也会触发。我在智能家居项目中,通常把灵敏度调到中等,然后配合延时电路使用。
另外,PIR传感器有个特性:检测到人体后,输出会保持高电平一段时间(默认约2-3秒)。这个时间也可以通过电位器调节。如果你需要更长的保持时间,可以在代码中自己实现延时逻辑。
2.5 综合实战:智能感应灯
好了,我们把今天学的知识串起来,做一个智能感应灯:当PIR检测到人时,LED呼吸点亮;人离开后,LED缓慢熄灭。
#define PIR_PIN 4
#define LED_PIN 2
#define LEDC_CHANNEL 0
#define LEDC_TIMER 8
#define LEDC_FREQ 5000
int pirState = LOW;
unsigned long lastMotionTime = 0;
const unsigned long timeout = 10000; // 10秒无人后熄灭
void setup() {
pinMode(PIR_PIN, INPUT);
ledcSetup(LEDC_CHANNEL, LEDC_FREQ, LEDC_TIMER);
ledcAttachPin(LED_PIN, LEDC_CHANNEL);
Serial.begin(115200);
delay(30000); // PIR稳定时间
}
void loop() {
pirState = digitalRead(PIR_PIN);
if (pirState == HIGH) {
lastMotionTime = millis();
// 呼吸点亮
for (int i = 0; i <= 255; i++) {
ledcWrite(LEDC_CHANNEL, i);
delay(5);
}
}
// 如果超过10秒无人,缓慢熄灭
if (millis() - lastMotionTime > timeout) {
for (int i = 255; i >= 0; i--) {
ledcWrite(LEDC_CHANNEL, i);
delay(10);
}
}
}
这个项目虽然简单,但包含了GPIO输入、输出、PWM、延时控制等核心知识点。你可以在家里试试,把它做成一个走廊感应灯。
嗯,今天的内容就到这里。下一章我们会讲ADC和模拟传感器,到时候你会学到如何读取温度、光照等模拟信号。记得把今天的代码跑一遍,有问题随时问我。
课后练习:
- 修改按键消抖代码,实现长按和短按的区分
- 用两个LED实现红绿灯效果,要求使用PWM控制亮度
- 将PIR传感器与按键结合:按键按下后,感应灯进入常亮模式;再次按下恢复自动模式
公众号:蓝海资料掘金营,微信deep3321