ESP32-S3 实战指南:BOOT-KEY 按键驱动开发全解析

一、基础知识
本篇我们使用 BOOT 按键来学习一下 GPIO 功能,首先补充一下相关术语介绍。
1、GPIO(General Purpose Input/Output)
- GPIO 是微控制器上的通用引脚,既可以作为输入(读取外部信号),也可以作为输出(控制外部设备)。
- 在 ESP32 中,GPIO 引脚可以灵活配置,用于多种用途,例如读取传感器数据、控制 LED、驱动电机等。
2、输入(Input)
- 当 GPIO 配置为输入时,它可以读取外部信号的状态(高电平或低电平)。
- 例如,读取按键是否被按下(按键按下时,引脚电平会发生变化)。
3、输出(Output)
- 当 GPIO 配置为输出时,它可以控制外部设备的状态(例如点亮 LED 或驱动继电器)。
- 例如,将 GPIO 设置为高电平(3.3V)来点亮 LED,或设置为低电平(0V)来关闭 LED。
4、内部上拉(Pull-up)和下拉(Pull-down)
- 上拉电阻:将 GPIO 引脚通过内部电阻连接到电源(3.3V),使引脚在未连接外部信号时保持高电平。
- 下拉电阻:将 GPIO 引脚通过内部电阻连接到地(GND),使引脚在未连接外部信号时保持低电平。
- 这些电阻可以确保 GPIO 引脚在没有外部信号时处于确定的状态,避免悬空(floating)导致的不稳定。
5、中断引脚(Interrupt Pin)
- 中断是一种硬件机制,当 GPIO 引脚的状态发生变化时(例如从高电平变为低电平),会触发一个中断信号。
- 中断可以让微控制器立即响应外部事件,而不需要不断轮询(polling)引脚状态。
- 例如,当按键按下时,GPIO 引脚的电平变化会触发中断,微控制器可以立即执行相应的处理程序。
6、BOOT 按键和 IO0 引脚
- BOOT 按键:ESP32 开发板上通常有一个 BOOT 按键,用于进入下载模式或复位。
- IO0 引脚:这是 ESP32 的一个 GPIO 引脚,编号为 GPIO0。它通常与 BOOT 按键连接。
- 当 BOOT 按键按下时,IO0 引脚的电平会发生变化(例如从高电平变为低电平)。
7、设置为 GPIO 中断,接收按键请求
- 将 IO0 引脚配置为中断引脚,用于检测 BOOT 按键的按下事件。
- 当按键按下时,IO0 引脚的电平变化会触发中断,微控制器可以立即执行相应的代码(例如处理按键请求)。
二、设计图
更多详细资料请查询官网 立创·实战派ESP32-S3开发板 - 立创开源硬件平台

三、实战操作
1、Copy 示例工程 sample_project
示例工程路径:$HOME/esp/esp-idf/tools/templates/sample_project

直接在 esp-idf 目录下搜索 sample_project 也可以找到,接着复制一份工程代码到自己的编码路径,然后修改文件夹名字为 boot_key。

可以看到模板工程下只有一个空函数 app_main,函数体还没有代码,我们需要参考官方提供的 gpio 示例进行编码,路径在:$HOME/esp/esp-idf/examples/peripherals/gpio/generic_gpio

2、引入头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"// ...
使用 printf 函数,需要添加 stdio.h 头文件。string.h 和 stdlib.h 我们这里用不着,可以去掉。接下来是 3 个 freeRTOS 的头文件,最后一个头文件是用于 gpio 的配置。
3、gpio 配置
3.1、官方示例
首先看一下官方源码示例是怎么写的,再来看看我们需要修改哪些地方,代码注释如下:

