通常在项目中,您希望 ESP32 执行其正常程序,同时持续监视某种事件。一种广泛采用的解决方案是使用中断。
ESP32 为每个内核提供最多 32 个中断槽。每个中断都有一定的优先级,可以分为两种类型。
硬件中断——这些中断是为了响应外部事件而发生的。例如,GPIO 中断(按下按键时)或触摸中断(检测到触摸时)。
软件中断——这些中断是为了响应软件指令而发生的。例如,简单的定时器中断或看门狗定时器中断(当定时器超时时)。
在 ESP32 中,我们可以定义一个中断服务例程函数,当 GPIO 引脚改变其逻辑电平时将调用该函数。
ESP32 板上的所有 GPIO 引脚都可以配置为充当中断请求输入。

在 Arduino IDE 中,我们使用一个名为 的函数attachInterrupt()来逐个引脚设置中断。语法如下所示。
attachInterrupt(GPIOPin, ISR, Mode);
该函数接受三个参数:
GPIOPin – 将 GPIO 引脚设置为中断引脚,告诉 ESP32 要监控哪个引脚。
ISR – 是每次中断发生时将调用的函数的名称。
模式– 定义何时应触发中断。预定义了五个常量作为有效值:
| LOW | 每当引脚为低电平时触发中断 |
| HIGH | 每当引脚为高电平时触发中断 |
| CHANGE | 每当引脚改变值(从高到低或从低到高)时触发中断 |
| FALLING | 当引脚从高电平变为低电平时触发中断 |
| RISING | 当引脚从低电平变为高电平时触发中断 |
当您希望 ESP32 不再监控该引脚时,可以调用该detachInterrupt()函数。语法如下所示。
detachInterrupt(GPIOPin);
中断服务例程 (ISR) 是每次 GPIO 引脚上发生中断时都会调用的函数。
它的语法如下所示。
void IRAM_ATTR ISR() {
Statements;
}
ESP32 中的 ISR 是特殊类型的函数,它们具有大多数其他函数所没有的一些独特规则。
什么是 IRAM_ATTR?
当我们使用该属性标记一段代码时IRAM_ATTR,编译后的代码将放置在 ESP32 的内部 RAM (IRAM) 中。否则,代码将保存在 Flash 中。而且 ESP32 上的 Flash 比内部 RAM 慢得多。
如果我们要运行的代码是中断服务例程(ISR),我们通常希望尽快执行它。如果我们必须“等待”ISR 从闪存加载,那么事情可能会出现严重错误。
理论已经够多了!让我们看一个实际的例子。
让我们将一个按钮连接到 ESP32 上的 GPIO#18 (D18)。您不需要对该引脚进行任何上拉,因为我们将在内部将该引脚上拉。

将按钮连接到 ESP32 以实现 GPIO 中断
下面的草图演示了中断的使用以及编写中断服务程序的正确方法。
该程序监视 GPIO#18 (D18) 的下降沿。换句话说,它会寻找按下按钮时发生的从逻辑高电平到逻辑低电平的电压变化。当这种情况发生时,该函数isr被调用。此函数中的代码计算按钮被按下的次数。
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
};
Button button1 = {18, 0, false};
void IRAM_ATTR isr() {
button1.numberKeyPresses++;
button1.pressed = true;
}
void setup() {
Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);
}
void loop() {
if (button1.pressed) {
Serial.printf("Button has been pressed %u times\n", button1.numberKeyPresses);
button1.pressed = false;
}
}
上传草图后,按 ESP32 上的 EN 按钮并以波特率 115200 打开串行监视器。按下按钮后,您将获得以下输出。

在草图的开头,我们创建一个名为 的结构Button。该结构具有三个成员——引脚编号、按键次数和按下状态。仅供参考,结构是单个名称下不同类型(但逻辑上彼此相关)的变量的集合。
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
}
然后,我们创建 Button 结构的一个实例,并将引脚编号初始化为18,按键次数初始化为0,默认按下状态初始化为false。
Button button1 = {18, 0, false};
下面的代码是一个中断服务程序。前面提到,ESP32 中的 ISR 必须具有该IRAM_ATTR属性。
在 ISR 中,我们只需将 KeyPresses 计数器增加 1 并将按钮按下状态设置为 True。
void IRAM_ATTR isr() {
button1.numberKeyPresses += 1;
button1.pressed = true;
}
在代码的设置部分,我们首先初始化与 PC 的串行通信,然后启用 D18 GPIO 引脚的内部上拉。
isr接下来,我们告诉 ESP32 监视 D18 引脚,并在引脚从高电平变为低电平(即下降沿)时调用中断服务程序。
Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);
在代码的循环部分,我们简单地检查按钮是否被按下,然后打印到目前为止按键被按下的次数,并将按钮按下状态设置为 false,以便我们可以继续接收中断。
if (button1.pressed) {
Serial.printf("Button 1 has been pressed %u times\n", button1.numberKeyPresses);
button1.pressed = false;
}
中断的一个常见问题是,同一事件经常会多次触发中断。如果您查看上面示例的串行输出,您会注意到即使您只按一次按钮,计数器也会增加几次。

