ESP32 中的中断

通常在项目中,您希望 ESP32 执行其正常程序,同时持续监视某种事件。一种广泛采用的解决方案是使用中断

ESP32 中的中断

ESP32 为每个内核提供最多 32 个中断槽。每个中断都有一定的优先级,可以分为两种类型。

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

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

ESP32 GPIO 中断

在 ESP32 中,我们可以定义一个中断服务例程函数,当 GPIO 引脚改变其逻辑电平时将调用该函数。

ESP32 板上的所有 GPIO 引脚都可以配置为充当中断请求输入。

esp32中断引脚

将中断附加到 GPIO 引脚

在 Arduino IDE 中,我们使用一个名为 的函数attachInterrupt()来逐个引脚设置中断。语法如下所示。

attachInterrupt(GPIOPin, ISR, Mode);

该函数接受三个参数:

GPIOPin – 将 GPIO 引脚设置为中断引脚,告诉 ESP32 要监控哪个引脚。

ISR – 是每次中断发生时将调用的函数的名称。

模式– 定义何时应触发中断。预定义了五个常量作为有效值:

LOW每当引脚为低电平时触发中断
HIGH每当引脚为高电平时触发中断
CHANGE每当引脚改变值(从高到低或从低到高)时触发中断
FALLING当引脚从高电平变为低电平时触发中断
RISING当引脚从低电平变为高电平时触发中断

从 GPIO 引脚分离中断

当您希望 ESP32 不再监控该引脚时,可以调用该detachInterrupt()函数。语法如下所示。

detachInterrupt(GPIOPin);

中断服务程序

中断服务例程 (ISR) 是每次 GPIO 引脚上发生中断时都会调用的函数。

它的语法如下所示。

void IRAM_ATTR ISR() {
    Statements;
}

ESP32 中的 ISR 是特殊类型的函数,它们具有大多数其他函数所没有的一些独特规则。

  1. ISR 不能有任何参数,并且它们不应该返回任何内容。
  2. ISR 应尽可能短且快,因为它们会阻止正常的程序执行。
  3. IRAM_ATTR根据ESP32 文档,它们应该具有该属性。

什么是 IRAM_ATTR?

当我们使用该属性标记一段代码时IRAM_ATTR,编译后的代码将放置在 ESP32 的内部 RAM (IRAM) 中。否则,代码将保存在 Flash 中。而且 ESP32 上的 Flash 比内部 RAM 慢得多。

如果我们要运行的代码是中断服务例程(ISR),我们通常希望尽快执行它。如果我们必须“等待”ISR 从闪存加载,那么事情可能会出现严重错误。

硬件连接

理论已经够多了!让我们看一个实际的例子。

让我们将一个按钮连接到 ESP32 上的 GPIO#18 (D18)。您不需要对该引脚进行任何上拉,因为我们将在内部将该引脚上拉。

将按钮连接到 ESP32 以实现 GPIO 中断

将按钮连接到 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 打开串行监视器。按下按钮后,您将获得以下输出。

esp32 GPIO 串行监视器上的中断输出

代码说明

在草图的开头,我们创建一个名为 的结构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;
}

管理跳动

中断的一个常见问题是,同一事件经常会多次触发中断。如果您查看上面示例的串行输出,您会注意到即使您只按一次按钮,计数器也会增加几次。

esp32 GPIO中断反弹问题

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

开关弹跳信号

您可能感觉立即发生了接触,但实际上按钮内的机械部件在进入特定状态之前会接触多次。这会导致触发多个中断。

这纯粹是一种被称为“开关弹跳”的机械现象,就像丢球一样——它会弹跳几次,然后最终落地。每一次弹跳都会引起一次中断,从而调动执行一次中断程序。所以就出现了按动一次触发了多次打印的情况。

如何消除开关弹跳的过程称为“去弹跳”。有两种方法可以实现这一目标。

  • 通过硬件:通过添加适当的RC滤波器来平滑过渡。
  • 通过软件:在触发第一个中断后的短时间内暂时忽略进一步的中断。

示例代码:消除中断抖动

这里重写了上面的草图,以演示如何以编程方式消除中断反跳。在此草图中,我们允许 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。

esp32 GPIO 中断去抖

代码说明:

此修复之所以有效,是因为每次执行 ISR 时,都会将函数返回的当前时间millis()与上次调用 ISR 的时间进行比较。

如果在 250ms 之内,ESP32 会忽略中断并立即返回到正在执行的操作。如果没有,它会执行语句中的代码if,增加计数器并更新last_button_time变量,以便该函数有一个新值可以在将来触发时进行比较。

这个问题也可以在远程序的基础上,在loop模块中打印以前延迟10毫秒再做一次判断,看是否还是按下的状态,如果是则打印不是则不打印。

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值