// 官方示例代码
void app_main(void)
{// Step1、定义了一个 gpio_config_t 结构体变量。gpio_config_t io_conf = {};// Step2、定义引脚中断类型,这里是 GPIO_INTR_DISABLE,表示中断关闭io_conf.intr_type = GPIO_INTR_DISABLE;// Step3、配置模式,这里是 GPIO_MODE_OUTPUT 表示输出模式io_conf.mode = GPIO_MODE_OUTPUT;// Step4、配置引脚io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;// Step5、配置是否打开下拉电阻io_conf.pull_down_en = 0;// Step6、配置是否打开上拉电阻io_conf.pull_up_en = 0;// Step7、使用 gpio_config 函数进行配置gpio_config(&io_conf);
}
3.2、对应改动点说明
改动点1、修改引脚中断类型
//falling edge interrupt
io_conf.intr_type = GPIO_INTR_NEGEDGE;
开发板上的按键没有按下的时候是高电平,按下去以后是低电平,因此定义成下降沿中断。这里原来是 GPIO_INTR_DISABLE,表示中断关闭,这里我们修改为 GPIO_INTR_NEGEDGE,即下降沿中断。这些宏定义在 gpio_types.h 文件中被定义,我们在 gpio_example_main.c 文件中的 GPIO_INTR_DISABLE 上单击右键,然后选择“转到定义”,就可以找到这几个宏定义,如下所示:

typedef enum {GPIO_INTR_DISABLE = 0, /*!< Disable GPIO interrupt */GPIO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */GPIO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */GPIO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */GPIO_INTR_LOW_LEVEL = 4, /*!< GPIO interrupt type : input low level trigger */GPIO_INTR_HIGH_LEVEL = 5, /*!< GPIO interrupt type : input high level trigger */GPIO_INTR_MAX,
} gpio_int_type_t;
改动点2、修改 gpio 模式
//set as input mode
io_conf.mode = GPIO_MODE_INPUT;
修改为输入模式,就可以读取外部信号的状态,按键按下时,捕获到引脚电平变化信号。
改动点3、修改引脚
//bit mask of the pins GPIO0
io_conf.pin_bit_mask = 1<<GPIO_NUM_0;
因为 BOOT 按键连接到了 GPIO0,所以这里我们把原来的 GPIO_OUTPUT_PIN_SEL 修改为 1<<GPIO_NUM_0,实际上 GPIO_NUM_0 是一个宏定义,值为 0。
改动点4、修改打开上下拉电阻
//disable pull-down mode
io_conf.pull_down_en = 0;
//enable pull-up mode
io_conf.pull_up_en = 1;
打开上拉电阻,将 GPIO 引脚通过内部电阻连接到电源,使引脚在未连接外部信号时保持高电平,从而避免引脚悬空导致的不稳定问题。
补充:为什么不用下拉电阻?
下拉电阻也可以用于避免引脚悬空,但它会将引脚在没有外部信号时拉低到地(0V)。在按键检测中,通常使用上拉电阻,因为按键按下时会将引脚拉低到地,这种设计更符合直觉和常见的硬件电路设计。
3.3、改动代码整合
改动后的代码可以简化成这样子,
// ...void app_main(void)
{//zero-initialize the config structure.gpio_config_t io_conf = {.intr_type = GPIO_INTR_NEGEDGE,.mode = GPIO_MODE_DEF_INPUT,.pin_bit_mask = 1<<GPIO_NUM_0,.pull_down_en = 0,.pull_up_en = 1};//configure GPIO with the given settingsgpio_config(&io_conf);}
4、中断服务函数
4.1、官方示例
同样的,首先看一下官方源码示例是怎么写的,再来看看我们需要修改哪些地方,代码注释如下:

// 官方示例代码#define ESP_INTR_FLAG_DEFAULT 0static QueueHandle_t gpio_evt_queue = NULL;static void IRAM_ATTR 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)
{uint32_t io_num;for (;;) {if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num));}}
}void app_main(void)
{// ...// Step1、创建了一个队列,队列消息数量为 10,gpio_evt_queue 是队列句柄gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));// Step2、创建了一个任务,任务名称为 gpio_task_examplexTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);// Step3、启动 GPIO 中断服务,其中 ESP_INTR_FLAG_DEFAULT 的值是 0gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);// Step4、添加某个 GPIO 的中断,第 1 个参数指定哪个 GPIO 产生中断,第 2 个参数是中断服务函数的名称,第 3 个参数是中断服务函数的参数gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);// ...
}
4.2、对应改动点说明
gpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void*) GPIO_NUM_0);
这里我们只需要添加 GPIO0 的中断,因此修改了参数1、参数3。
4.3、改动代码整合
// ...void app_main(void)
{// ...//create a queue to handle gpio event from isrgpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));//start gpio taskxTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);//install gpio isr servicegpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);//hook isr handler for specific gpio pingpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void*) GPIO_NUM_0);
}
5、完整代码
#include <stdio.h>
// #include <string.h>
// #include <stdlib.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"#define ESP_INTR_FLAG_DEFAULT 0static QueueHandle_t gpio_evt_queue = NULL;static void IRAM_ATTR 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)
{uint32_t io_num;for (;;) {if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num));}}
}void app_main(void)
{//zero-initialize the config structure.gpio_config_t io_conf = {.intr_type = GPIO_INTR_NEGEDGE,.mode = GPIO_MODE_DEF_INPUT,.pin_bit_mask = 1<<GPIO_NUM_0,.pull_down_en = 0,.pull_up_en = 1};//configure GPIO with the given settingsgpio_config(&io_conf);//create a queue to handle gpio event from isrgpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));//start gpio taskxTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);//install gpio isr servicegpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);//hook isr handler for specific gpio pingpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void*) GPIO_NUM_0);
}