要找出发生这种情况的原因,您必须查看信号。如果您在按下按钮时监控信号分析仪上引脚的电压,您将得到如下信号:

您可能感觉立即发生了接触,但实际上按钮内的机械部件在进入特定状态之前会接触多次。这会导致触发多个中断。
这纯粹是一种被称为“开关弹跳”的机械现象,就像丢球一样——它会弹跳几次,然后最终落地。每一次弹跳都会引起一次中断,从而调动执行一次中断程序。所以就出现了按动一次触发了多次打印的情况。
如何消除开关弹跳的过程称为“去弹跳”。有两种方法可以实现这一目标。
这里重写了上面的草图,以演示如何以编程方式消除中断反跳。在此草图中,我们允许 ISR 在每次按下按钮时仅执行一次,而不是多次执行。
对草图的更改突出显示在绿色的。
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
};
Button button1 = {18, 0, false};
//建立一个变量用来保存上次调用中断处理程序的时间,如果这个时间小于250毫秒则不再确认按下按钮。
unsigned long button_time = 0;
unsigned long last_button_time = 0;
void IRAM_ATTR isr() {
button_time = millis();
if (button_time - last_button_time > 250)
{
button1.numberKeyPresses++;
button1.pressed = true;
last_button_time = button_time;
}
}
void setup() {
Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);
}
void loop() {
if (button1.pressed) {
Serial.printf("Button has been pressed %u times\n", button1.numberKeyPresses);
button1.pressed = false;
}
}
让我们再次查看按下按钮时的串行输出。请注意,每次按下按钮只会调用一次 ISR。

此修复之所以有效,是因为每次执行 ISR 时,都会将函数返回的当前时间millis()与上次调用 ISR 的时间进行比较。
如果在 250ms 之内,ESP32 会忽略中断并立即返回到正在执行的操作。如果没有,它会执行语句中的代码if,增加计数器并更新last_button_time变量,以便该函数有一个新值可以在将来触发时进行比较。
这个问题也可以在远程序的基础上,在loop模块中打印以前延迟10毫秒再做一次判断,看是否还是按下的状态,如果是则打印不是则不打印。
AMR是Autonomous Mobile Robot的缩写,即自主移动机器人,是集环境感知,动态决策规划,行为控制与执行等多功能于一体的综合系统。
本讲主要介绍VSCode Arduino开发环境的搭建,及与Arduino IDE开发环境的比较。
本文主要讲解WebServer库如何来处理表单请求。
WebServer是非常常用的一个功能,在设备上使用该功能可以直接通过浏览器访问和操作设备。
Arduino-ESP32与ESP-IDF的版本对应表。
Arduino-ESP32提供了多种文件系统解决方案,本文将深入解析SPIFFS、LittleFS和SD卡三种主流存储方案,帮助你做出最佳选择。
ESP32-P4-WIFI6-DEV-KIT是一款微雪(Waveshare)设计的基于 ESP32-P4 的多媒体开发板,并集成 ESP32-C6,支持 Wi-Fi 6 和 BLE 5 无线连接。它提供丰富的人机交互接口,包括 MIPI-CSI (集成图像信号处理器 ISP)、MIPI-DSI、SPI、I2S、I2C、LED PWM、MCPWM、RMT、ADC、UART 和 TWAI 等。
ESP-Hosted 解决方案提供了将 ESP 板用作 Wi-Fi 和 Bluetooth/BLE 连接的通信处理器的方法。
ESP-Hosted 提供了一种将ESP芯片和模组用作通信协处理器的解决方案,该解决方案为主机微处理器或微控制器提供无线连接,使主机能够与其他设备通信。简单来说为网卡方案。
Arduino+ESP32上使用TFT_eSPI库快速点亮这个屏幕,驱动芯片ST7789