各位小伙伴们好!今天给大家带来一篇ESP32开发板上按键中断处理的干货教程。在嵌入式开发中,按键是最基础但也最常用的输入方式,掌握了按键中断的处理,你就能实现更加灵活的人机交互功能。本文将带你一步步实现ESP32开发板上Boot按键的中断检测功能,希望能对你的项目开发有所帮助!
本教程使用的开发环境如下:
如果你还没有搭建好ESP32的开发环境,可以参考ESP32环境搭建教程完成准备工作。
在我们的ESP32开发板上有两个按键:
我们本次教程将重点关注如何使用Boot按键,实现按下按键时触发中断并输出提示信息的功能。
首先让我们运行一下ESP-IDF提供的按键示例,看看效果。
# 编译下载命令示例
idf.py set-target esp32
idf.py flash monitor
当程序运行后,按下开发板上的Boot按键,终端会输出按键中断信息。可以通过CTRL+[
退出终端监视。
接下来我们将从头开始创建一个Boot按键检测项目:
# 从示例项目复制创建新项目
cp -r $IDF_PATH/examples/get_started/sample_project ./boot_key
cd boot_key
打开CMakeLists.txt
文件,修改项目名称:
cmake_minimum_required(VERSION 3.5)
project(boot_key)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(boot_key)
打开main/main.c
文件,编写按键中断检测代码:
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
// 定义一个消息队列,用于中断和任务之间的通信
static xQueueHandle gpio_evt_queue = NULL;
// GPIO中断服务函数
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
// 发送GPIO口编号到队列,使用ISR版本的发送函数
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
// GPIO任务处理函数
static void gpio_task_example(void* arg)
{
uint32_t io_num;
for(;;) {
// 从队列中接收GPIO编号
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
// 输出GPIO编号和当前电平状态
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}
void app_main(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_NEGEDGE, // 下降沿触发中断
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pin_bit_mask = (1ULL << GPIO_NUM_0), // 选择GPIO0(Boot按键)
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉电阻
};
// 配置GPIO
gpio_config(&io_conf);
// 创建消息队列,容量为10个uint32_t元素
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
// 创建GPIO处理任务
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
// 安装GPIO中断服务
gpio_install_isr_service(0);
// 将中断处理函数挂载到GPIO0
gpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void*) GPIO_NUM_0);
// 主程序可以进行其他操作...
printf("Boot Key 中断检测程序已启动,请按下开发板上的Boot按键测试...\n");
}
# 选择目标芯片
idf.py set-target esp32s3
# 配置项目(如需要)
idf.py menuconfig
# 编译、下载并监视输出
idf.py flash monitor
让我们深入分析一下这段代码的实现原理:
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_NEGEDGE, // 下降沿触发中断
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pin_bit_mask = (1ULL << GPIO_NUM_0), // 选择GPIO0(Boot按键)
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉电阻
};
这段代码创建了一个GPIO配置结构体,我们设置了:
当按下Boot按键时,程序的执行流程为:
gpio_isr_handler
函数gpio_task_example
任务从队列接收消息这种设计将中断处理和任务处理分开,符合FreeRTOS的设计理念,避免在中断处理函数中执行耗时操作。
我们可以生成一个默认的配置文件,方便后续开发使用:
# 在项目目录下执行
idf.py fullclean
idf.py set-target esp32s3
idf.py menuconfig
# 配置完成后保存退出,会生成sdkconfig文件
这个配置文件包含了项目所需的各种配置参数,在下次编译时会自动加载,无需重新配置。
通过本教程,我们学习了:
// 按键去抖示例代码
#define DEBOUNCE_TIME_MS 50
static void gpio_task_example(void* arg)
{
uint32_t io_num;
uint32_t last_press_time = 0;
for(;;) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
uint32_t current_time = xTaskGetTickCount() * portTICK_PERIOD_MS;
if((current_time - last_press_time) > DEBOUNCE_TIME_MS) {
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
last_press_time = current_time;
}
}
}
}
希望这篇教程对你有所帮助!如有疑问,欢迎在评论区交流讨论!
如果你觉得这篇文章有用,别忘了点赞、收藏和关注,你的支持是我创作的最大动力!