3、ESP32 GPIO基础与数字信号

各位同学,今天我们来聊聊ESP32最基础、也是最核心的东西——GPIO。说白了,就是芯片上那些可以编程控制的引脚。你想想看,一个单片机再厉害,如果没法跟外界打交道,那它就是一块废铁。GPIO就是ESP32的「手脚」,用来感知世界、控制设备。

3.1 什么是GPIO?

GPIO全称是General Purpose Input Output,通用输入输出口。每个GPIO引脚可以配置成输入模式,也可以配置成输出模式。输入就是读外部信号,比如按键按下没按下;输出就是让引脚输出高电平或低电平,比如点亮一个LED。

ESP32有34个GPIO引脚,编号从0到39。但注意,不是所有引脚都能随便用。我刚开始用ESP32时,就踩过这个坑——有些引脚内部已经连到了Flash或PSRAM,你强行拉高拉低,系统直接死机。嗯,这里要特别提醒:GPIO6到GPIO11是内部Flash的SPI接口,千万别动它们

引脚范围 可用性 备注
GPIO0 - GPIO5 可用 注意GPIO0上电电平影响启动模式
GPIO6 - GPIO11 不可用 内部Flash占用,别碰
GPIO12 - GPIO19 可用 GPIO12上电电平影响Flash电压
GPIO21 - GPIO23 可用 常用I2C、SPI引脚
GPIO25 - GPIO27 可用 可作DAC输出
GPIO32 - GPIO39 可用 GPIO34-39只能输入,不能输出
⚠️ 避坑指南:我曾经在一个项目里,为了省事直接把按键接到了GPIO6上,结果程序死活跑不起来。查了半天才发现是Flash占用了。从那以后,我画PCB前一定会先查引脚功能表。

3.2 三个核心函数

在Arduino框架下,操作GPIO只需要记住三个函数。我个人习惯把它们称为「GPIO三板斧」。

3.2.1 pinMode() —— 设置引脚模式

这个函数告诉ESP32,某个引脚是当输入用还是当输出用。语法很简单:

pinMode(pin, mode);

mode参数有三个常用选项:

  • OUTPUT:输出模式,引脚可以输出高电平(3.3V)或低电平(0V)
  • INPUT:输入模式,读取引脚上的电平状态
  • INPUT_PULLUP:输入上拉模式,内部自动接一个上拉电阻

为什么要有INPUT_PULLUP?你想想看,如果按键一端接地,另一端接GPIO,当按键没按下时,引脚是悬空的,电平不确定。内部上拉电阻会把引脚默认拉到高电平,这样按键按下时读到低电平,逻辑就清晰了。

💡 我的经验:我建议你养成习惯,按键一律用INPUT_PULLUP模式。省去外部上拉电阻,电路更简洁,而且内部上拉电阻约50kΩ,功耗也低。

3.2.2 digitalWrite() —— 输出数字信号

这个函数让引脚输出高电平或低电平:

digitalWrite(pin, value);

value可以是:

  • HIGH:输出3.3V高电平
  • LOW:输出0V低电平

注意,ESP32的GPIO输出高电平是3.3V,不是5V。如果你要驱动5V的器件,需要加电平转换电路。我记得有次学员直接用ESP32去驱动一个5V继电器,结果继电器吸合不了,还差点烧了引脚。

3.2.3 digitalRead() —— 读取数字信号

这个函数读取引脚当前的电平状态:

int value = digitalRead(pin);

返回值是HIGH(1)或LOW(0)。

3.3 实战:按键控制LED

光说不练假把式。我们直接写一个完整的例子:按一下按键,LED亮;再按一下,LED灭。

硬件连接:

  • LED正极接GPIO2,负极串联220Ω电阻到GND
  • 按键一端接GPIO0,另一端接GND

代码实现:

// 引脚定义
const int ledPin = 2;      // LED连接到GPIO2
const int buttonPin = 0;   // 按键连接到GPIO0

int ledState = LOW;        // LED当前状态
int buttonState;           // 按键当前状态
int lastButtonState = HIGH; // 上一次按键状态

void setup() {
  pinMode(ledPin, OUTPUT);      // LED引脚设为输出
  pinMode(buttonPin, INPUT_PULLUP); // 按键引脚设为输入上拉
  Serial.begin(115200);
}

void loop() {
  buttonState = digitalRead(buttonPin);
  
  // 检测按键按下(电平从高变低)
  if (buttonState == LOW && lastButtonState == HIGH) {
    // 翻转LED状态
    ledState = !ledState;
    digitalWrite(ledPin, ledState);
    
    Serial.print("LED状态:");
    Serial.println(ledState == HIGH ? "亮" : "灭");
    
    // 简单消抖延时
    delay(50);
  }
  
  lastButtonState = buttonState;
}

🔑 关键点解析

  • 用了INPUT_PULLUP,按键没按下时读到HIGH,按下时读到LOW
  • 通过检测电平变化(从高到低)来判断按键动作,而不是直接读电平
  • 加了50ms延时做简单消抖。实际项目中我建议用定时器或状态机来做,更可靠

3.4 常见问题与调试技巧

做数字输入输出时,最容易出问题的就是电平匹配和引脚冲突。我分享几个实用技巧:

  1. 用串口打印调试:在代码里加Serial.println()打印引脚状态,比用万用表量方便多了
  2. 注意上电时序:有些外设上电瞬间会拉低引脚,导致ESP32误判。我一般会在setup()里加个500ms延时再初始化
  3. 不要超过额定电流:ESP32每个GPIO最大输出电流约40mA,整个芯片总电流不能超过200mA。驱动LED没问题,但别直接驱动电机
  4. 未使用的引脚:建议设为INPUT_PULLDOWN或输出低电平,避免悬空引入干扰
⚠️ 我曾经踩过的坑:有次做温湿度采集项目,传感器数据老是跳变。查了两天,最后发现是GPIO引脚没做上拉,悬空时感应到了周围的电磁干扰。从那以后,所有输入引脚我都习惯性加上拉或下拉。

3.5 小结

这一章我们学了GPIO的三个核心函数:pinMode()配置模式、digitalWrite()输出信号、digitalRead()读取信号。通过按键控制LED的例子,你应该能体会到数字输入输出的本质——就是读0和1,写0和1。

下一章我们会用这些知识去驱动一个更有趣的外设——温湿度传感器DHT11。到时候你会发现,数字信号的世界远不止开关灯这么简单。

好,今天的课就到这里。动手把代码烧进去试试,有问题随时交流。