查看: 307|回复: 1

[Arduino] ESP32 Arduino教程:外部中断

[复制链接]

简介

esp32 arduino教程旨在解释如何使用ESP32开发板Arduino核心处理外部中断。

测试是在一个集成在ESP32开发板中的DFRobot的ESP-WROOM-32设备上进行的。

设置代码

首先我们将对该中断附加在全局变量上的引脚进行声明。请注意,根据您的ESP32开发板,ESP32微控制器的引脚编号和开发板标注的引脚编号可能不匹配。在FireeBeetle esp32开发板中,以下使用的引脚(数字引脚25)与标记为IO25/D2的引脚相互匹配。

const byte interruptPin = 25;

此外我们还将对计数器进行声明,中断例程将使用该计数器与主循环函数进行通信,并发出中断已发生的信号。请注意,此变量需要声明为volatile(易失性),因为它将由ISR和主代码共享。否则,该变量可能会因编译器优化而将其删除。

volatile int interruptCounter = 0;

此外,我们将声明一个计数器,对自程序启动后全局究竟出现多少次中断进行追踪。因此,每次发生中断时,此计数器都会递增。

int numberOfInterrupts = 0;

最后,我们将声明一个portMUX_TYPE类型的变量,利用其对主代码和中断之间的同步进行处理。我们稍后会看到如何使用该变量。

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

转到设置功能,我们首先打开一个串行连接,以便能够输出我们程序的结果。

Serial.begin(115200);

Serial.println("Monitoring interrupts: ");

接下来,由于我们将使用外部引脚中断,我们需要将先前声明的引脚编号配置为输入引脚。为此,我们调用pinMode函数,将引脚编号和操作模式作为参数传递。

pinMode(interruptPin, INPUT_PULLUP);

为了在引脚无电气信号输入时获悉输入状态,我们使用INPUT_PULLUP模式。因此,当无信号输入时,它将处于VCC电压等级而非处于浮动状态,以免检测到任何不存在的外部中断。

接下来,我们通过调用attachInterrupt函数将中断附加到引脚。作为首个参数,我们将调用结果传递给digitalPinToInterrupt函数,该函数将使用的引脚编号转换为相应的内部中断编号。

接下来,我们传递中断处理函数,换而言之,当指定引脚上出现中断时,该函数将予以执行。我们将其称为handleInterruptand,稍后再指定其代码。

最后我们传递中断模式,它基本指定了在引脚输入信号中哪种类型变化触发了中断。我们将使用FALLING,这意味着当在引脚上检测到从VCC到GND的变化时,中断将会发生。

attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING);

最终的设置代码如下所示。

const byte interruptPin = 25;

volatile int interruptCounter = 0;

int numberOfInterrupts = 0;

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

void setup() {

  Serial.begin(115200);

  Serial.println("Monitoring interrupts: ");

  pinMode(interruptPin, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING);

}

主循环

现在我们将转到主循环。那里我们将简单检查中断计数器是否大于零。如果是,这意味着我们存在中断需要处理。

因此,如果出现中断,我们首先要注意递减该中断计数器,以发出中断已被检测到并将被处理的信号。

请注意,此计数器方法比使用标志更好,因为如果当主代码无法处理所有中断时,多个中断出现,我们将不会丢失任何事件。作为对比,如果我们使用一个标志,并且在没有主代码能够对其进行处理的情况下发生多个中断,那么标志值设置将在ISR中保持为真,并且主循环处理程序将仅解释为似乎仅发生了一次中断。

值得注意的是,其它重要事项还包括我们应该在写入与中断共享的变量时禁用中断。这样我们就可以确保主代码和ISR之间不存在任何对它的并发访问。

