ESP32 GPIO General course
来自Waveshare Wiki
预备知识
GPIO 简介
GPIO(General-Purpose Input/Output Port,通用输入/输出接口)是一种可编程接口,广泛应用于各种电子设备中。GPIO 在设置为输入模式时,能够感知外界信号,用于接收传感器或其他外部模块的输入;而在设置为输出模式时,则能够控制外部设备,如点亮LED灯、驱动蜂鸣器或控制继电器等。
在开发过程中,GPIO通常用于连接外部的简单模块,如LED、按键、蜂鸣器等。通过设置GPIO的输入或输出状态,我们可以轻松读取外部信号或控制外部设备。特别地,当GPIO管脚用于作为内部外设的信号引脚时,这被称为引脚复用,常见于某些协议的实现,例如UART、I2C等通信接口。
通过灵活配置GPIO,可以实现多种功能,且由于其简单易用,GPIO是嵌入式系统中不可或缺的基础硬件资源之一。
GPIO 工作模式
模式名称 | 说明 |
---|---|
INPUT | 普通输入模式 |
OUTPUT | 输出模式,可以读取引脚状态 |
PULLUP | 启用内部上拉电阻 |
INPUT_PULLUP | 输入模式,启用内部上拉电阻 |
PULLDOWN | 启用内部下拉电阻 |
INPUT_PULLDOWN | 输入模式,启用内部下拉电阻 |
OPEN_DRAIN | 开漏输出模式,需要外部上拉电阻 |
OUTPUT_OPEN_DRAIN | 开漏输出模式 |
ANALOG | 模拟输入/输出模式 |
GPIO 中断
ESP32-S3 具有 99 个外部中断源,但 CPU0 和 CPU1 各只能处理 32 个中断。因此,ESP32-S3 采用 中断矩阵 来映射外部中断到 CPU,以便在外设产生中断信号后,及时通知相应的 CPU 处理。
- CPU 中断分配
- 每个 CPU 共有 32 个中断号 (0~31)
- 26 个外部中断(外设引发)
- 6 个内部中断(CPU 自身触发)
- 每个 CPU 共有 32 个中断号 (0~31)
- 外部中断类型
- 电平触发中断:高电平触发,电平需保持至 CPU 响应
- 边沿触发中断:上升沿触发,一旦产生,CPU 即可响应
- NMI(不可屏蔽中断):系统发生致命错误时触发
- 内部中断类型
- 定时器中断:由内部定时器触发,可用于周期性任务
- 软件中断:软件写入特殊寄存器时触发
- 解析中断:用于性能监测与分析
- ESP32-S3 外部中断特点
- 所有 GPIO 均可配置为外部中断引脚
- 与 Arduino 控制器不同,ESP32-S3 几乎所有引脚都支持外部中断
- Arduino 设备上,只有部分引脚支持外部中断,需先查找引脚编号再配置
- GPIO 中断触发模式
模式名称 | 说明 |
---|---|
DISABLED | 禁用中断,表示此引脚不响应任何中断 |
RISING | 上升沿中断,当GPIO引脚的电平从低变高时触发中断 |
FALLING | 下降沿中断,当GPIO引脚的电平从高变低时触发中断 |
CHANGE | 电平变化中断,当GPIO引脚的电平发生变化时触发中断 |
ONLOW | 低电平触发中断,通常在GPIO引脚的电平为低时触发中断 |
ONHIGH | 高电平触发中断,通常在GPIO引脚的电平为高时触发中断 |
ONLOW_WE | 低电平触发中断并伴随外部事件(WE),表示某种外部条件触发了低电平中断 |
ONHIGH_WE | 高电平触发中断并伴随外部事件(WE),表示某种外部条件触发了高电平中断 |
GPIO 函数
/****************************************************************************** * 函数名称:pinMode * 描 述:设置引脚的工作模式 * 输 入:①.uint8_t pin:需配置的引脚编号 * ②.uint8_t mode:为GPIO指定工作模式 * 输 出:无 * 返 回 值:无 *****************************************************************************/ void pinMode(uint8_t pin, uint8_t mode);
/****************************************************************************** * 函数名称:digitalWrite * 描 述:设置指定引脚的输出电平。根据传入的电平值,将指定引脚设置为高电平或低电平 * 输 入:①.uint8_t pin:需配置的引脚编号 * ②.uint8_t value:设置的电平值,常见值包括: 0:低电平 1:高电平 * 输 出:无 * 返 回 值:无 *****************************************************************************/ void digitalWrite(uint8_t pin, uint8_t value);
/****************************************************************************** * 函数名称:digitalRead * 描 述:读取指定引脚的电平状态。根据引脚的输入状态,返回引脚的电平(高或低) * 输 入:uint8_t pin:要读取状态的引脚编号 * 输 出:返回引脚的电平状态(int类型) * 返 回 值:int:引脚的电平状态,0:低电平 1:高电平 *****************************************************************************/ int digitalRead(uint8_t pin);
/****************************************************************************** * 函数名称:attachInterrupt * 描 述:为指定引脚设置中断服务程序(ISR)。当引脚的状态变化时,触发指定的回调函数 * 输 入:①.uint8_t pin:要设置中断的引脚编号 * ②.voidFuncPtr handler:当中断触发时调用的回调函数 * ③.int mode:中断触发模式,取值可以为:LOW(低电平触发)、CHANGE(电平变化触发)、RISING(上升沿触发)、FALLING(下降沿触发) * 输 出:无 * 返 回 值:无 *****************************************************************************/ void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode);;
/****************************************************************************** * 函数名称:attachInterruptArg * 描 述:为指定引脚设置中断服务程序(ISR)。当引脚的状态变化时,触发指定的回调函数。 * 允许向回调函数传递一个附加的参数,`arg` 是一个指向中断参数的指针。 * 输 入:①.uint8_t pin:要设置中断的引脚编号 * ②.voidFuncPtrArg handler:当中断触发时调用的回调函数,回调函数需要接受一个指针参数 * ③.void* arg:指向中断参数的指针,该参数会传递给回调函数 * ④.int mode:中断触发模式,取值可以为:LOW(低电平触发)、CHANGE(电平变化触发)、RISING(上升沿触发)、FALLING(下降沿触发) * 输 出:无 * 返 回 值:无 * 备 注:与 'attachInterrupt' 相比,'attachInterruptArg'可以传递一个附加的参数 'arg',该参数会被传递到回调函数中。 * 'handler' 回调函数的定义需要能够接收一个 'void*' 类型的参数。 * voidFuncPtrArg 是一个带有参数的回调函数类型: * void myInterruptHandler(void* arg) { * // 处理中断时使用 arg 参数 * } *****************************************************************************/ attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode);
/****************************************************************************** * 函数名称:detachInterrupt * 描 述:取消指定引脚的中断服务程序(ISR)。停止引脚的中断触发 * 输 入:uint8_t pin:要取消中断的引脚编号 * 输 出:无 * 返 回 值:无 *****************************************************************************/ void detachInterrupt(uint8_t pin);
- 所用函数所在文件夹路径,以用户名waveshare为例
C:\Users\wavehsare\AppData\Local\Arduino15\Packages\esp32\hardware\esp32\3.1.1\cores\esp32\esp32-hal-gpio.c
示例程序
01_GPIO
程序说明
- 示例程序:01_GPIO
- 本示例实现了LED以1000毫秒的频率闪烁,并将LED的高/低状态读取并打印到串行监视器
硬件连接
使用 ESP32-S3-Touch-LCD-7 开发板
- 无需修改程序,配置参数后烧录
- 将板子接入电脑,将Sensor AD接口与LED灯连接
ESP32-S3 LED 3V3 VCC GND GND AD LED
- ESP32-S3-Touch-LCD-7 原理图部分
使用其他开发板
- 可修改引脚为led对应引脚以观察现象
代码分析
- 这行代码定义了常量LED,将数字引脚6指定为LED的控制引脚。使用常量使得代码更清晰,后续修改引脚编号时只需修改这一行
#define LED 6
- setup()函数是初始化部分,只会在程序开始时运行一次。这里设置了LED引脚为输出模式(pinMode(LED, OUTPUT)),并初始化串口通信,用于后续打印信息
void setup()
- loop()是主程序部分,它会无限循环运行。在这里,我们控制LED的开关和每次LED状态变化时通过串口监视器输出状态信息
void loop()
- 控制LED闪烁
digitalWrite(LED, HIGH):将LED引脚设置为高电平,LED点亮 delay(1000):等待1秒(1000毫秒) digitalWrite(LED, LOW):将LED引脚设置为低电平,LED熄灭
- 读取LED状态并输出
使用digitalRead(LED)读取LED引脚的电平状态,并根据状态值打印不同的消息到串口监视器
运行效果
- LED灯闪烁(演示视频使用的LED灯珠,3V3---LED+;AD---LED-;GND悬空)
- 串口监视器打印信息
02_KEY
程序说明
- 示例程序:02_KEY
- 本示例实现了当按钮被按下时,LED 的状态会在打开和关闭之间切换
硬件连接
使用 ESP32-S3-Touch-LCD-7 开发板
- 无需修改程序,配置参数后烧录
- 将板子接入电脑,将Sensor AD接口与LED灯连接
ESP32-S3 LED 3V3 VCC GND GND AD LED
- ESP32-S3-Touch-LCD-7 原理图部分
使用其他开发板
- 修改 LED 引脚为对应所使用的 LED 引脚
- 修改 BUTTON 引脚为对应所使用的按钮引脚
代码分析
- 常量定义与变量声明,后续修改引脚编号时只需修改宏定义这两行
#define LED 6 // 定义 LED 的引脚为 6 #define BUTTON 0 // 定义按钮的引脚为 0 uint8_t stateLED = 0; // LED 初始状态为 0(关闭) unsigned long lastButtonPress = 0; // 记录按钮最后一次按下的时间,用于防抖 unsigned long debounceDelay = 50; // 防抖延时,单位为毫秒,设置为 50 毫秒。按钮在被按下时,由于物理特性可能会产生抖动,通过这个延时来防止抖动导致的多次触发
- setup()函数
void setup() { pinMode(LED, OUTPUT); // 将 LED 引脚配置为输出模式,这样可以通过 digitalWrite() 控制其高低电平,进而控制 LED 的亮灭 pinMode(BUTTON, INPUT_PULLUP); // 将 BUTTON 引脚配置为输入模式,并启用内部上拉电阻。按钮按下时,电平变为 LOW,松开时为 HIGH。这意味着按钮按下时,BUTTON 引脚连接到地 }
- loop() 函数
void loop() { int buttonState = digitalRead(BUTTON); // 读取按钮的当前状态,返回 LOW 如果按钮被按下,HIGH 如果按钮松开
- 按钮按下且防抖延时:判断按钮是否按下,并且判断距离上次按下的时间是否已经超过了 debounceDelay(50 毫秒)。如果是,这意味着按钮按下操作已经“稳定”,可以触发操作
// 检查按钮是否按下,并且是否经过防抖延时 if (buttonState == LOW && (millis() - lastButtonPress) > debounceDelay) { lastButtonPress = millis(); // millis() 返回程序启动后经过的毫秒数,用来判断防抖延时 stateLED = stateLED ^ 1; // 通过异或运算符 ^ 切换 stateLED 的状态。如果 stateLED 是 0(关闭),就变成 1(打开);如果是 1(打开),就变成 0(关闭) digitalWrite(LED, stateLED); // 根据切换后的状态更新 LED 的亮灭
- 等待按钮松开:while (digitalRead(BUTTON) == LOW) 循环会一直执行,直到按钮松开(即 BUTTON 引脚的电平变为 HIGH)。这保证了每次按下按钮后,LED 只会切换一次
// 等待按钮松开,并加上轻微延时,避免 CPU 占用过高 while (digitalRead(BUTTON) == LOW) { delay(5); // 每 5 毫秒检查一次按钮状态 } } }
运行效果
- 按键控制LED灯亮灭(演示视频使用的LED灯珠,3V3---LED+;AD---LED-;GND悬空)
03_GPIOInterrupt
程序说明
- 示例程序:03_GPIOInterrupt
- 本示例使用中断控制一个 LED 灯的开关。通过按钮按下触发中断,每次按钮按下时,LED 状态在开和关之间切换
硬件连接
使用 ESP32-S3-Touch-LCD-7 开发板
- 无需修改程序,配置参数后烧录
- 将板子接入电脑,将Sensor AD接口与LED灯连接
ESP32-S3 LED 3V3 VCC GND GND AD LED
- ESP32-S3-Touch-LCD-7 原理图部分
使用其他开发板
- 修改 LED 引脚为对应所使用的 LED 引脚
- 修改 BUTTON 引脚为对应所使用的按钮引脚
代码分析
- 常量定义与变量声明,后续修改引脚编号时只需修改宏定义这两行
#define LED 6 // 定义 LED 的引脚为 6 #define BUTTON 0 // 定义按钮的引脚为 0 volatile uint8_t stateLED = 0; // LED 状态(0 为关闭,1 为打开) volatile unsigned long lastButtonPress = 0; // 记录上次按钮按下的时间 const unsigned long debounceDelay = 50; // 防抖延时,单位毫秒 volatile bool buttonPressed = false; // 按钮按下标志位
- 中断服务函数(ISR):isrButton 是中断服务函数,当按钮按下时触发;使用 millis() 获取当前时间,并与 lastButtonPress 进行比较,确保按钮按下事件的时间间隔大于防抖动延时(50ms);如果防抖动判断通过,就更新 lastButtonPress 为当前时间,并将 buttonPressed 设置为 true,标志按钮被按下
void IRAM_ATTR isrButton() { unsigned long currentMillis = millis(); // 获取当前时间 // 防抖动判断 if (currentMillis - lastButtonPress > debounceDelay) { lastButtonPress = currentMillis; // 更新上次按钮按下时间 buttonPressed = true; // 设置按钮按下标志 } }
- setup() 函数
void setup() { pinMode(LED, OUTPUT); // 设置 LED 引脚为输出模式 pinMode(BUTTON, INPUT_PULLUP); // 设置按钮引脚为输入模式,并启用上拉电阻 attachInterrupt(BUTTON, isrButton, FALLING); // 将按钮引脚的状态变化(按下,FALLING)与中断服务函数 isrButton() 关联。当按钮按下时会触发 isrButton() }
- loop() 函数:当 buttonPressed 为 true 时,切换 LED 状态,并等待按钮松开
void loop() { if (buttonPressed) { buttonPressed = false; // 重置按钮按下标志 stateLED = stateLED ^ 1; // 切换 LED 状态(0 -> 1 或 1 -> 0) digitalWrite(LED, stateLED); // 根据切换后的状态更新 LED // 等待按钮松开,并加上轻微延时,避免 CPU 占用过高 unsigned long startMillis = millis(); while (digitalRead(BUTTON) == LOW) { // 非阻塞检查按钮状态 if (millis() - startMillis > debounceDelay) { break; // 如果超过防抖动延时,则跳出循环,继续处理其他逻辑 } } } }
运行效果
- 使用中断通过按键控制 LED 灯的开关(演示视频使用的LED灯珠,3V3---LED+;AD---LED-;GND悬空)