首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何在STM32嵌入式开发中优雅地处理按键(单击、双击、长按)?

如何在STM32嵌入式开发中优雅地处理按键(单击、双击、长按)?

作者头像
不脱发的程序猿
发布于 2025-02-10 12:26:11
发布于 2025-02-10 12:26:11
1.2K06
代码可运行
举报
运行总次数:6
代码可运行

要优雅地处理按键的单击、双击和长按事件,关键在于:

  • 使用去抖动技术,确保每次按键状态的变化都可靠。
  • 通过定时器或者系统时钟来判断按键按下的持续时间和时间间隔。
  • 使用状态机或者标志位来处理不同的按键事件,确保按键事件的识别不被误触发。

为了避免过度复杂化,务必保持代码清晰易读,适当的时间阈值和状态切换逻辑非常重要。

通过这些方法,我们能够有效而优雅地处理按键事件,提升用户交互体验。

下面从硬件和软件两个层面给出详细的解决方案:

1、按键去抖动

按键在物理层面上具有机械抖动特性,即按下或松开时会产生多次的电平波动,导致微控制器读取到多个错误的状态变化。

解决方案通常有两种:

1.1 软件去抖动

通过延时去抖动是最简单的方式,即在检测到按键状态变化时,等待一个小的时间间隔,再读取按键状态。

常用的做法是:设置一个去抖动的时间窗口(比如20-50ms),在按键状态变化时,程序等待一段时间后再读取按键状态。

比如,假设按键按下时,判断是否按键稳定,并防止过早检测到重复变化。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define DEBOUNCE_TIME 50  // 去抖动时间(单位:ms)

volatile uint32_t lastDebounceTime = 0;
uint8_t lastButtonState = 0;

void ButtonHandler(void) {
    uint8_t currentButtonState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
    if (currentButtonState != lastButtonState) {
        lastDebounceTime = HAL_GetTick();
    }

    if ((HAL_GetTick() - lastDebounceTime) > DEBOUNCE_TIME) {
        if (currentButtonState != lastButtonState) {
            lastButtonState = currentButtonState;
            if (currentButtonState == GPIO_PIN_SET) {
                // 处理按键按下逻辑
            } else {
                // 处理按键松开逻辑
            }
        }
    }
}

1.2 硬件去抖动

硬件去抖动可以通过RC滤波器(电阻和电容)来实现,使用硬件设计的方式来滤除按键抖动信号,这种方法可以减少CPU负担。

2、按键事件处理

一旦解决了去抖动问题,接下来就是根据不同的按键模式(单击、双击、长按)来识别和响应按键事件。

我们可以通过计时器和状态机来实现。

2.1 单击检测

单击是指按键被快速按下和松开。为了检测单击,通常我们通过检测按键按下事件,并在一定的时间内等待松开。

如果按键按下和松开之间的时间小于某个阈值,我们认为是单击。

2.2 双击检测

双击是指按键被连续点击两次,通常要求两次按下和松开之间的时间小于某个阈值,且两次按下事件之间的时间间隔小于一定时间。

2.3 长按检测

长按是指按键保持按下超过某个阈值,通常用定时器来检测按下时间。

2.4 设计思路

我们通过一个简单的状态机来控制不同的按键模式,结合定时器来实现按键的时序逻辑。

主要流程如下:

  • 使用一个定时器(如HAL定时器)来记录按键按下和松开的时间。
  • 设定超时时间来区分不同类型的按键事件。
  • 使用状态机或标志位来判断是单击、双击还是长按。

按键事件管理流程:按下按键时,记录当前时间(按下时间戳)。

松开按键时,计算按下与松开的时间差:

  • 如果时间差小于某个阈值(例如500ms),则是单击事件。
  • 如果按下与松开之间的时间差小于双击阈值(例如300ms),则判定为双击。
  • 如果按下时间超过某个阈值(例如1500ms),则判定为长按。

双击检测需要检查两个按下事件之间的时间间隔是否小于一个设定的时间(例如300ms)。

示例代码设计:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define SINGLE_CLICK_TIME    300  // 单击最大时间间隔(ms)
#define DOUBLE_CLICK_TIME    600  // 双击最大时间间隔(ms)
#define LONG_PRESS_TIME      1500 // 长按时间(ms)

typedef enum {
    BUTTON_IDLE,
    BUTTON_PRESSED,
    BUTTON_RELEASED,
    BUTTON_DOUBLE_CLICK_WAIT
} ButtonState;

volatile ButtonState buttonState = BUTTON_IDLE;
volatile uint32_t buttonPressTime = 0;
volatile uint32_t buttonReleaseTime = 0;

