ESP-IDF ESP32按键检测(单击、长按、连击)

基于esp32的单按键多功能 在平常项目中,经常会使用按键来进行一些人机交互。但只是经过判断按键连接引脚电平变化来判断单击操作,因此一个按键只能对应一个事件。为了扩展硬件的利用效率(榨干软件开发大脑),可以通过软件算法来实现判断按键的单击 双击 长按等操作。 阅读详情
AI摘要

本文详细介绍了基于ESP32的按键检测与处理机制,包括按键的初始化、扫描、长按、短按及松手事件的处理。通过具体的代码示例,展示了如何实现按键的多种点击类型,如单击、双击、长按和松开等。

头文件,bsp_key.h

#define KEY_NUM     2    //按键数量

typedef enum{
    KEY1_RELEASE = 0x00,
    KEY1_LONGCLICKED,
    KEY1_LONGLONG,      
    KEY1_MULTICLICKED_1,
    KEY1_MULTICLICKED_2,
    KEY1_MULTICLICKED_3,
    KEY1_MULTICLICKED_4,
    KEY1_MULTICLICKED_5,
    KEY2_RELEASE,
    KEY2_LONGCLICKED,   
    KEY2_LONGLONG,    
    KEY2_MULTICLICKED_1,
    KEY2_MULTICLICKED_2,
    KEY2_MULTICLICKED_3,
    KEY2_MULTICLICKED_4,
    KEY2_MULTICLICKED_5,
};

#define LONG_PRESS_THRESHOLD 1000   //长长按时间
#define IDLE_COUNT_THRESHOLD 200    //空闲时间
#define CLICK_LONG_THRESHOLD 500    //长按时间
#define CLICK_COUNT_MAX 1500        
#define CONFIRM_COUNT 25            //消抖时间

typedef struct{
    int (*Scan)(void);
    void (*Init)(void);
    uint16_t KeyCnt;
    uint8_t IdleCnt;
    uint8_t ClickCnt;
    uint8_t LongPressed;
    uint8_t State;
}Key_t;

通过修改头文件中的宏定义可以修改按键检测的参数。

源文件,bsp_key.c

定义按键对象

Key_t g_Key_List[KEY_NUM];

初始化代码

/*************************************  USER CODE  *************************************/

/*
*********************************************************************************************************
*	函 数 名: Key1_Scan
*	功能说明: 扫描按键1的状态
*	形    参: 无
*	返 回 值: 按键1的状态
*********************************************************************************************************
*/
static int Key1_Scan(void)
{
    return gpio_get_level(9);
}

/*
*********************************************************************************************************
*	函 数 名: Key2_Scan
*	功能说明: 扫描按键2的状态
*	形    参: 无
*	返 回 值: 按键2的状态
*********************************************************************************************************
*/
static int Key2_Scan(void)
{
    return gpio_get_level(2);
}

/*
*********************************************************************************************************
*	函 数 名: Key1_Init
*	功能说明: 初始化按键1的配置
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
static void Key1_Init(void)
{
    gpio_config_t io_conf = {
        .intr_type = GPIO_INTR_DISABLE, //falling edge interrupt
        .mode = GPIO_MODE_INPUT, //set as input mode
        .pin_bit_mask = 1<<GPIO_NUM_9, //bit mask of the pins GPIO9
        .pull_down_en = 0, //disable pull-down mode
        .pull_up_en = 1 //enable pull-up mode
    };
    //configure GPIO with the given settings
    gpio_config(&io_conf);
}

/*
*********************************************************************************************************
*	函 数 名: Key2_Init
*	功能说明: 初始化按键2的配置
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
static void Key2_Init(void)
{
    gpio_config_t io_conf2 = {
        .intr_type = GPIO_INTR_DISABLE, //falling edge interrupt
        .mode = GPIO_MODE_INPUT, //set as input mode
        .pin_bit_mask = 1<<GPIO_NUM_2, //bit mask of the pins GPIO9
        .pull_down_en = 0, //disable pull-down mode
        .pull_up_en = 1 //enable pull-up mode
    };
    //configure GPIO with the given settings
    gpio_config(&io_conf2);
}
/*************************************  END  *************************************/

