ESP32的中断
ESP32的中断矩阵是一个关键组件,它负责将外部中断源单独映射到每个CPU的外部中断上。其主要功能和特点如下:
- 中断源映射:中断矩阵能够接受多个外部中断源作为输入,然后将这些中断源映射到CPU的外部中断上。这种映射关系允许CPU及时响应并处理来自不同外设的中断信号。
- 多CPU支持:ESP32通常具有多个CPU核心,中断矩阵可以为每个CPU生成相应的外部中断。这意味着不同的CPU可以并行处理来自不同外设的中断,提高了系统的整体性能。
- 屏蔽与查询功能:中断矩阵还提供了屏蔽CPU的NMI(非屏蔽中断)类型中断的功能,这有助于确保关键中断信号能够得到优先处理。此外,它还可以查询外部中断源当前的中断状态,为系统调试和故障排查提供了便利。
在ESP32的实际应用中,中断矩阵的作用不可忽视。它使得CPU能够高效地处理来自各种外设的中断信号,从而实现对系统资源的有效利用和响应速度的提升。通过合理地配置中断矩阵,开发人员可以优化系统的性能,提高系统的稳定性和可靠性。
以上描述来自文心一言。
简单来说就是ESP32通过中断矩阵,在外部中断这块很强。每个CPU可以有32个中断,而ESP32是双核的,因此理论上可以处理64个中断。而在之前STM32的介绍外部中断的文章中我们可以得知,STM32只有16个外部中断通道,因此同时只能有16个外部中断。
虽然我个人觉得外部中断实际运用中大概率不会同时使用那么多,但是我单方面宣布,在外部中断这方面ESP32完胜STM32。
其实在硬件层面的设计我们可以不需要知道的很清楚,我们只需要知道怎么使用即可。
库函数
外部中断相关的函数,在编程指南中和GPIO是在同一个地方的。
GPIO & RTC GPIO - ESP32 - — ESP-IDF 编程指南 latest 文档 (espressif.com)https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/peripherals/gpio.html我们按照网站中的顺序依次看看和中断相关的函数。
设置中断触发的类型,这个实际上可以在初始化GPIO的时候就设置好。
常用的就是上升沿或者是下降沿。
这个不用多说,就是启用GPIO的中断(实测的时候,没有使用这个函数貌似也可以,之前忘记加上这个结果依旧可以进入中断,但是最好就是加上)。
有使能也有失能。
这个函数,给所有GPIO注册一个中断处理函数,但是我实测之后发现并不好使,它只被触发了一次,可能后续还有什么需要操作的地方,但是我没找到解决方法。
因此我们就不使用这个函数去注册中断处理函数了。
我们使用下面两个函数去注册。
首先我们使用这个函数去配置中断。并且也看到了,这个函数和上面那个是冲突的。
可填的参数有下面几个,我们根据实际需求选择。
配置完中断之后,我们还需要绑定一个中断处理函数。
使用上面的函数,先填入我们的GPIO口,再填入中断函数的名字,最后一个参数是传给中断函数的参数,我们给个NULL就行。
关于中断处理函数,我们名字可以随便起,然后返回值为void,参数列表可以给个void*的形参。
另外可以在返回值后面,函数名前面加个IRAM_ATTR。不加也可以。
如果需要使用IRAM_ATTR,那么需要包含头文件。
#include "esp_attr.h"
下面是文心一言的介绍。
在ESP-IDF(Espressif IoT Development Framework)中,IRAM_ATTR是一个宏定义,用于将特定的函数或变量指定为在IRAM(指令RAM)中运行或存储。IRAM是ESP32等芯片内部的一块特殊RAM区域,它主要用于存储那些需要在RAM中直接执行的代码,以加快代码执行速度并减少从Flash加载代码所消耗的时间。
使用IRAM_ATTR宏定义可以确保相关的代码或数据被放置在IRAM中。这对于时间关键的代码部分尤为重要,因为这些代码需要尽可能快地执行,以便及时响应中断或执行其他关键任务。通过将这部分代码放在IRAM中,可以减少CPU访问Flash所带来的延迟,因为直接从RAM中读取和执行代码通常比从Flash中读取要快得多。
例如,某些中断服务程序(ISR)需要快速响应中断事件,因此它们应该被放置在IRAM中以确保快速执行。同样,一些需要在RAM中运行的库函数或应用程序代码也可以使用IRAM_ATTR来指定它们的存储位置。
需要注意的是,IRAM的容量有限,因此应该谨慎使用IRAM_ATTR,确保只将真正需要快速执行的代码放置在IRAM中,以避免浪费宝贵的RAM资源。在ESP-IDF的文档和参考手册中,通常会详细说明IRAM的大小和使用限制,以便开发者能够正确地使用它。
那么有了上面的函数之后,我们就可以实现一下按键控制LED的亮灭。
完整代码
#include "driver/gpio.h"
#include <unistd.h>
#include "esp_attr.h"
void Z_Init_LED_GPIO(void){
gpio_config_t init;
init.intr_type = GPIO_INTR_DISABLE; // 失能中断;
init.mode = GPIO_MODE_OUTPUT | GPIO_MODE_INPUT; // 输出模式和输入
init.pin_bit_mask = (1ULL << 18); // GPIO18
init.pull_down_en = GPIO_PULLDOWN_DISABLE; // 失能下拉模式
init.pull_up_en = GPIO_PULLUP_ENABLE; // 使能上拉模式
gpio_config(&init);
}
void IRAM_ATTR IT_Function(void *arg){
//消抖处理
if(gpio_get_level(10)==1){
usleep(10);
if(gpio_get_level(10)==1){
//执行的代码写这里
gpio_set_level(18,!gpio_get_level(18));
}
}
}
void Z_Init_IT_GPIO(void){
gpio_config_t init;
init.intr_type = GPIO_INTR_POSEDGE; // 开启上升沿中断;
init.mode = GPIO_MODE_INPUT; // 输入模式
init.pin_bit_mask = (1ULL << 10); // GPIO10
init.pull_down_en = GPIO_PULLDOWN_DISABLE; // 失能下拉模式
init.pull_up_en = GPIO_PULLUP_ENABLE; // 使能上拉模式
gpio_config(&init);
gpio_install_isr_service(ESP_INTR_FLAG_EDGE); // 安装 GPIO 中断服务程序(边沿触发)
gpio_isr_handler_add(GPIO_NUM_10, IT_Function, NULL); // 分配中断处理程序
gpio_intr_enable(GPIO_NUM_10);
}
void app_main(void){
Z_Init_LED_GPIO();
Z_Init_IT_GPIO();
while (1){
sleep(1);
}
}
需要注意的是最好不要在中断函数里执行太久,避免使用延时函数(上面代码为了消抖没办法才用的)和打印日志。