void ButtonHandler(void) {
    uint8_t currentButtonState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);

    switch (buttonState) {
        case BUTTON_IDLE:
            if (currentButtonState == GPIO_PIN_RESET) {  // 按键按下
                buttonPressTime = HAL_GetTick();
                buttonState = BUTTON_PRESSED;
            }
            break;

        case BUTTON_PRESSED:
            if (currentButtonState == GPIO_PIN_SET) {  // 按键松开
                buttonReleaseTime = HAL_GetTick();
                uint32_t pressDuration = buttonReleaseTime - buttonPressTime;

                if (pressDuration < SINGLE_CLICK_TIME) {
                    // 单击事件
                    HandleSingleClick();
                } else if (pressDuration >= LONG_PRESS_TIME) {
                    // 长按事件
                    HandleLongPress();
                } else {
                    // 进入双击等待状态
                    buttonState = BUTTON_DOUBLE_CLICK_WAIT;
                }
            }
            break;

        case BUTTON_DOUBLE_CLICK_WAIT:
            if (currentButtonState == GPIO_PIN_RESET) {
                uint32_t currentTime = HAL_GetTick();
                if (currentTime - buttonReleaseTime < DOUBLE_CLICK_TIME) {
                    // 双击事件
                    HandleDoubleClick();
                    buttonState = BUTTON_IDLE;  // 处理完后回到初始状态
                } else {
                    buttonState = BUTTON_IDLE;  // 超过双击最大时间间隔,回到初始状态
                }
            }
            break;
    }
}

void HandleSingleClick() {
    // 处理单击事件
}

void HandleDoubleClick() {
    // 处理双击事件
}

