[i=s] 本帖最后由 SpiritSong 于 2026-9-14 22:38 编辑 [/i]
按键扫描实现
按键状态机是嵌入式系统中替代阻塞式延时消抖的工业级方案,核心是将按键的物理交互过程抽象为离散的有限状态集合,所有状态迁移由确定性条件驱动,全程不阻塞CPU主流程,可同时支持短按、长按、双击等复杂交互逻辑。
核心设计优势
- 完全摒弃
HAL_Delay()阻塞消抖,不影响系统其他外设的实时响应
- 单定时器资源即可驱动任意数量按键,无需为每个按键分配独立外部中断
- 状态逻辑完全解耦,新增双击、连发等交互模式无需重构核心代码
- 彻底规避机械按键5~20ms弹跳引发的误触发、重复触发问题
标准状态定义
工业级按键状态机的核心状态共6个,每个状态对应明确的物理语义与工程目的:
| 状态名称 |
物理含义 |
核心作用 |
| KEY_IDLE(空闲) |
按键处于稳定释放态,无任何动作 |
所有状态迁移的起点,确保系统有明确初始态 |
| KEY_DEBOUNCE_DOWN(下降沿消抖) |
首次检测到高→低电平跳变,启动消抖验证 |
过滤按下瞬间的机械弹跳,排除虚假触发 |
| KEY_PRESSED(已确认按下) |
消抖完成且按键仍保持低电平 |
作为短按、长按计时的统一入口 |
| KEY_LONG_PRESSED(长按态) |
持续按下时长超过预设阈值(如800ms) |
触发长按专属动作,支持周期性连发回调 |
| KEY_DEBOUNCE_UP(上升沿消抖) |
首次检测到低→高电平跳变,启动释放消抖 |
过滤释放瞬间的弹跳,防止假释放误判 |
| KEY_RELEASED(已确认释放) |
释放消抖完成且按键稳定高电平 |
触发短按动作,返回IDLE态等待下一次操作 |
确定性状态迁移规则
所有状态跳转必须由「当前状态 + 输入事件」的原子条件驱动,无模糊逻辑:
- KEY_IDLE → KEY_DEBOUNCE_DOWN:检测到GPIO下降沿,启动20ms消抖计时
- KEY_DEBOUNCE_DOWN → KEY_PRESSED:20ms计时结束,期间电平始终为低;若中途电平跳回高则直接退回IDLE
- KEY_PRESSED → KEY_LONG_PRESSED:进入PRESSED态后计时超过长按阈值,且按键仍保持按下
- KEY_PRESSED → KEY_DEBOUNCE_UP:检测到GPIO上升沿,直接进入释放消抖流程
- KEY_LONG_PRESSED → KEY_DEBOUNCE_UP:长按过程中检测到上升沿,进入释放消抖
- KEY_DEBOUNCE_UP → KEY_IDLE:20ms释放消抖完成,期间电平始终为高,触发短按事件
工程实现关键细节
- 采样策略:推荐在SysTick中断中以1ms间隔采样GPIO电平,既完整覆盖5~20ms的抖动窗口,又不会过度占用CPU资源
- 边沿检测:状态迁移依赖电平跳变的边沿而非绝对电平,需维护上一次采样值与当前值做对比,避免长按期间反复触发事件
- 时间基准:所有计时基于系统
HAL_GetTick()的时间戳差值计算,不依赖绝对延时,确保时序精度不受主循环负载影响
- 事件输出:短按、长按事件以标志位形式输出给上层应用,状态机核心逻辑中不直接执行业务动作,保证驱动层与业务层完全解耦
典型硬件适配要点
- 按键采用内部上拉输入模式,默认未按下时GPIO为高电平,按下时接地为低电平,无需额外外部上拉电阻
- 若硬件采用下拉接法,只需将电平判断逻辑整体翻转,状态机核心迁移逻辑无需修改
- 多按键场景下,为每个按键分配独立的状态变量与计时参数,复用同一套状态机处理函数即可,无需重复编写代码