|
[md]## 简介
此[esp32 arduino](https://www.dfrobot.com/product-1559.html)教程旨在解释如何使用E[SP32开发板](http://www.dfrobot.com.cn/goods-1359.html)和Arduino核心处理外部中断。
测试是在一个集成在ESP32开发板中的DFRobot的ESP-WROOM-32设备上进行的。
## 设置代码
首先我们将对该中断附加在全局变量上的引脚进行声明。请注意,根据您的ESP32开发板,[ESP32微控制器](http://www.dfrobot.com.cn/goods-1359.html)的引脚编号和开发板标注的引脚编号可能不匹配。在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函数](https://www.arduino.cc/en/Reference/NoInterrupts)和[Interrupts函数](https://www.arduino.cc/en/Reference/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 - 中断处理程序输出。
注:本文作者是Nuno Santos,他是一位和蔼可亲的电子和计算机工程师,住在葡萄牙里斯本 (Lisbon)。
他写了200多篇有关ESP32、ESP8266的有用的教程和项目。
查看更多ESP32/ESP8266教程和项目:
中文版教程 : [ESP32教程](http://mc.dfrobot.com.cn/thread-271930-1-1.html) 合集
英文版教程 :[ESP32 tutorial](https://www.dfrobot.com/blog-964.html)合集[/md] |
|