第十三章 EXIT实验
本章将详细介绍如何将GPIO引脚配置为外部中断输入,帮助开发者掌握GPIO中断的基础使用方法。这部分内容对处理按键输入、传感器信号等外部事件至关重要,通过中断方式可以有效提高系统的响应效率。
本章分为如下几个小节:
13.1 外部中断介绍
13.2 硬件设计
13.3 程序设计
13.4 下载验证
13.1 外部中断介绍
在上一章节中,我们虽然实现了GPIO口输入功能的读取,但代码始终在检测IO输入口的变化,导致在代码量增加时,按键检测部分的轮询效率降低。尤其在某些特定场合,如按键可能一天才被按下一次,实时检测将造成大量时间浪费。为优化此问题,我们引入了外部中断的概念。外部中断即在按键被按下(触发中断)时执行相关功能,从而显著节省CPU资源,因此在实际项目中应用广泛。
13.1.1 什么是中断
外部中断属于硬件中断,由微控制器外部事件触发。微控制器的特定引脚被设计为对特定事件(如按钮按压、传感器信号变化等)作出响应,这些引脚通常称为“外部中断引脚”。一旦外部中断事件发生,当前程序执行将立即暂停,并跳转到相应的中断服务程序(ISR)进行处理。处理完毕后,程序会恢复执行,从被中断的地方继续。下图是CPU中断处理过程。
图13.1.1 CPU中断处理过程
对于嵌入式和实时系统而言,外部中断的使用至关重要,它能使系统对外部事件作出即时响应,极大提升系统效率和实时性。
13.1.2 ESP32-P4外部中断介绍
ESP32-P4的HP IO MUX和HP GPIO矩阵提供了强大的中断处理能力,允许开发者配置GPIO引脚以响应外部事件。这些事件可以是按键按下、传感器信号变化等。ESP32-P4的HP IO MUX和HP GPIO矩阵可以生成以下中断信号,供中断矩阵处理。中断矩阵的相关知识请看第二章节的内容。
1)GPIO_INTR0
2)GPIO_INTR1
3)GPIO_INTR2
4)GPIO_INTR3
5)GPIO_PAD_COMP_INTR
ESP32-P4的中断矩阵为开发者提供了灵活的外部事件响应机制。ESP32-P4的55个GPIO引脚可以通过任意一个内部中断信号(GPIO_INTR0、GPIO_INTR1、GPIO_INTR2、GPIO_INTR3)来响应外部事件。这些中断信号通过中断矩阵传递和处理,确保系统可以快速响应和执行。其中,GPIO_PAD_COMP_INTR 是一种特殊的中断信号,与模拟电压比较器相关。详细信息可以参考《ESP32-P4技术参考手册》中的模拟电压比较器(Analog Voltage Comparator)章节。
根据本书籍第二章的2.4.5小节中的图2.4.5.2所示位置,可以找到GPIO_INTR0、GPIO_INTR1、GPIO_INTR2和GPIO_INTR3这些内部中断源的中断号,它们分别对应中断号74至77号,如下图所示。
图13.1.2.1 GPIO_INTR0等CPU外设中断源映射和状态寄存器
这些中断信号由ESP32-P4的GPIO引脚触发,并通过中断矩阵传递给处理器核心以响应外部事件。至于如何配置这55个GPIO的触发条件,实际上笔者在LED实验时已经讲解过,GPIO管脚支持外部中断配置。外部中断具备两种主要的触发类型:电平触发和边沿触发。电平触发指的是高电平和低电平触发,而边沿触发则包含上升沿、下降沿和任意沿的触发。
下图展示了配置这四个内部中断源(GPIO_INTR0、GPIO_INTR1、GPIO_INTR2、GPIO_INTR3)触发条件的寄存器。
表13.1.2.1 HP IO MUX和HP GPIO Matrix提供了内部中断源
上表13.1.2.1展示了ESP32-P4的HP IO MUX和HP GPIO Matrix中GPIO内部中断源的配置寄存器。针对GPIO_INTR0、GPIO_INTR1、GPIO_INTR2和GPIO_INTR3,可以通过配置不同的寄存器对GPIO的中断触发条件进行设置。
下面笔者以GPIO_INTR0为例讲解:
1)GPIO_STATUS_INTERRUPT[n] & GPIO_PINn_INT_ENA[0] (n: 0 ~ 31):用于配置GPIO0到GPIO31号引脚的启动中断。
2)GPIO_STATUS1_INTERRUPT[n] & GPIO_PINn+32_INT_ENA[0] (n: 0 ~ 22):用于配置GPIO32到GPIO54号引脚的中断触发类型。
其他内部中断(如GPIO_INTR1、GPIO_INTR2、GPIO_INTR3)的配置方式与GPIO_INTR0类似。下图为GPIO_STATUS_INTERRUPT和GPIO_PINn_INT_ENA[0] (n: 0 ~ 31)描述内容。
图13.1.2.2 GPIO_STATUS_INTERRUPT和GPIO_PINn_INT_ENA[0] (n: 0 ~ 31)描述
其他寄存器的详细描述,请参阅《ESP32-P4技术参考手册》中的7.18 节 “Interrupts”。
上述的内容是HP系统的外部中断相关内容,在低功耗(LP)系统下,ESP32-P4的LP IO MUX和GPIO矩阵可以生成LP_GPIO_INTR0中断信号(中断号为10),并通过中断矩阵发送至处理器。低功耗模式时,GPIO0~GPIO15由LP系统管理,允许将这些GPIO配置为外部中断,并由LP_GPIO_INTR0统一管理,如下图所示。
图13.1.2.3 LP IO MUX和LP GPIO矩阵内部中断源
在低功耗(LP)系统下,要开启GPIO0~GPIO15的中断,可以使用上图的LP_GPIO_STATUS_DATA[n](n: 0 ~ 15)寄存器开启中断。同时,使用LP_GPIO_PINn_INT_TYPE(n: 0 ~ 15)寄存器配置每个 GPIO 引脚的中断触发类型,包括高电平、低电平、上升沿、下降沿和任意沿触发。这些配置可以实现低功耗系统中对 GPIO 引脚的外部中断响应,有效支持低功耗操作模式下的外部事件监控需求。
13.2 硬件设计
13.2.1 例程功能
通过将BOOT按键配置为外部中断触发,可以在每次按下BOOT按键时触发中断处理程序,从而实现LED0的电平翻转效果。
13.2.2 硬件资源
1)LED灯
LED 0 - IO51
2)KEY按键
BOOT - IO35
13.2.3 原理图
KEY原理图已在12.2.3小节中详细阐述,为避免重复,此处不再赘述。
13.3 程序设计
13.3.1 GPIO的IDF驱动
接下来,笔者将介绍常用的GPIO EXIT函数,这些函数的描述及其作用如下:
1,注册中断函数gpio_install_isr_service
该函数用于注册中断服务,其函数原型如下:- esp_err_t gpio_install_isr_service(int intr_alloc_flags);
复制代码 函数参数:
表13.3.1.1 gpio_install_isr_service函数形参描述
返回值:
ESP_OK表示操作成功。
ESP_ERR_NO_MEM表示内存不足,无法安装服务。
ESP_ERR_INVALID_STATE表示ISR服务已安装,不能重复安装。
ESP_ERR_NOT_FOUND表示未找到符合指定标志的空闲中断。
ESP_ERR_INVALID_ARG表示GPIO参数错误。
2,分配中断函数gpio_isr_handler_add
该函数用于注册某个管脚的中断服务函数,其函数原型如下:- esp_err_t gpio_isr_handler_add( gpio_num_t gpio_num, gpio_isr_t isr_handler,
- void *args);
复制代码 函数形参:
表13.3.1.2 gpio_isr_handler_add函数形参描述
返回值:
ESP_OK表示操作成功。
ESP_ERR_INVALID_STATE表示ISR服务未初始化或状态错误。
ESP_ERR_INVALID_ARG表示参数错误。
这里需要注意ISR处理程序函数的格式,如下所示。- void (*gpio_isr_t)(void *arg);
复制代码 在编写中断回调函数时,需要注意。
13.3.2 程序流程图
图11.3.2.1 EXIT实验程序流程图
13.3.3 程序解析
在03_exit例程中,作者在03_exit\components\BSP路径下新建EXIT文件夹,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
1,EXIT驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。EXIT驱动源码包括两个文件:exit.c和exit.h。
下面先解析exit.h的程序。对EXIT引脚做了相关定义以及功能实现。- /* 引脚定义 */
- #define BOOT_INT_GPIO_PIN GPIO_NUM_35
- /* IO操作 */
- #define BOOT_INT gpio_get_level(BOOT_INT_GPIO_PIN)
复制代码 上述源码,定义了BOOT按键所使用的GPIO 脚编号为 GPIO_NUM_35,即GPIO35,通过宏定义BOOT_INT,直接调用gpio_get_level(BOOT_INT_GPIO_PIN),用于读取GPIO_NUM_35引脚的电平状态。如果BOOT按键连接到GPIO35,可以直接使用BOOT_INT来判断引脚电平,从而检测 BOOT 按键的按下或释放状态。
下面我们再解析exit.c的程序,看一下初始化函数exit_init,代码如下:
[code]/** * @brief 外部中断初始化程序 * @param 无 * @retval 无 */void exit_init(void){ gpio_config_t gpio_init_struct; /* 配置BOOT引脚和外部中断 */ gpio_init_struct.mode = GPIO_MODE_INPUT; /* 输入模式 */ gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE; /* 上拉使能 */ gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 下拉失能 */ gpio_init_struct.intr_type = GPIO_INTR_NEGEDGE; /* 下降沿触发 */ gpio_init_struct.pin_bit_mask = 1ull |