按键检测与消抖 按键检测是做项目时经常遇到的问题很多时候缺乏经验容易顾此失彼。个人梳理了一下技术点水平有限不足之处希望指出希望能和大家共同进步。1.按键检测我们这里以默认低电平按键按下读取到高电平来叙述按键按下检测比较稳妥的是边沿读取即读取电平之间的切换单纯的读取电平容易受肌肉抖动影响故现在按下读取应该读取低电平变成高电平这一过程uint8_t key_last 0;//上次电平 uint8_t key_now 0; if(key_last 0 key_now 1) { }2.按键消抖手指按下的时候会有抖动所以应该消抖消抖延时时间一般在20ms左右。注意这里最好采用非阻塞延时否则会影响外设上的其他功能tick放在中断中执行我这里中断是1ms执行1次根据需要调整就可以。另外MS_TO_TICK(ms)可以根据需要灵活调整#define MS_TO_TICK(ms) ((ms) * 1) uint32_t data tick 0; /* 系统时间戳单位1ms */ uint32_t xdata data_return 0; uint16_t xdata now; /* 判断是否过了指定毫秒数 */ uint8_t elapsed_ms(uint32_t start, uint32_t ms) { now tick; if(now start) { data_return now - start; } if(now start) { data_return 0xFFFF - start now 1; } return (data_return MS_TO_TICK(ms)) ? 1 : 0; }3.单击和长按的功能逻辑大家最想看的还是整体代码这里采用switch-case的结构有些地方应该没有定义在上面定义一下即可typedef enum { KEY_IDLE, KEY_DEBOUNCE, KEY_PRESS, KEY_HOLD } State; uint8_t Key_Scan(void) { uint8_t key_now KEY_PIN; switch(State) { case KEY_IDLE: { // 上升沿检测按下 if(key_last 0 key_now 1) { debounce_tick tick; state KEY_DEBOUNCE; } } break; case KEY_DEBOUNCE: { // 必须保持稳定高电平 if(key_now 1) { if(elapsed_ms(debounce_tick, 20)) { press_tick tick; state KEY_PRESS; } } else { state KEY_IDLE; } } break; case KEY_PRESS: { // 下降沿松开 if(key_last 1 key_now 0) { state KEY_IDLE; if(!long_flag) { key_last key_now; return KEY_SHORT; } } // 长按判断持续状态不依赖边沿 if(elapsed_ms(press_tick, LONG_PRESS_MS)) { long_flag 1; return KEY_LONG; } } break; } key_last key_now; return KEY_NONE; }4.功能拓展其实还可以在这个架构上增加例如双击之类的功能如果有需要可以自己尝试增加功能代码仅供参考如有问题欢迎提出