四、编译下载
配置 menu config,设置 flash size 为 16 MB,

一键烧录,

boot 按键效果,

可以看到,当我们按下 boot 键的时候,控制台输出 GPIO[0] intr, val: 0 符合预期。
五、生成默认配置
编译下载后,结果没有问题的话,使用 idf.py save-defconfig 命令生成 sdkconfig.defaults 文件。这个命令要打开“命令终端”执行,看结果的“串口终端”不行。
idf.py save-defconfig

回车执行命令后,会看到工程中多了一个 sdkconfig.defaults 文件,点击打开 sdkconfig.defaults 文件,会看到里面的内容。这个文件里面包含了我们对 menuconfig 的修改。

这时候,我们可以把工程中配置和编译生成的文件夹全部去掉,最后的文件如下所示:

使用 VSCode 重新打开工程,在选择目标芯片后,sdkconfig.defaults 文件里面的配置就配置到 menuconfig 里面了,省去了手动配置 menucofig。如果 menuconfig 里面配置的内容很多,这个文件就显得很有必要了。
相关文章:
ESP32-S3 实战指南:BOOT-KEY 按键驱动开发全解析
一、基础知识 本篇我们使用 BOOT 按键来学习一下 GPIO 功能,首先补充一下相关术语介绍。 1、GPIO(General Purpose Input/Output) GPIO 是微控制器上的通用引脚,既可以作为输入(读取外部信号)࿰…...
ssh配置 远程控制 远程协作 github本地配置
0.设备版本 windows11 ubuntu24.0.4 1.1 在 Linux 上启用 SSH 服务 首先,确保 Linux 计算机上安装并启用了 SSH 服务。 安装和启动 OpenSSH 服务(如果未安装) # 在终端安装 OpenSSH 服务(如果尚未安装) sudo apt …...
C++知识整理day9——继承(基类与派生类之间的转换、派生类的默认成员函数、多继承问题)
文章目录 1.继承的概念和定义2.基类与派生类之间的转换3.继承中的作用域4.派生类的默认成员函数5.实现一个不能被继承的类6.继承与友元7.继承与静态成员8.多继承和菱形继承问题8.1 继承分类及菱形继承8.2 虚继承 1.继承的概念和定义 概念: 继承(inheritance)机制是⾯…...
2024年国赛高教杯数学建模A题板凳龙闹元宵解题全过程文档及程序
2024年国赛高教杯数学建模 A题 板凳龙闹元宵 原题再现 “板凳龙”,又称“盘龙”,是浙闽地区的传统地方民俗文化活动。人们将少则几十条,多则上百条的板凳首尾相连,形成蜿蜒曲折的板凳龙。盘龙时,龙头在前领头&#x…...
华为认证考试证书下载步骤(纸质+电子版)
华为考试证书可以通过官方渠道下载相应的电子证书,部分高级认证如HCIE还支持申请纸质证书。 一、华为电子版证书申请步骤如下: ①访问华为培训与认证网站 打开浏览器,登录华为培训与认证官方网站 ②登录个人账号 在网站首页,点…...
[杂学笔记]工厂模式、多态、内存空间区域划分、cp指令破坏软连接问题、UDP如何实现可靠传输、滑动窗口的原理、进程与线程、线程之间的通信
目录 1.工厂模式 2.多态 3.内存空间区域划分 4.cp指令破坏软连接问题 5.UDP实现可靠传输 6.滑动窗口的原理 7.进程与线程 8.线程之间的通信 1.工厂模式 工厂模式是一种创建对象的设计模式。它提供了一种创建对象的方式,将对象的创建和使用分离,通…...
开源RAG主流框架有哪些?如何选型?
开源RAG主流框架有哪些?如何选型? 一、开源RAG框架全景图 (一)核心框架类型对比 类型典型工具技术特征适用场景传统RAGLangChain, Haystack线性流程(检索→生成)通用问答、知识库检索增强型RAGRAGFlow, AutoRAG支持重排序、多路召回优化高精度问答、复杂文档处理轻量级…...
【Android】用 chrome://inspect/#devices 调试H5页面
通常做Android开发的过程中,不可避免的需要遇到去与H5交互,甚至有时候需要去调试H5的信息。 这里分享一下Android工程里如何调试H5页面信息: 直接在浏览器地址栏输入 : chrome://inspect/#devices 直接连接手机usb,打开开发者模式…...
贪心算法精品题
1.找钱问题 本题的贪心策略在于我们希望就可能的保留作用大的5元 class Solution { public:bool lemonadeChange(vector<int>& bills) {std::map<int ,int> _map;for(auto ch:bills){if(ch 5) _map[ch];else if(ch 10){if(_map[5] 0) return false;else{_m…...
七、Spring Boot:初识与项目搭建
深入解析 Spring Boot:初识与项目搭建 Spring Boot 是基于 Spring Framework 的开源 Java 基础框架,旨在简化 Spring 应用的开发过程。它通过“约定优于配置”的理念,极大地减少了开发中的配置工作,同时提供了“开箱即用”的功能…...
WEB1~6通杀
##解题思路 这六道题,通杀了,只因为是PHP的特性 来,看web6,过滤最复杂的正则,而且不能解析成大于999的值,但是,php是弱类型的语言,我只要输入任意字符数字,最终值就为0&…...
孜然单授权系统V2.0PHP授权系统
孜然单授权V1.0系统,延续了2022年开发的孜然多应用授权系统V2.0 变更:多应用变单系统,去除没用的垃圾代码,从0开发,去除了一些没用的功能 完善了开发文档,之前那套是我写着玩的屎山代码,V1.0将展…...
Apache SeaTunnel 构建实时数据同步管道(最新版)
文章作者 王海林 白鲸开源 数据集成引擎研发 Apache SeaTunnel Committer & PMC Member,Apache SkyWalking Committer,多年平台研发经验,目前专注于数据集成领域。 导读 在当今数字化快速发展的时代,数据已然成为企业决策…...
数据保护API(DPAPI)深度剖析与安全实践
Windows DPAPI 安全机制解析 在当今数据泄露与网络攻击日益频繁的背景下,Windows 提供的 DPAPI(Data Protection API)成为开发者保护本地敏感数据的重要工具。本文将从 双层密钥体系、加密流程、跨上下文加密、已知攻击向量与防御措施、企业…...
服务器离线部署DeepSeek
目标 本次部署的目标是在本地服务器上部署DeepSeek。但是该服务不能连接外网,因此只能使用离线部署的方式。为了一次完成部署。现在云服务器上进行尝试。 云服务器部署尝试 云服务器配置 CentOS72080Ti 11GB 安装准备 1、上传iso并配置为本地yum源 安装前先将…...
ComfyUI:Stable Diffusion 及 LoRA、VAE 、ControlNet模型解析
目录 Stable Diffusion流程 扩散过程 去噪过程 checkpoints LoRA LoRA 位置与结构 LoRA 层与原层的关系 LoRA 层的参数拆解 VAE 训练特定 VAE 时更新的参数部分 ControlNet ControlNet 位置与结构 ControlNet 的训练过程 ControlNet 的参数处理与信息融合 Contr…...
微信小程序:多菜单栏设计效果
一、实现效果 二、代码 wxml 编辑前端界面,步骤 菜单逻辑: 逐步取出数组中的项,首先取出顶部菜单项,然后选中后取出选中的底部数据(左侧菜单+右侧内容),然后点击左侧菜单取出选中的左侧菜单对应的右侧内容 ①这里我的数据是全部封装到一个数组对象的,首先我的循环…...
【Linux Oracle】time命令+oracle exp压缩
Linux && Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.说明 Linux中的time命令:主要用于测量命令的执行时间,并显示该命令在执行过程中所使用的系统资源情况,如CPU时间、内存和…...
20分钟 Bash 上手指南
文章目录 bash 概念与学习目的第一个 bash 脚本bash 语法变量的使用位置参数管道符号(过滤条件)重定向符号条件测试命令条件语句case 条件分支Arrayfor 循环函数exit 关键字 bash 脚本记录历史命令查询文件分发内容 bash 概念与学习目的 bash࿰…...
v4l2子系统学习(三)编写虚拟摄像头驱动
文章目录 1、声明2、前言3、虚拟摄像头驱动编写3.1、编写硬件相关代码3.2、程序示例 1、声明 本文是在学习韦东山《驱动大全》V4L2子系统时,为梳理知识点和自己回看而记录,全部内容高度复制粘贴。 韦老师的《驱动大全》:商品详情 其对应的…...
【前端定位线上问题的多种方案(不依赖 Sentry)】
前端定位线上问题的多种方案(不依赖 Sentry) 🛠️ 一、构建时注入调试信息 🔧 1. 注入版本信息与 Git 提交哈希 Webpack 配置: // webpack.config.js const webpack require(webpack); const gitRevision require(…...
【虚拟仪器技术】labview操作指南和虚拟仪器技术习题答案(一)
今天是2025年2月24日,画的是fate/Grand Order里面的阿尔托莉雅.卡斯特,武内老师的画。 目录 第1章 第2章 第3章 第4章 第5章 关注作者了解更多 我的其他CSDN专栏 毕业设计 求职面试 大学英语 过程控制系统 工程测试技术 虚拟仪器技术 可编程…...
LabVIEW电能质量分析软件
随着电力系统的复杂性增加,电能质量问题日益突出,传统的电能质量检测装置多采用DSP技术,不仅开发周期长、功能单一,而且在多功能集成方面存在局限性。基于LabVIEW虚拟仪器开发平台的电能质量分析软件利用FFT、STFT、WT、HHT等多种…...
视频裂变加群推广分享引流源码
源码介绍 视频裂变加群推广分享引流源码 最近网上很火,很多人都在用,适合引流裂变推广 测试环境:PHP7.4(PHP版本不限制) 第一次访问送五次观看次数,用户达到观看次数后需要分享给好友或者群,好友必须点击推广链接后才会增加观看次…...
《深度剖析:AI与姿态估计技术在元宇宙VR交互中的应用困境》
在元宇宙的宏大版图里,虚拟现实(VR)交互是构建沉浸式体验的关键支柱,而人工智能(AI)与姿态估计技术的融合,本应成为提升交互体验的强大引擎。但在实际应用中,它们面临着诸多复杂且棘…...
项目一 - 任务3:搭建Java集成开发环境IntelliJ IDEA
通过本次实战,我们成功搭建了Java集成开发环境IntelliJ IDEA,并完成了多个任务。首先,安装了IntelliJ IDEA并进行了个性化设置,如选择主题、调整字体和编码等。接着,创建了Java项目、包和类,编写并运行了简…...
RoCBert:具有多模态对比预训练的健壮中文BERT
摘要 大规模预训练语言模型在自然语言处理(NLP)任务上取得了最新的最优结果(SOTA)。然而,这些模型容易受到对抗攻击的影响,尤其是对于表意文字语言(如中文)。 在本研究中࿰…...
Dockerfile 中的 COPY 语句:作用与使用详解
在 Docker 的构建过程中,Dockerfile 是一个核心文件,它定义了镜像的构建步骤和内容。其中,COPY 语句是一个非常重要的指令,用于将文件或目录从构建上下文(通常是 Dockerfile 所在的目录及其子目录)复制到容…...
DeepSeek开源周Day2:DeepEP - 专为 MoE 模型设计的超高效 GPU 通信库
项目地址:https://github.com/deepseek-ai/DeepEP 开源日历:2025-02-24起 每日9AM(北京时间)更新,持续五天 (2/5)! 引言 在大模型训练中,混合专家模型(Mixture-of-Experts, MoE)因其动…...
六十天前端强化训练之第二天CSS选择器与盒模型深度解析
欢迎来到编程星辰海的博客讲解 目录 一、CSS 核心概念 1. 三种引入方式 2. CSS 注释 3. 常见单位系统 二、CSS选择器核心知识 1. 基础选择器类型 2. 组合选择器 3. 伪类选择器(部分示例) 4. 优先级计算规则 三、盒模型深度解析 1. 标准盒模型图…...
