按键检测与消抖
·
按键检测是做项目时经常遇到的问题,很多时候缺乏经验容易顾此失彼。个人梳理了一下技术点,水平有限,不足之处希望指出,希望能和大家共同进步。
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.功能拓展
其实还可以在这个架构上增加例如双击之类的功能,如果有需要可以自己尝试增加功能,代码仅供参考,如有问题欢迎提出
更多推荐


所有评论(0)