想象一下,你正在用STM32做一个炫酷的电子项目,结果按键一按就“抽风”——单击变双击、长按不认账、组合键像在玩猜谜游戏……
为什么按键总让人抓狂?
答案很简单:机械抖动和状态管理!
今天,我们用一篇“说人话”的博客,教你如何用状态机 + 事件驱动,让STM32的按键像德芙一样丝滑!
(文末附完整代码和逻辑图,代码强迫症患者请直接划到底部)
按键的一生,就像一场精心编排的舞台剧:
[释放]
↓ 按下
[消抖] →(抖动?滚回去!)
↓ 确认
[按下] →(长按?搞事情!)
↓ 松手
[等待释放] →(双击?再来一次!)
// 事件编码:高8位是按键ID,低8位是事件类型
#define KEY_EVENT_ENCODE(key_id, event) (((key_id) << 8) | (event))
#define KEY_GET_ID(code) ((code) >> 8) // 解码:提取按键ID
#define KEY_GET_EVENT(code) ((code) & 0xFF) // 解码:提取事件类型
// 示例:KEY_ID_1的按下事件 → 0x0101
static void ProcessKeyFSM(uint8_t key_id) {
Key_CB_Type *cb = &g_key_cb[key_id];
uint8_t key_state = ReadKeyState(key_id);
uint16_t event = 0;
switch (cb->state) {
case KEY_STATE_RELEASED:
if (key_state) {
// 进入消抖状态,记录“案发时间”
cb->state = KEY_STATE_DEBOUNCE;
cb->press_start_time = g_key_time_cnt;
cb->click_count = 0; // 双击计数器归零
}
break;
case KEY_STATE_DEBOUNCE:
if ((g_key_time_cnt - cb->press_start_time) >= DEBOUNCE_TIME) {
if (key_state) {
// 确认有效按下,触发PRESS事件
event = KEY_EVENT_ENCODE(key_id, KEY_EVT_PRESS);
cb->state = KEY_STATE_PRESSED;
} else {
// 判定为“手抖”,退回释放状态
cb->state = KEY_STATE_RELEASED;
}
}
break;
// 更多状态处理...(完整代码见文末)
}
if (event) g_key_event_reg = event; // 更新全局事件寄存器
}
// 在main.c中
int main(void) {
HAL_Init();
SystemClock_Config();
Key_Init(); // 初始化按键GPIO
while (1) {
// 在1ms定时中断中调用Key_Scan()
uint16_t event = Key_GetEvent();
if (event != KEY_EVT_NONE) {
uint8_t key_id = KEY_GET_ID(event);
KeyEvent_Type evt_type = (KeyEvent_Type)KEY_GET_EVENT(event);
switch (evt_type) {
case KEY_EVT_SINGLE_CLICK:
LED_Toggle(); // 单击控制LED
break;
case KEY_EVT_LONG_PRESS:
Buzzer_Beep(); // 长按触发蜂鸣器
break;
// 更多事件处理...
}
}
}
}
drv_key.h
:点击查看完整头文件drv_key.c
:点击查看完整实现按键驱动设计,本质是对“时间”和“状态”的精准掌控。
希望这篇博客让你从“按键恐惧症”患者,变身“按键掌控者”!
最后送上一句程序员哲学:
“好的代码,就像爱情——不需要解释,但需要精心设计。” ❤️
评论区互动:
你的项目中最奇葩的按键BUG是什么?欢迎分享!
(点赞过100,下期揭秘《STM32按键防抖的十大骚操作》!)