按键消抖还在 delay?难怪你按一下,STM32 像抽风一样连触发
你是不是也遇到过这种情况明明只按了一下按键LED 却闪了好几次。菜单本来只想往下走一格结果一下跳了两三格。更烦的是项目里想做“短按切换模式长按进入设置”结果短按也触发长按也触发程序像抽风一样。很多初学者第一反应就是那我 delay 一下不就好了比如这样写if(KEY0){delay_ms(20);if(KEY0){LED!LED;}}这段代码看起来没问题。点灯实验里也确实能用。但我想说一句按键消抖不是简单 delay 一下。01 按键为什么会抖机械按键不是理想开关。你手指按下去的一瞬间触点不是立刻稳定接通而是会在很短时间内来回抖几下。也就是说单片机看到的电平可能是这样的1 1 0 1 0 0 1 0 0 0你以为自己只按了一次。但单片机可能以为你按了好几次。尤其是 STM32 这种主频很高的芯片几毫秒时间里它已经循环检测很多次 GPIO 了。所以按键乱触发不一定是板子坏了。很多时候是代码把“电平抖动”当成了“有效按键”。02 delay 消抖最大的问题是什么delay 消抖不是完全不能用。如果你只是写个点灯实验问题不大。但一到真实项目里它就容易出事。比如你的程序还要做这些事情串口接收数据 OLED 刷新显示 ADC 采样 PWM 输出 FreeRTOS 任务调度 通信协议解析这时候你按一下按键程序直接卡在 delay_ms(20)。如果用户连续按几次系统就会一卡一卡。串口数据可能丢。屏幕刷新可能变慢。通信响应可能超时。所以 delay 消抖的问题不是“不准”而是太粗暴。项目里不怕代码简单就怕代码简单到把系统堵住。03 更靠谱的办法定时扫描比较推荐的做法是每隔固定时间扫描一次按键比如 10ms 扫描一次。不是一读到按下就马上处理。而是连续几次都读到按下才确认按键真的按下了。比如#defineKEY_DOWN_LEVEL0#defineKEY_SCAN_TIME10#defineKEY_FILTER_CNT3uint8_tkey_cnt0;uint8_tkey_state1;uint8_tkey_event0;voidKey_Scan(void){if(KEY_READ()KEY_DOWN_LEVEL){if(key_cntKEY_FILTER_CNT){key_cnt;}if(key_cntKEY_FILTER_CNTkey_state1){key_state0;key_event1;// 按键按下事件}}else{key_cnt0;key_state1;}}这段代码的意思很简单。如果连续 3 次都检测到按下才认为按键真的按下。如果 10ms 扫描一次连续 3 次就是大约 30ms。这样大部分机械抖动就被过滤掉了。关键是这个过程不需要 delay 卡住程序。你可以把Key_Scan()放到定时器中断里也可以放到 10ms 周期任务里。主循环只处理事件if(key_event){key_event0;LED!LED;}这样写程序就清爽很多。04 短按和长按别写乱了很多人短按、长按分不清是因为逻辑写错了。最常见的错误是按下瞬间就触发短按按久了再触发长按。这样当然会冲突。因为刚按下的时候你根本不知道用户是想短按还是想长按。正确思路应该是按下后开始计时 超过一定时间触发长按 如果没超过长按时间就松开触发短按比如#defineLONG_PRESS_TIME80// 80 * 10ms 800msuint16_tpress_time0;uint8_tshort_event0;uint8_tlong_event0;uint8_tlong_flag0;voidKey_Process(void){if(key_state0)// 已确认按下{if(press_timeLONG_PRESS_TIME){press_time;}elseif(long_flag0){long_flag1;long_event1;// 长按事件}}else{if(press_time0press_timeLONG_PRESS_TIME){short_event1;// 松开时判断短按}press_time0;long_flag0;}}这里有个重点短按一般在松开时判断不是在按下时判断。这句话很重要。很多项目里按键逻辑乱就是因为短按触发太早。05 项目里建议这样分层不要在业务代码里到处读 GPIO。比如菜单程序里不要这样写if(KEY10){menu;}这样后面一旦要加消抖、长按、组合键代码会越来越乱。更好的方式是底层负责读取 GPIO 按键模块负责消抖、短按、长按 业务逻辑只处理按键事件比如主程序只关心if(short_event){short_event0;menu;}if(long_event){long_event0;enter_setting_mode();}这样代码就舒服多了。菜单不用关心按键有没有抖。业务不用关心低电平有效还是高电平有效。以后换按键、改消抖时间、加长按重复触发也不会把整个工程改乱。06 最后记住一句话按键消抖看起来是小问题。但它考验的是嵌入式编程思维。GPIO 读到的是电平。用户操作才是事件。不要一看到低电平就执行。不要动不动就 delay 堵住程序。真正适合项目的按键写法应该是定时扫描 消抖计数 状态判断 事件输出下次你的 STM32 按键又出现“按一次触发多次”“短按长按分不清”的问题别急着怀疑硬件。先看看代码是不是还停留在按下-delay-执行很多按键翻车现场问题就出在这里。如果这篇文章对你有帮助建议收藏起来转发给正在学 STM32 的朋友。也欢迎留言说说你项目里遇到过哪些离谱的按键问题。