/*
*********************************************************************************************************
*	函 数 名: bsp_Key_Init
*	功能说明: 初始化按键列表,设置扫描和初始化函数指针,初始化按键状态和计数器
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_Key_Init(void)
{
    g_Key_List[0].Scan = Key1_Scan;
    g_Key_List[0].Init = Key1_Init;
    g_Key_List[1].Scan = Key2_Scan;
    g_Key_List[1].Init = Key2_Init;

    for (uint8_t i = 0; i < KEY_NUM; i++)
    {
        g_Key_List[i].KeyCnt = 0;
        g_Key_List[i].IdleCnt = 0;
        g_Key_List[i].ClickCnt = 0;
        g_Key_List[i].LongPressed = 0;
        g_Key_List[i].State = 0;
    }

    g_Key_List[0].Init();
    g_Key_List[1].Init();
}

按键扫描代码:

/*
*********************************************************************************************************
*	函 数 名: bsp_key_scan
*	功能说明: 扫描按键状态,处理长按、短按和松手事件
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_key_scan(void)
{
    uint8_t KeyVal = 0x00;

    for(uint8_t i = 0; i < KEY_NUM; i++)
    {
        if (g_Key_List[i].Scan() == 0)
        {
            if (g_Key_List[i].KeyCnt < CLICK_COUNT_MAX)
                g_Key_List[i].KeyCnt++;
            
            /* 长长按 */
            if (g_Key_List[i].KeyCnt > LONG_PRESS_THRESHOLD && !g_Key_List[i].LongPressed)
            {
                KeyVal = i * 8 + KEY1_LONGLONG;
                g_Key_List[i].State = KeyVal;
                // printf("长长按, KeyVal = 0x%x\r\n", KeyVal);
                xQueueSend(gpio_evt_queue, &KeyVal, NULL);
                g_Key_List[i].LongPressed = 1;
                g_Key_List[i].KeyCnt = 0;
            }
            g_Key_List[i].IdleCnt = 0;
        }
        else
        {
            /* Release */
            if (g_Key_List[i].State != i*8)
            {
                if (g_Key_List[i].KeyCnt == 0)
                {
                    KeyVal = i * 8;
                    g_Key_List[i].State = i*8;
                    xQueueSend(gpio_evt_queue, &KeyVal, NULL);
                    // printf("Release\r\n");
                }
                else
                    g_Key_List[i].KeyCnt--;
            }

            if (g_Key_List[i].LongPressed)
            {
                g_Key_List[i].KeyCnt = 0;
                g_Key_List[i].LongPressed = 0;
                g_Key_List[i].ClickCnt = 0;
            }

            /* 单击、多击松手 */
            if (g_Key_List[i].IdleCnt++ >= IDLE_COUNT_THRESHOLD)
            {
                g_Key_List[i].KeyCnt = 0;

                if (g_Key_List[i].ClickCnt)
                {
                    KeyVal = i * 8 + KEY1_MULTICLICKED_1 - 1 + g_Key_List[i].ClickCnt;
                    g_Key_List[i].State = KeyVal;
                    xQueueSend(gpio_evt_queue, &KeyVal, NULL);
                    // printf("按键%d击, KeyVal = 0x%x\r\n",g_Key_List[i].ClickCnt,KeyVal);
                }
                g_Key_List[i].ClickCnt = 0;
                g_Key_List[i].IdleCnt = 0;
            }
            /* long Press */
            if (g_Key_List[i].KeyCnt > CLICK_LONG_THRESHOLD)
            {
                KeyVal = i * 8 + KEY1_LONGCLICKED;
                g_Key_List[i].State = KeyVal;
                xQueueSend(gpio_evt_queue, &KeyVal, NULL);
                g_Key_List[i].KeyCnt = 0;
                // printf("按键长按, KeyVal = 0x%x\r\n", KeyVal);
                g_Key_List[i].ClickCnt = 0;
            }
            /* 单击、多击 */
            else if (g_Key_List[i].KeyCnt <= CLICK_LONG_THRESHOLD && g_Key_List[i].KeyCnt >= CONFIRM_COUNT)
            {
                g_Key_List[i].ClickCnt++;
                g_Key_List[i].KeyCnt = 0;
            }
        }
    }
}

代码中使用 xQueueSend(gpio_evt_queue, &KeyVal, NULL) 发送键值至其他线程,在裸机中使用可以替换为向自己的队列FIFO写键值。

示例:

扫描线程,周期2ms,在裸机环境中可使用systick定时器,在systick中断中调用bsp_key_scan函数。

