ESP32 Arduino教程:外部中断-Arduino中文社区 - Powered by Discuz! Archiver

dfrobot 发表于 2019-6-18 16:58

ESP32 Arduino教程:外部中断

## 简介



此(https://www.dfrobot.com/product-1559.html)教程旨在解释如何使用E(http://www.dfrobot.com.cn/goods-1359.html)和Arduino核心处理外部中断。

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





## 设置代码



首先我们将对该中断附加在全局变量上的引脚进行声明。请注意,根据您的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环境中,我们通常使用(https://www.arduino.cc/en/Reference/NoInterrupts)和(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教程和项目:
中文版教程 : (http://mc.dfrobot.com.cn/thread-271930-1-1.html) 合集
英文版教程 :(https://www.dfrobot.com/blog-964.html)合集
页: [1]
查看完整版本: ESP32 Arduino教程:外部中断