ESP32中断介绍

lcysts
15
2025-10-19

中断系统无论是在电脑操作系统,还是在单片机中,都是非常核心的内容,要开发高性能、实时响应的应用,中断的学习是必不可少的。

什么是中断

举个生活中最简单的例子,你正在专心敲代码,突然手机响了,这是你需要暂时停止手上的工作,去接听电话,接完电话之后,再回来敲代码,整个过程就完成了一次中断处理。

中断处理的整体流程就如上图所示,一开始主程序运行得好好的,结果突发一个中断请求,CPU转而去执行中断任务,执行完之后再回来执行主任务,当然再去处理中断之前,CPU需要保留主程序的现场数据,以便回来时能够继续处理之前的任务。

中断的类型

  • 硬件中断——这些中断是为了响应外部事件而发生的。例如,GPIO 中断(按下按键时)或触摸中断(检测到触摸时)。

  • 软件中断——这些中断是为了响应软件指令而发生的。例如,简单的定时器中断或看门狗定时器中断(当定时器超时时)。

ESP32中断相关函数

attachInterrupt

在Arduino中,可以使用下面这个函数添加一个中断处理逻辑

void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode)

这个函数有三个参数:

  • pin:中断触发的中断号,虽然对于ESP32,这个参数可以直接写GPIO引脚编号,但是建议尽量调用digitalPinToInterrupt函数,将原始GPIO引脚转换成中断系统中的对应编号使用,比如digitalPinToInterrupt(23),ESP32能直接使用GPIO编号,是因为对于ESP32,digitalPinToInterrupt函数的返回值就是原样返回,但对于其他类型的单片机,逻辑就不一样了

  • intRoutine:中断处理函数,就是当触发中断时,需要执行的操作,注意这个操作应该尽可能轻量,不能将繁重的工作写在中断处理函数中,这样会极大降低单片机响应,最糟糕时可能导致单片机重启

  • mode:中断触发的模式,一共有以下几种模式可选

detachInterrupt

该函数用于解除某个中断号的中断逻辑,函数签名如下

void detachInterrupt(uint8_t pin)

函数只有一个参数:

  • pin:要取消中断程序的中断号,同样地,建议调用digitalPinToInterrupt函数来将GPIO编号转换成中断号

实战演练

在ESP32开发板上,有一个板载LED灯,这颗灯是通过2号引脚控制的,给2号引脚高电平时,小灯点亮,给低电平时,小灯熄灭。

随后,我们引入一个按键开关

这个开关有三个引脚,VCC接ESP32 3V3,GND接ESP32 GND, OUT接ESP32 GPIO引脚(比如23号引脚)

按钮按下时,23号引脚为低电平,按钮未按下时,23号引脚为高电平,我们将使用该开关来控制板载LED灯,实现如下效果:

每单击(按下后释放)按钮一次,板载LED灯的状态反转一次

首先分析按钮按下并释放这个过程,其OUT引脚的电平方波信号如下图所示

我们只需要在捕获到波形上升沿的时候,反转一次LED灯的状态即可,使用中断来实现,代码如下

#include <Arduino.h>

#define PIN 23     // 按钮开关
#define LED_PIN 2  // 板载LED灯引脚

void reverse_led() {
    // 先读出原来引脚的电平,前面加上感叹号取反,然后再输出到引脚,实现高低电平反转
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}

void setup() {
    pinMode(LED_PIN, OUTPUT);
    // 三个参数分别为:
    // 1. 中断监听引脚
    // 2. 发生中断后要执行的动作函数
    // 3. 中断触发信号(RISING:上升沿)
    attachInterrupt(digitalPinToInterrupt(PIN), reverse_led, RISING);
}

void loop() {}

烧录运行上述代码,基本功能能实现,但是你可能会发现,有些时候,按钮在一次按下过程中,LED灯的状态会有多次反转的现象。

注意

这其实是一个非常常见的按钮抖动现象,按钮按下过程中,会可能会发生触点多次接触/断开的情况,这就导致一次按下,出现多个上升沿的现象,继而导致LED灯状态多次反转

要解决上述问题也很简单,按钮消抖即可,消抖的逻辑为:在很短时间内(比如500ms)的多次上升沿直接去重忽略即可

#include <Arduino.h>

#define PIN 23     // 按钮开关
#define LED_PIN 2  // 板载LED灯引脚

unsigned long last_press_time = 0;

void reverse_led() {
    unsigned long current_time = millis();
    // 只有上一次按下的时间距离当前时间超过500ms,才认为是一次有效的操作
    if (current_time - last_press_time > 500) {
        // 先读出原来引脚的电平,前面加上感叹号取反,然后再输出到引脚,实现高低电平反转
        digitalWrite(LED_PIN, !digitalRead(LED_PIN));
        last_press_time = current_time;
    }
}

void setup() {
    pinMode(LED_PIN, OUTPUT);
    // 三个参数分别为:
    // 1. 中断监听引脚
    // 2. 发生中断后要执行的动作函数
    // 3. 中断触发信号(RISING:上升沿)
    attachInterrupt(digitalPinToInterrupt(PIN), reverse_led, RISING);
}

void loop() {}

程序中引入了一个新的函数(这个函数的作用是,返回单片机运行到现在所经历的毫秒数)

unsigned long millis()

动物装饰