void key_task_example(void* arg)
{
    for(;;)
    {
        bsp_key_scan();
        vTaskDelay(2);
    }
}
static void gpio_task_example(void* arg)
{
    uint32_t KeyVal;

    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &KeyVal, portMAX_DELAY)) 
        {
            switch (KeyVal)
            {
                case KEY1_RELEASE :
                    printf("Release\r\n");
                break;

                case KEY1_LONGCLICKED :
                    printf("KEY1_LONGCLICKED\r\n");
                break;

                case KEY1_LONGLONG :
                    printf("KEY1_LONGLONGCLICKED\r\n");
                break;

                case KEY1_MULTICLICKED_1 :
                    printf("KEY1_MULTICLICKED_1\r\n");
                break;

                case KEY1_MULTICLICKED_2 :
                    printf("KEY1_MULTICLICKED_2\r\n");
                break;

                case KEY1_MULTICLICKED_3 :
                    printf("KEY1_MULTICLICKED_3\r\n");
                break;                     
                
                case KEY1_MULTICLICKED_4 :
                    printf("KEY1_MULTICLICKED_4\r\n");
                break;                     
                
                case KEY1_MULTICLICKED_5 :
                    printf("KEY1_MULTICLICKED_5\r\n");
                break;                

                default: break;
            }
        }
    }
}

运行结果:

ESP32的长按、短按、双击、多击 按键检测实现 乐鑫官方有个仓库叫做esp-iot-solution,里面有很多常用外设的驱动和物联网场景的实现代码。其中就有一个button模块来实现按键的长按、短按检测: espressif/esp-iot-solution › components › general › button › button。 README翻译如下: 本模块封装了一个按键对象 一个按键设备定义如下: GPIO 端口号 信号有效电平 触发模式:触发模式决定了是否调用连续触发 多少秒后开始进行进行连续触发 一个按键设备提供如下 阅读详情

相关推荐

【ESP32】打造全网最强esp-idf基础教程——5按键值获取逻辑

大家在使用一些智能家居家电时,可能都会有那么几个按键,然后看操作说明,按键的短按长按功能是不一样的,短按的话基本上是执行标准功能,比如说开灯关灯,那么长按可能就是执行一些不常用的功能了,比如说配网。因此按键的处理对物联网开发来说是必备的基础技能,要说简单,也很简单,要玩出花样,也能玩出很多花样。本例程中会对单个按键实现长按和短按功能的划分,同时也可以扩展出多个按键的应用。原理图如下

Hello_kitty_ling的博客 2447

ESP32按键长短按识别

记录定时器中断中的电平为低的次数,大于两次为长按(即按下时间超过1/2秒),小于两次为短按(即按下时间小于1/2秒);将GPIO中断设置为CHANGE模式(即电平改变时触发),在回调函数中判断电平的高低;利用GPIO中断检测按下抬起,定时器计次,实现长短按识别。

weixin_54516393的博客 654

智能家居按钮开发实战:用ESP32实现单击开关灯/长按调亮度(基于FreeRTOS定时器)

本文详细介绍了基于ESP32和FreeRTOS定时器开发智能家居物理按钮的实战过程。通过乐鑫button组件实现精准的按键检测,区分单击开关灯与长按调亮度,并解决了在定时器回调中安全控制PWM调光、避免系统卡死的核心问题。文章涵盖了环境配置、组件使用、任务间通信设计及高级调试技巧,为物联网设备交互开发提供了稳定可靠的解决方案。

weixin_29179311的博客 551

2·ESP32-C3入门教程——按键基本法

物联网开发从入门到进阶,怎么入坑esp32?以可视化的点灯和按键,快速学习最基本的 input 和 output 的概念

TenYao_的博客 4962

【SoC】【ESP32】基于VSCode+ESP-IDF插件构建多任务LED控制示例工程

本文详细介绍了如何使用VSCode和ESP-IDF插件搭建ESP32开发环境,并构建一个多任务LED控制示例工程。通过创建自定义LED驱动组件,并实践FreeRTOS的动态与静态任务创建方法,让三个LED以不同频率独立闪烁。文章涵盖了环境配置、SDK设置、代码编写、编译烧录及调试的全过程,是学习ESP32多任务编程的实用入门指南。

weixin_29194255的博客 702

【打卡帖】7日玩转ESP32——(第2日) GPIO输入,按键的长按和短按

开发板上面有一个Boot Button按键,今日课程就是基于这个按键,实现GPIO输入,按键的长按和短按的功能。 二、软件准备 ESP-IDF 中有一个GPIO例程,实现的是GPIO输入输出控制。基于此,实现我们今日的课程功能。 三、今日作业 基于ESP32-C3-DevKitM开发板 按键短按,计数值自增并输出打印 按键长按,计数值清零并输出打印 作业完成后,别忘了打卡~ 完成打卡的每人可有新程序员杂志。并且根据完成质量和打卡时间,评选