在Arduino环境中,我们通常使用NoInterrupts函数Interrupts函数禁用和重启中断。尽管如此,在撰写本文时,这些功能尚未在ESP32 Arduino核心中予以实现(https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Arduino.h#L82-L85 )。

因此,我们使用portENTER_CRITICAL和portEXIT_CRITICAL宏进行声明,在临界区内执行变量递减。这些调用都接收先前声明的portMUX_TYPE全局变量的地址作为输入。

if(interruptCounter>0){

      portENTER_CRITICAL(&mux);

      interruptCounter--;

      portEXIT_CRITICAL(&mux);

      //Handle the interrupt

}

在负责递减计数器之后,我们现在将递增全局计数器,该计数器保存自程序开始以来检测到的中断数。由于中断服务程序不会访问它,因此不需要在临界区内增加此变量。

随后,我们将打印一条消息,指示已检测到中断以及到目前为止究竟发生了多少次中断。注意由于ISR旨在尽快执行,因此绝不应在中断服务程序内,完成向串行端口的数据发送。如果这样做,您很可能会遇到各种运行时问题。

这样在我们的体系结构中,ISR只负责发出中断发生的主循环的简单操作,然后主循环处理其余事项。

您可以查看以下完整的主循环代码。

void loop() {

  if(interruptCounter>0){

      portENTER_CRITICAL(&mux);

      interruptCounter--;

      portEXIT_CRITICAL(&mux);

      numberOfInterrupts++;

      Serial.print("An interrupt has occurred. Total: ");

      Serial.println(numberOfInterrupts);

  }

}

中断处理函数

为了结束代码运行,我们将声明我们的中断处理函数。如前所述,它只会对向主循环发出中断信号的全局变量递增行为进行处理。

我们还将此操作封装到关键环节,通过调用portENTER_CRITICAL_ISR和portExit_CRITICAL_ISR宏对该环节进行声明。它们还接收portMUX_TYPE全局变量地址作为输入。

这是必需的,因为我们将要使用的变量也通过主循环进行更改,如前所述,我们需要防止并发访问问题。

void handleInterrupt() {

  portENTER_CRITICAL_ISR(&mux);

  interruptCounter++;

  portEXIT_CRITICAL_ISR(&mux);

}

中断处理函数的完整代码如下所示。

最终的代码

最终的源代码如下所示。您可以将其复制并粘贴到Arduino环境中进行测试。

const byte interruptPin = 25;

volatile int interruptCounter = 0;

int numberOfInterrupts = 0;

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

void setup() {

  Serial.begin(115200);

  Serial.println("Monitoring interrupts: ");

  pinMode(interruptPin, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING);

}

void handleInterrupt() {

  portENTER_CRITICAL_ISR(&mux);

  interruptCounter++;

  portEXIT_CRITICAL_ISR(&mux);

}

void loop() {

  if(interruptCounter>0){

      portENTER_CRITICAL(&mux);

      interruptCounter--;

      portEXIT_CRITICAL(&mux);

      numberOfInterrupts++;

      Serial.print("An interrupt has occurred. Total: ");

      Serial.println(numberOfInterrupts);

  }

}

测试代码

如需测试代码,只需将其上传到ESP32并打开Arduino IDE串行监视器即可。触发中断的最简单方法是使用电线连接,并断开中断连接到GND的数字引脚。

由于引脚被声明为INPUT_PULLUP,因此这将触发从VCC到GND的转换,并将检测到外部中断。请务必保持谨慎,以免将接地引脚连接到错误的GPIO,并造成整个电路板的损坏。

您应该获得类似图1的输出,该输出显示正在触发的中断和正在打印的全局计数器。

1.png

图1  - 中断处理程序输出。

注:本文作者是Nuno Santos,他是一位和蔼可亲的电子和计算机工程师,住在葡萄牙里斯本 (Lisbon)。
他写了200多篇有关ESP32、ESP8266的有用的教程和项目。

查看更多ESP32/ESP8266教程和项目:
中文版教程 : ESP32教程 合集
英文版教程 :ESP32 tutorial合集

gada888  版主

发表于 2019-6-3 17:35:34

不错,学习中
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
wifi气象站

硬件清单

btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2019 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail