第九章 EXTI实验
在前面两章的学习中,我们掌握了ESP32-S3的IO口最基本的操作。本章我们将介绍如何把ESP32-S3的IO口作为外部中断输入来使用,在本章中,我们将以中断的方式,实现boot按键控制板载的LED灯状态翻转。 本章分为如下几个小节: 9.1 外部中断介绍 9.2 硬件设计 9.3 软件设计 9.4 下载验证
9.1 外部中断介绍 很多时候,我们程序采集一个传感器的数据,采集后就要进行分析判断,若符合某个条件就会做出处理。为了随时根据传感器的变化做出反应,所以程序需要一直重复这个过程。这种方式称为轮询,这种方式是最简单的。 但轮询有时候并不能很好完成一些实际场景的应用,比如我某个时刻按下按键,但这时候程序执行的是采集传感器数据的过程,这就意味着没有检测到按键按下的动作,此时该系统就成了无法正常响应的系统了。通过对该按键配置外部中断功能,这时候就能很好解决上述问题。 9.1.1 中断程序 外部中断是由外部设备发起请求的中断。每个中断对应一个中断程序,中断可以看作一段独立于主程序之外的程序,也称为中断回调函数。当中断被触发时,控制器会暂停当前正在运行的主程序,而跳转去运行中断程序。当中断程序运行完毕,则返回到先前主程序暂停的位置,继续运行主程序,如此便可达到实时响应处理事件的效果。中断程序运行示意图如下图所示。 图9.1.1.1 中断程序执行示意图 9.1.2 ESP32-S3的中断介绍 便于大家了解ESP32-S3芯片的中断知识,这里简单介绍一下。 ESP32-S3有99个外部中断源,但是CPU0或CPU1只能够处理32个中断。ESP32-S3将外部中断映射到CPU0或CPU1中断就需要用到中断矩阵。中断映射的过程如下图所示。 图9.1.2.1 ESP32-S3芯片中断源到CPU中断过程图 ESP32-S3中断矩阵会将任一外部中断源单独分配到双核CPU的任一外部中断上,以便在外设中断信号产生后,及时通知CPU0或CPU1进行处理。 每个CPU都有32个中断号(0 ~ 31),其中包括26个外部中断,6个内部中断。 外部中断为外部中断源引发的中断,包括下面三种类型: 1) 电平触发类型中断:高电平触发,要求保持中断的电平状态直到CPU响应 2) 边沿触发类型中断:上升沿触发,此中断一旦产生,CPU即可响应 3) NMI中断:不可屏蔽中断,产生该中断时,表示系统发生了致命错误 内部中断为CPU内部自己产生的中断,包括以下三种类型: 1) 定时器中断:由内部定时器触发,可用于产生周期性的中断 2) 软件中断:软件写特殊寄存器时将触发此中断 3) 解析中断:用于性能监测和分析 ESP32-S3的外部中断是很强大的,每个引脚都可设置成外部中断触发引脚。但在大部分的Arduino控制器上,并非所有引脚都有中断功能。只有少数带外部中断功能的引脚上,Arduino控制器才能捕获到该中断信号并做出反应。在这过程中,难免需要通过中断引脚找中断的编号。 9.1.3 中断触发模式 ESP32-S3的中断触发模式有5种,即前面所提及的电平触发以及边沿触发,如下表所示。 中断触发模式 | 说明 | 示意图 | | | | | | | | 电平变化触发,即由高电平变低电平或由低电平变高电平时触发 | | | | | | | |
表9.1.3.1 ESP32-S3中断模式说明表 9.1.4 中断触发函数介绍 本小节介绍到的函数可在以下文件中找到: Arduino15\packages\esp32\hardware\esp32\2.0.11\cores\esp32\esp32-hal-gpio.c Arduino-esp32库提供了两个中断函数,一个用于对中断引脚进行初始化设置,另一个是关闭外部中断。 attachInterrupt函数,该函数功能是指定中断引脚,并对中断引脚进行初始化设置。 void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode); 参数pin为要设置中断触发输入的引脚,ESP32-S3所有引脚均可以配置为外部中断引脚。 参数handler为中断回调函数,当引脚中断触发时,会终止当前运行的程序,转而执行该程序。 参数mode为5种中断触发模式,如9.1.3.1表所示。 注意:中断回调函数不能有参数,且没有返回值。 假如不需要监测某个引脚的信号变化,可通过detachInterrupt函数关闭外部中断。 void detachInterrupt(uint8_t pin); 其中参数pin为已经设置中断触发输入的引脚。在本例程中没有用到该函数。 另外,很多时候,我们会见到attachInterrupt函数的第一参数会使用digitalPinToInterrupt(pin)函数,这里简单解释一下为什么? 其实前面也有所提及,在一些Arduino开发板中比如Arduino Uno、Leonardo,只有2和3引脚有外部中断功能,而中断编号对应为0和1。在Arduino Uno开发板中,attachInterrupt函数第一个参数为中断编号,第二个参数为中断回调函数,第三个参数为触发模式,所以为了避免硬件引脚和中断编号,直接通过digitalPinToInterrupt函数解决。很多时候,attachInterrupt函数的使用也采用以下方式。 attachInterrupt(digitalPinToInterrupt(pin), handler, mode); ESP32-S3的中断引脚跟中断编号一致,也可不必用digitalPinToInterrupt函数,但是你在程序中也没有问题。 9.2 硬件设计 1. 例程功能 通过外部中断的方式让开发板上的BOOT独立按键控制LED灯翻转。 2. 硬件资源 1)LED灯 LED-IO1 2)独立按键 BOOT-IO0 3. 原理图 独立按键硬件部分的原理图,如下图所示。 图9.2.1 独立按键与ESP32-S3连接原理图 这里需要注意的是:BOOT设计为采样到按键另一端的低电平为有效电平。 9.3 软件设计 9.3.1 程序流程图 下面看看本实验的程序流程图: 图9.3.1.1 程序流程图 9.3.2 程序解析 1. exti驱动代码这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。EXTI驱动源码包括两个文件:exti.cpp和exti.h。 下面我们先解析exti.h的程序。 由硬件设计小节,我们知道KEY按键在硬件上连接到IO0,为了与按键实验进行区分,我们做了下面的引脚定义。 /* 引脚定义 */ #define KEY_INT_PIN 0 /* 外部中断引脚IO0 */ 下面我们再解析exti.cpp的程序,这里有两个函数exti_init和key_isr,其定义如下: uint8_t led_state = 0; /* 决定灯亮灭状态变量 */ /** * @param 无 * @retval 无 */ void exti_init(void) { key_init(); /* KEY初始化 */ attachInterrupt(digitalPinToInterrupt(KEY_INT_PIN), key_isr, FALLING); /* 设置KEY引脚为中断引脚,下降沿触发 */ } /** * @brief KEY外部中断回调函数 * @param 无 * @retval 无 */ void key_isr(void) { delay(10); if (KEY == 0) { led_state =! led_state; /* 两种情况:从0变1,从1变为0 */ } } exti_init函数是初始化外部中断引脚,首先调用key_init设置KEY引脚为上拉输入模式,然后调用attachInterrupt函数设置中断回调函数和设置中断引脚为下降沿触发。 key_isr函数就是中断引脚的中断回调函数。当按键被按下时,出现了下降沿即高电平变为低电平时,这时候就会跳到该函数去执行。函数内部很简单,首先调用delay函数进行按键消抖,然后判断按键是否真的被按下。当按键是按下情况,就对全局变量led_state进行取反操作,即led_state就有两种情况0和1,最终在loop函数中作为LED(x)宏函数的参数x决定了LED灯的状态。 2. 03_exti.ino代码在03_exti.ino里面编写如下代码: #include "led.h" #include "exti.h" /** * @brief 当程序开始执行时,将调用setup()函数,通常用来初始化变量、函数等 * @param 无 * @retval 无 */ void setup() { led_init(); /* LED初始化 */ exti_init(); /* 外部中断引脚初始化 */ } /** * @brief 循环函数,通常放程序的主体或者需要不断刷新的语句 * @param 无 * @retval 无 */ void loop() { LED(led_state); /* 灯的亮灭由led_state值决定,led_state变化在key_isr函数中实现 */ } 在setup函数中,除了要调用led_init函数对LED灯进行初始化,还要调用exti_init函数对按键KEY进行初始化以及配置KEY所在引脚的下降沿触发中断功能。接下来,在loop函数中,LED的状态通过全局变量led_state决定,若led_state为0,即运行LED(0)代码,这时候LED灯就会亮起;若led_state为1,即运行LED(1)代码,这时候LED灯就会熄灭。 当按键被按下时,这时候出现了下降沿,就会触发中断,进入到中断回调函数中。在该函数中,会对全局变量led_state进行取反操作,执行完又回到loop函数暂停的地方继续往下执行。最终,实现的效果就是:通过按下按键,LED灯的状态会进行翻转。 9.4 下载验证 下载完之后,通过BOOT按键来控制LED灯的亮灭状态。
|