6531

ESP-IDF使用iot-button组件实现按键检测的功能

按键组件实现了 GPIO 和 ADC 两种按键,并允许同时创建两种不同的按键。可以检测按键不同的触发事件

荻夜的博客 4394

ESP32S3电池电量监测实战:从分压电路到IDF组件配置(附GitHub源码)

本文详细介绍了ESP32S3电池电量监测的完整实战方案。从硬件分压电路的电阻选型、滤波设计,到软件层面利用ESP-IDF的ADC校准机制提升精度,并重点讲解了如何集成bat_monitor开源组件快速实现电压采集、电量计算、充电检测与事件通知。文章提供了从原理到调试的完整指南和GitHub源码,帮助开发者构建稳定可靠的物联网设备电源管理系统。

ee345的博客 531

基于FreeRTOS的ESP-IDF开发——5.使用按键[不带消抖、带消抖、长按短按识别]

基于FreeRTOS的ESP-IDF开发——5.使用按键[不带消抖、带消抖、长按短按识别]

qq_53381910的博客 3949

基于Simulink的防空导弹六自由度弹道建模与仿真实战

本文详细介绍了基于Simulink的防空导弹六自由度弹道建模与仿真方法。通过分析导弹动力学原理、Simulink环境配置、气动力建模、推进与控制系统设计等关键环节,提供了一套完整的实战指南。文章特别强调了六自由度模型在防空导弹仿真中的重要性,并分享了常见问题排查与优化建议,为相关领域工程师提供实用参考。

apple5的专栏 868

Vercel静态站被墙别慌!手把手教你用Cloudflare免费CDN+域名解析搞定DNS污染

本文详细介绍了如何利用Cloudflare免费CDN和域名解析解决Vercel静态网站遭遇DNS污染的问题。通过诊断DNS污染、设计Cloudflare解决方案架构、实战配置步骤以及高级调优技巧,帮助用户快速恢复站点访问并提升防护能力。

weixin_27442001的博客 703

VScode+ESP32快速安装ESP-IDF插件

在VS code 软件中快速安装ESP32插件,使用ESP-IDF开发ESP32

hwd00001的博客 5735

避坑指南:Windows11语言包安装后部分界面仍显示中文的终极解决方案

本文详细解析了Windows11语言包安装后部分界面仍显示中文的问题,提供了从基础到进阶的完整解决方案。通过PowerShell命令、注册表清理和系统文件检查等方法,帮助用户彻底解决语言显示不一致的问题,确保系统界面语言统一。

weixin_29232507的博客 1639

ESP32 ESP-IDF组件学习之Button组件

本文介绍了ESP32 ESP-IDF框架中的Button组件使用方法。主要内容包括:1)通过VS Code安装Button组件;2)示例代码展示GPIO按键初始化、事件回调注册及处理;3)组件功能详解,包括按键消抖、多种事件检测(按下/释放/长按等)和低功耗支持;4)关键配置参数说明及常见问题排查方法。该组件可简化GPIO按键开发,提供50ms默认消抖时间,支持10种事件类型检测,并能与esp_sleep模块配合实现低功耗唤醒功能。

本博客分享软硬件一体化开发实战。硬件涵盖 PCB 设计、STM32 等 MCU 底层开发;软件包含 C++、Rust,Qt/QML、PyQt、Slint、TouchGFX、LVGL 各类 GUI 框架。方便软硬件开发者查阅学习。欢迎关注! 783

淘宝母婴用品数据挖掘及可视化分析研究

1. 背景介绍 在当今的电子商务时代,母婴用品市场已经成为了各大电商平台的一片蓝海。淘宝作为中国最大的电子商务平台,其上的母婴用品交易数据自然引人关注。理解这些数据,可以帮助商家更好地制定营销策略,也可以帮助消费者更好地选择产品。因此,本文将对淘宝母婴用品的数据进行深入的挖掘和分析。 1.1 电子商务的

AI天才研究院 1258

【YOLOv8改进-注意力机制】BRA(bi-level routing attention ):双层路由注意力

【YOLOv8改进-注意力机制】BRA(bi-level routing attention ):双层路由注意力我们提出了一种新颖的动态稀疏注意力机制,通过双层路由实现更加灵活且具有内容感知的计算分配。

专注于图像领域,主要研究内容包括计算机视觉和深度学习,特别是在图像分类、目标检测和图像生成等方面有深入的研究和实践经验。 3171

