esp32系列(4):GPIO中断学习

首先总结一下前面以及学习的内容:配置好开发环境后实现了基本的信号输入输出。
之前学习过中断相关知识就可以跳过前言部分了,如果初学的话,首先了解一下中断的概念。
之前的demo运行起来都是 初始化 -> while(1)循环 的工作模式。
现在有一个简单的场景:我们在while(1)循环中完成这些事:

1 看文档 -> 2 写代码 -> 3 调试代码 ┐  
   ↑                              │   
    └──────────────────────────────┘

以上是学习的过程, 现在增加一个触发状态"学累了"可以触发"玩手机"这个事件。
如果还是以前的循环处理过程,则实现的结果是每次在完成"看文档"、“写代码”、“调试代码”后,才可以判断是否学累了。
而实际情况是我们在做这三件事情的过程中可能就会达到“学累了”状态,而触发“玩手机”事件,玩了一会手机之后,继续进行学习任务。这就是中断的作用,在处理一个任务时,通过中断让cpu先处理中断事件,处理完后继续做之前的任务。

1 中断矩阵

ESP32 中断矩阵将任一外部中断源单独分配到每个 CPU 的任一外部中断上。

在这里插入图片描述

中断矩阵:

  • 输入:71个外部中断
  • 输出:两个 CPU 分别生成 26 个外部中断
  • 屏蔽 CPU 的 NMI 类型中断
  • 查询外部中断源当前的中断状态

关于外部中断源,没必要都去学习,我们只要知道一些常用的外设都可以产生中断就行,比如gpio、串口、spi、定时器等。

2 代码实现

2.1 GPIO 信号作为中断源

参考examples\peripherals\gpio\generic_gpio中的示例工程。
esp32系列(3):GPIO学习(以简单GPIO输入输出、ADC、DAC为例)通过 esp_err_t gpio_config(const gpio_config_t *pGPIOConfig) 函数配置GPIO的基本输入输出,同样的 gpio_config_t 结构体的 gpio_int_type_t类型 intr_type 字段可以设置中断类型。

  • GPIO_INTR_DISABLE : 禁用GPIO中断
  • GPIO_INTR_POSEDGE : 上升沿触发
  • GPIO_INTR_NEGEDGE : 下降沿触发
  • GPIO_INTR_ANYEDGE : 任意沿触发
  • GPIO_INTR_LOW_LEVEL : 低电平触发
  • GPIO_INTR_HIGH_LEVEL : 高电平触发

配置完成后,调用 esp_err_t gpio_install_isr_service(int intr_alloc_flags) 函数设置 GPIO ISR(中断服务程序,interrupt service routine) 处理服务。

调用 esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void *args); 为相应的GPIO引脚添加ISR handler。

在ISR handler指向的函数中,示例程序新建了一个 queue ,这涉及到freeRTOS的知识,不懂。但我试了如果不适用 queue 直接将相关的处理放在中断处理程序中,在运行时程序会崩溃。具体的原理有待学习。使用 queue 大概的流程就是

  • xQueueCreate( uxQueueLength, uxItemSize ) 创建一个queue实例。
  • xTaskCreate 函数创建一个新任务,并将其添加到准备运行的任务列表中。
  • 在中断处理程序中调用 xQueueSendFromISR 函数,功能:在 queue 中发布一个 item ,在中断服务程序中使用这个函数是安全的。
  • 在xTaskCreate创建的任务中 调用 xQueueReceive 函数从 queue 中接收 item 。并在收到 item 后完成相应的处理。

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"  
/**
 * Brief:
 * 基本的GPIO中断输入学习
 *
 * GPIO status:
 * GPIO4:  input,interrupt from rising edge.
 *
 * Test:
 *
 *
 */ 

#define GPIO_4   4
#define ESP_INTR_FLAG_DEFAULT 0 //中断标志位  

static xQueueHandle gpio_evt_queue = NULL;

void gpio_task1(void)
{
    for (int i = 5; i > 0; i--)
    {
        printf("task1: %d\n", i);
        vTaskDelay(500 / portTICK_RATE_MS);
    }
    return;
}

void gpio_task2(void)
{
    for (int i = 5; i > 0; i--)
    {
        printf("task2: %d\n", i);
        vTaskDelay(500 / portTICK_RATE_MS);
    }
    return;
}

void gpio_isr_handler(void* arg) 
{   
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);

}

static void gpio_task_example(void* arg)
{
    for( ;; )
    {
        // Task code goes here.
        uint32_t io_num;
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

void app_main(void)
{
    // GPIO1
    gpio_config_t io_conf = {};                 //新建配置GPIO pad的gpio_config功能参数的结构体  
    io_conf.pin_bit_mask = (1ULL << GPIO_4);    //设置GPIO4的掩码为1
    io_conf.mode = GPIO_MODE_INPUT;            //设置GPIO4 为输出模式
    io_conf.pull_up_en = 1;                     //上拉
    io_conf.pull_down_en = 0;                   //不下拉  
    io_conf.intr_type = GPIO_INTR_POSEDGE;      //GPIO4上升沿触发中断         
    if (gpio_config(&io_conf) == ESP_OK)       //配置GPIO4
        printf("gpio4_config succeed \n"); 
    else 
        printf("gpio4_config failed \n");   

    if (gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT) == ESP_OK)       //设置中断服务
        printf("gpio_install_isr_service succeed \n"); 
    else 
        printf("gpio_install_isr_service failed \n");  

    if (gpio_isr_handler_add(GPIO_4, gpio_isr_handler, (void*)GPIO_4 ) == ESP_OK)       //设置中断处理程序
        printf("gpio_isr_handler_add succeed \n"); 
    else 
        printf("gpio_isr_handler_add failed \n");   

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    while(1)
    {
        gpio_task1();
        gpio_task2();
    }
}

将GPIO4间断的插在GND,测试结果:

在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
ESP32支持硬件中断GPIO中断。 硬件中断: 硬件中断是由特定的硬件事件触发的,如定时器、串口接收中断等。在ESP32中,硬件中断可以管理许多不同的硬件资源,例如定时器、GPIO、RTC等。ESP32的硬件中断可以使用Arduino的attachInterrupt()函数进行设置。 GPIO中断GPIO中断是由GPIO引脚状态的改变触发的,例如输入信号从低电平变为高电平或从高电平变为低电平。ESP32中的GPIO引脚可以通过pinMode()函数配置为输入和输出模式,当GPIO引脚的输入状态发生改变时,ESP32可以通过gpio_intr_enable()函数启用GPIO中断。在中断响应函数中,可以使用gpio_get_level()函数读取GPIO引脚的当前输入状态。 下面是一个示例代码,演示如何在ESP32中使用GPIO中断: ``` #define BUTTON_PIN 0 // GPIO0 void IRAM_ATTR handleInterrupt(){ // Interrupt service routine // Toggle the LED digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); } void setup() { pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(LED_BUILTIN, OUTPUT); attachInterrupt(BUTTON_PIN, handleInterrupt, FALLING); } void loop() { // Do nothing } ``` 在上面的示例代码中,我们使用GPIO0作为输入引脚,通过INPUT_PULLUP配置它为输入模式,并在FALLING边沿触发GPIO中断。当GPIO引脚状态发生变化时,handleInterrupt()函数将被调用,在其中我们可以执行一些操作,例如切换LED的状态。在setup()函数中,我们使用attachInterrupt()函数将GPIO0的中断处理函数注册到GPIO中断,以便当引脚状态改变时能够调用它。在loop()函数中,我们什么也不做。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lu-ming.xyz

觉得有用的话点个赞吧 :)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值