ESP32的中断

esp32的结构体对齐 esp32gpio_vscode


ESP32的中断矩阵是一个关键组件,它负责将外部中断源单独映射到每个CPU的外部中断上。其主要功能和特点如下:

  1. 中断源映射:中断矩阵能够接受多个外部中断源作为输入,然后将这些中断源映射到CPU的外部中断上。这种映射关系允许CPU及时响应并处理来自不同外设的中断信号。
  2. 多CPU支持:ESP32通常具有多个CPU核心,中断矩阵可以为每个CPU生成相应的外部中断。这意味着不同的CPU可以并行处理来自不同外设的中断,提高了系统的整体性能。
  3. 屏蔽与查询功能:中断矩阵还提供了屏蔽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)

esp32的结构体对齐 esp32gpio_esp32的结构体对齐_02

https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/peripherals/gpio.html我们按照网站中的顺序依次看看和中断相关的函数。

esp32的结构体对齐 esp32gpio_mcu_03

设置中断触发的类型,这个实际上可以在初始化GPIO的时候就设置好。

esp32的结构体对齐 esp32gpio_esp32的结构体对齐_04

 常用的就是上升沿或者是下降沿。

esp32的结构体对齐 esp32gpio_嵌入式硬件_05

 这个不用多说,就是启用GPIO的中断(实测的时候,没有使用这个函数貌似也可以,之前忘记加上这个结果依旧可以进入中断,但是最好就是加上)。

esp32的结构体对齐 esp32gpio_vscode_06

有使能也有失能。

esp32的结构体对齐 esp32gpio_esp32的结构体对齐_07

这个函数,给所有GPIO注册一个中断处理函数,但是我实测之后发现并不好使,它只被触发了一次,可能后续还有什么需要操作的地方,但是我没找到解决方法。

因此我们就不使用这个函数去注册中断处理函数了

我们使用下面两个函数去注册。

esp32的结构体对齐 esp32gpio_mcu_08

首先我们使用这个函数去配置中断。并且也看到了,这个函数和上面那个是冲突的。

可填的参数有下面几个,我们根据实际需求选择。

esp32的结构体对齐 esp32gpio_vscode_09

esp32的结构体对齐 esp32gpio_嵌入式硬件_10

配置完中断之后,我们还需要绑定一个中断处理函数。

使用上面的函数,先填入我们的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);
    }
}

需要注意的是最好不要在中断函数里执行太久,避免使用延时函数(上面代码为了消抖没办法才用的)和打印日志。