esp32课设记录(一)按键的短按、长按与双击

因此,总体逻辑应该是这样的:首先是空闲状态。当按钮按下,进入按下状态。当按钮松开后,测量按下的时间,到达阈值认为触发长按事件,直接进入空闲状态,防止长按后还触发双击事件。没有到达阈值则进入释放状态。此时等待一段时间,在此时间内按键再次按下,判定触发双击事件,当释放后进入空闲状态。未按下则判定进入单击事件,随后进入空闲状态。

qq_23220445的博客 1332

Debezium报错处理系列之二十八: Expiring 1 record(s) for test-ts-0:120001 ms has passed since batch creation

Debezium报错处理系列之二十八: Expiring 1 record(s) for test-ts-0:120001 ms has passed since batch creation

zhengzaifeidelushang的博客 3290

【普中ESP32S3开发攻略--基于ESP-IDF】-- 第 8 章 按键控制实验

本章我们通过按键实验来介绍IO 口作为输入的使用。

普中科技的博客 1371

OpenClaw 子代理配对失败问题分析与解决方案

OpenClaw子代理配对失败问题分析与解决方案 问题核心在于agent设备权限不足,仅有operator.approvals权限而缺少operator.admin权限,导致子代理启动时形成权限死锁。本文提供三种解决方案: 直接编辑设备配对文件(推荐):修改本地JSON文件,为agent设备添加完整operator.admin权限,操作简单快捷。 标准重新配对流程:删除现有agent设备后重新配对,通过正常审批流程获取完整权限,符合系统安全设计。 修改源码降低权限要求(仅限开发环境):调整子代理代码避免强制

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 150

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

ESP32按键检测中断方式(8)

在这个实验中,我们设置的是 IO0 是边沿触发中断,也就是低电平变成高电平或者高电平变成低电平 都会产生中断。 在程序中,我们是按以下方法检测的: 按键按下:由高电平变成低电平。 按键弹起:由低电平变成高电平。 长按键:按键按下到弹起之间的时间大于 1 秒。 短按键:按键按下到弹起之间的时间小于 1 秒。 ESP32 的 ESP-IDF 编程指南可以从官网上查询: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/index.html (1)

bytechip的博客 3281

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

VSCode + ESP-IDF使用ESP-Prog进行JTAG调试ESP32S3

看了网上好多文章和视频,关于写这个调试详细的并不多,有也是需要付费观看的,很是扯淡恶心,某某文章区区调试文还要收费39块!被恶心到了,于是自己摸索了一天最终搞了出来,秉着共享的心态分享出来,理解有偏驳,有不对的地方还请多多指正!!!

qq_40944005的博客 1983

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

ESP32 ESP-IDF GPIO按键中断响应

此示例显示了如何配置GPIO以及如何在中断时使用它

晨之清风 3951

ESP-IDF button 组件介绍

​乐鑫推出 button 组件 —— 一个功能完善且稳定可靠的 ESP-IDF 按钮驱动组件,可帮助您在项目中更轻松地实现按钮处理

乐鑫 Espressif 529

ESP32 ESP-IDF GPIO使用详解

本文介绍了ESP32在ESP-IDF环境下GPIO的基本操作与配置方法。主要内容包括:1)GPIO简介及初始化配置,涵盖输入/输出模式、上下拉电阻设置;2)电平设置与读取操作;3)中断使用方法,包括中断类型配置和服务安装;4)驱动能力调整和输入防抖动处理;5)使用注意事项,如引脚复用、RTC GPIO等特殊功能。文章还提供了LED闪烁的代码示例框架(代码不完整),并建议参考官方文档获取最新信息。适用于ESP32开发者快速掌握GPIO基础操作。

本博客分享软硬件一体化开发实战。硬件涵盖 PCB 设计、STM32 等 MCU 底层开发;软件包含 C++、Rust,Qt/QML、PyQt、Slint、TouchGFX、LVGL 各类 GUI 框架。方便软硬件开发者查阅学习。欢迎关注! 1376

【ESP32-IDF+VScode】开发笔记(二):按键输入——按键控制LED状态

本文章通过按键控制LED亮灭的一个小工程实例,来对ESP32的ESP-IDF+VSCode开发环境进行熟悉,并讲解GPIO输入相关配置和操作。

weixin_43803230的博客 2523
上一篇: LVGL-圆弧
下一篇: LVGL slider添加图片
whIIe
博客等级 码龄6年 27粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值