void HandleLongPress() {
    // 处理长按事件
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 美男子玩编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
基于FreeRTOS车载音响Source切换与诊断功能实现
基于FreeRTOS设计汽车音响的Source切替和Diag功能,需要合理划分任务、充分利用实时操作系统的特性,确保系统的响应速度和稳定性。
不脱发的程序猿
2025/04/21
1340
基于FreeRTOS车载音响Source切换与诊断功能实现
嵌入式C语言实现事件型按键驱动
本项目是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。 项目资源下载请参见:https://download.csdn.net/download/m0_38106923/87537836
不脱发的程序猿
2023/07/13
5360
STM32按键状态机3——增加双击与功能优化
上篇文章:STM32按键状态机2——状态简化与增加长按功能,介绍了将按键检测增加长按功能,并将按下抖动与松开抖动共用一个抖动状态来表示,其状态图如下:
xxpcb
2022/10/04
1.4K0
STM32按键状态机3——增加双击与功能优化
面试加分项|一个优秀的按键驱动框架
按键是嵌入式产品中不可或缺的一部分,但往往受制于结构尺寸等因素,按键数量有限,如何利用有限的按键实现更多的骚操作,本文介绍一种优雅的按键实现方法,纯c语言实现,只需要与底层接口对接便可以轻松移植到嵌入式平台,实现单击、连击、短按、长按功能。
用户8913398
2021/08/13
5200
面试加分项|一个优秀的按键驱动框架
STM32F0x HAL库学习笔记(3)使用HAL库延时函数(HAL_Delay())[通俗易懂]
Systick(滴答时钟)是一个24位,向下计数的定时器,当倒计时完成后,定时器可以产生一个中断,所以,当频率一定,计数个数一定时,这个中断就会以一定的时间间隔发生,如果每个中断发送后调用的中断函数中给一个变量累加,这样我们就可以获得一个与时间相关的变量。有关于滴答时钟相关知识,官方手册和网上已经有非常多的篇幅介绍讲解,这里不再赘述。
全栈程序员站长
2022/08/28
7.7K0
第1期 | MultiButton,一个小巧简单易用的事件驱动型按键驱动模块
本专栏由Mculover666创建,主要内容为寻找嵌入式领域内的优质开源项目,一是帮助开发者使用开源项目实现更多的功能,二是通过这些开源项目,学习大佬的代码及背后的实现思想,提升自己的代码水平,和其它专栏相比,本专栏的优势在于:
Mculover666
2020/07/16
1.5K0
第1期 | MultiButton,一个小巧简单易用的事件驱动型按键驱动模块
03-HAL库GPIO输入与多功能按键实现
源码地址:https://gitee.com/MR_Wyf/hal-cubemx-rt-thread.git
用户8913398
2022/11/16
1.2K0
03-HAL库GPIO输入与多功能按键实现
STM32高级开发——使用DFU方案
素材来源:https://blog.csdn.net/zhengyangliu123/article/details/78788815
李肖遥
2022/12/22
1.6K0
STM32高级开发——使用DFU方案
STM32CubeMX实战教程(三)——外部中断(中断及HAL_Delay函数避坑)
学单片机的,相信对中断的概念都已经了如指掌了,中断具体是什么我在这里也就不再详细说明,不懂的上网找找也一大堆。那么在介绍实验之前我先跟大家简单讲讲STM32当中的NVIC(嵌套向量中断控制器)
全栈程序员站长
2022/09/05
6.8K2
STM32CubeMX实战教程(三)——外部中断(中断及HAL_Delay函数避坑)
【STM32H7教程】第88章 STM32H7的SDMMC总线应用之SD卡移植FatFs文件系统
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第88章 STM32H7的SDMMC总线应用之SD卡移植F
Simon223
2021/01/26
3.6K0
【STM32H7教程】第88章       STM32H7的SDMMC总线应用之SD卡移植FatFs文件系统
【STM32F429开发板用户手册】第38章 STM32F429的FMC总线应用之是32路高速IO扩展
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第38章       STM32F429的FMC总线应用之是32路高速IO
Simon223
2020/08/06
1K0
开源按键组件MultiButton支持菜单操作(事件驱动型)
之前一个老友写的MultiButton开源按键组件的剖析讲解,它的设计思想简洁且高效,这篇文章我上周也分享出来给大家共同来学习了。
杨源鑫
2020/05/21
3.4K0
蓝桥杯嵌入式之SysTick和独立按键
SysTick是滴答定时器,在相应的时间间隔内对变量进行操作(通过执行SysTick中断函数实现)。所以在比赛和学习中被组合成准确延时函数。
用户5935416
2019/08/01
8410
蓝桥杯嵌入式之SysTick和独立按键
【STM32H7教程】第19章 STM32H7的GPIO应用之按键FIFO
完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第19章       STM32H7的GPIO应用之按键FIFO
Simon223
2019/05/19
1.8K0
【STM32H7教程】第19章    STM32H7的GPIO应用之按键FIFO
STM32定时器—PWM 输出
PWM(Pulse Width Modulation,脉冲宽度调制)。是一种利用微处理器的数字输出来对模拟电路进行控制的技术,广泛应用在测量、通信、功率控制等诸多领域。
韦东山
2022/05/09
1.8K0
STM32定时器—PWM 输出
驱动库分享整理(1)——用于单片机中的小巧多功能按键支持库
按键在嵌入式开发中,是比较重要的,也是常见的外设,因此,很有必要学习,也要掌握编写基础的按键驱动,通常最基本的情况下,都是使用状态机的框架来出来,因为尽管硬件电路上有滤波电路,但还是要软件滤波的。(软件滤波很多采集系统中都是用到的)。 在学习过程中,可以自己尝试编写简单的单机,组合按键单机等,锻炼思维。当然网上有很多的按键驱动库,值得收藏,其中思想是比较好的,可以学习其思路,同事也可以在了解功能后,移植到自己的项目中。
用户4645519
2022/05/09
1.4K0
【STM32H7】第7章 RL-TCPnet V7.X网络协议栈移植(RTX5)
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95243 第7章   RL-TCPnet网络协议栈移植(RTX5) 本章教程为大家讲
Simon223
2020/09/07
9910
【STM32H7】第7章   RL-TCPnet V7.X网络协议栈移植(RTX5)
【STM32H7教程】第94章 STM32H7的SPI总线应用之双机通信(DMA方式)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第94章       STM32H7的SPI总线应用之双机通信(DMA
Simon223
2022/05/10
2K0
【STM32H7教程】第94章    STM32H7的SPI总线应用之双机通信(DMA方式)
【STM32H7教程】第20章 STM32H7的GPIO应用之无源蜂鸣器
蜂鸣器主要有电磁式和电压式两种,而且都有无源蜂鸣器和有源蜂鸣器两类。开发板使用的是电磁式有源蜂鸣器,而有源和无源的区别是有源蜂鸣器内部自带振荡器,给个电压就能发声,但频率是固定的,只能发出一种声音,而无源蜂鸣器频率可控,给个方波才可以发声,并且根据不同频率发出不同的声音效果。
Simon223
2019/05/19
1.6K0
【STM32F407开发板用户手册】第18章 STM32F407的GPIO应用之跑马灯
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第18章       STM32F407的GPIO应用之跑马灯 本章教程为
Simon223
2020/07/15
7040
【STM32F407开发板用户手册】第18章       STM32F407的GPIO应用之跑马灯
推荐阅读
相关推荐
基于FreeRTOS车载音响Source切换与诊断功能实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验