esp32学习-按键操作

运行效果:点击按键0,输出按键0的电平
在这里插入图片描述

1.关键概念

2.运行流程

2.1gpio配置

gpio配置主要分为四步

  1. gpio参数配置
  2. 初始化配置参数
  3. 使能中断
  4. 给指定GPIO端口注册对应的中断函数

2.1.1gpio参数配置(通过gpio_config_t结构体配置属性)

esp32 gpio参数配置主要配置如下五项

  1. 目标引脚
  2. gpio工作模式
  3. 是否启用上拉电阻
  4. 是否启用下拉电阻
  5. 中断触发模式
    在这里插入图片描述

2.1.2初始化gpio配置参数gpio_config

参数配置结构体配置好以后,使用gpio_config初始化配置参数

2.1.3使能中断gpio_install_isr_service

使能中断以后才可以配置中断函数等

宏定义说明
0默认配置
ESP_INTR_FLAG_LEVEL1 ~ ESP_INTR_FLAG_LEVEL5指定中断优先级
ESP_INTR_FLAG_IRAM将 ISR 放入 IRAM,加快响应速度(适合深睡唤醒)
ESP_INTR_FLAG_SHARED允许中断共享
ESP_INTR_FLAG_EDGE明确声明为边沿触发
ESP_INTR_FLAG_LOWMED / HIGH控制中断分配策略

2.1.4配置中断函数gpio_isr_handle_add

给指定GPIO口注册对应的中断函数,并指定函数参数gpio_isr_handler_add(ESP32的中断函数是根据io编号进行绑定的)

参数类型说明
gpio_numgpio_num_t要绑定中断的引脚编号(如 GPIO_NUM_0
isr_handlergpio_isr_t中断回调函数指针
argsvoid*用户自定义参数,在回调函数中原样返回

注意

  1. 回调函数格式必须为void IRAM_ATTR gpio_isr_handler(void* arg),IRAM_ATTR 表示函数放在 IRAM(指令 RAM)中执行,避免因 Flash 访问延迟而丢失中断响应
  2. 未执行上一步使能对应gpio口时会返回错误ESP_ERR_INVALID_STATE

2.2 freertos线程任务配置(和中断信息进行交互)

2.2.1 创建缓存队列xQueueCreate

该处通过缓存与中断响应函数进行数据交互
常用接口:

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
参数说明
uxQueueLength队列最大消息数
uxItemSize每个消息元素的大小(字节)

2.2.2 创建rtos线程任务xTaskCreate

xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
参数类型说明
pvTaskCodeTaskFunction_t要执行的任务函数,必须是 void func(void *param) 格式
pcNameconst char*任务名称(用于调试)
usStackDepthuint32_t栈大小,单位为 4 字节
pvParametersvoid*传递参数
uxPriorityUBaseType_t任务优先级(数值越大越高)
pxCreatedTaskTaskHandle_t*返回任务句柄(可为 NULL)

2.2.3 常用其他相关接口

1.中断服务发送数据到缓存队列

xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
参数含义
xQueue队列句柄
pvItemToQueue指向要发送的数据的指针
pxHigherPriorityTaskWoken若唤醒更高优先级任务则置为 pdTRUE(可为 NULL)

返回结果:

  • pdPASS:数据成功放入队列
  • errQUEUE_FULL:队列满,无法放入数据

2.从队列中获取数据

xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY);
参数含义
xQueue队列句柄
pvBuffer用于接收数据的缓冲区指针
xTicksToWait阻塞时间(单位:tick)

在这里插入图片描述
返回值:

  • pdPASS:成功接收到数据
  • errQUEUE_EMPTY:队列为空且等待时间超时

2.3 其他相关接口

1.获取引脚电平结果

int gpio_get_level(gpio_num_t gpio_num);

3 代码示例

main.c

#include <stdio.h>
#include <inttypes.h>
#include <driver/gpio.h>
#include <hal/gpio_types.h>
#include <esp_attr.h>
#include "freertos/FreeRTOS.h" // 要使用队列queue,必须先includeFreeRTOS.h
#include <freertos/queue.h>

/*
错误点总结:
①gpio_isr_handler函数中不能直接将arg参数传递给xQueueSendFromISR,因为主函数中添加中断函数时传递的GPIO_NUM_0是一个枚举值,地址中内容为0
*/

QueueHandle_t gpio_evt_queue = NULL;

void IRAM_ATTR gpio_isr_handler(void* arg){
    uint32_t gpio_num = (uint32_t)arg; // 将传入参数GPIO_NUM_0从void*转回实际数据类型uint32_t
    // xQueueSendFromISR(gpio_evt_queue, arg, NULL); // arg是一个指针,其保存的地址为GPIO_NUM_0(即地址0),解析的时候会被当做空指针处理
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); // 将端口号保存到队列中

}

void gpio_task_example(void* arg){
    uint32_t gpio_num;
    while(1){
        xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY); // 获取队列的值放到io_num变量中,portMAX_DELAY表示为空时一直等待
        printf("GPIO[%lu] intr, val: %d\n", gpio_num, gpio_get_level(gpio_num)); // 无符号整数占位使用lu
    }
}


void app_main(void)
{
    // 1.配置gpio
    gpio_config_t key_config = {
        .intr_type = GPIO_INTR_NEGEDGE,
        .mode = GPIO_MODE_INPUT,
        .pin_bit_mask = 1 << GPIO_NUM_0,
        .pull_down_en = 1,
        .pull_up_en = 0,

    };

    // 2.初始化GPIO配置
    gpio_config(&key_config);

    // 3.初始化消息队列,容量为10个int32_t数据类型
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

    // 4.创建线程输出队列内容
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    // 5.使能定时器中断
    gpio_install_isr_service(0);

    // 6.指定IO口,配置定时器中断函数
    gpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void *)GPIO_NUM_0);
}

4.问题总结

1.中断回调函数中IRAM_ATTR作用

  • RAM_ATTR 是 ESP-IDF 中的一个函数属性宏,全称是 “Instruction RAM Attribute”,用于告诉编译器 将该函数放入 ESP32 的 IRAM(指令 RAM)中执行,而不是默认的 flash。
  • ESP32 正常情况下大多数代码是从 Flash(外部 SPI Flash)中运行的。但当发生中断时,如果中断服务函数(ISR)所在代码段仍在 Flash 中,而此时恰好 Flash 被其他操作占用(比如写 Flash 或 WiFi 使用 SPI Flash),会导致中断函数无法及时执行,从而造成 系统崩溃或响应延迟。

2.队列声明QueueHandle_t不用指定存放数据类型吗?
freertos中队列是根据元素大小(字节进行存储的),类型安全与数据准确性由自己保证

3.为什么main函数运行完以后没有直接退出
app_main只是freertos调度任务中的一个,就算结束以后freertos还是一直进行调度(例如app_main线程中通过xTaskCreate创建的新的线程任务)

4.队列使用报错
在这里插入图片描述
使用queue.h前必须include FreeRTOS.h头文件
在这里插入图片描述
5.读写缓存队列时,怎么知道读写数据的大小
创建队列时指定了每一个元素的大小
如下每一个元素大小为uint32_t,即四个字节

gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值