一、按键的抖动
1.按键的定义和原因
按键抖动是由于机械按键在闭合和断开时,由于触点的弹性作用,会产生一系列的抖动现象。这种抖动对于人类来说几乎感觉不到,但对单片机来说,却是一个可以感应到的过程,且处理时间较长,通常在毫秒级。
2.按键抖动的影响
按键抖动会导致单片机在处理按键信号时出现误判,即一次按键操作可能被错误地识别为多次操作,从而导致误处理。例如,按键一次按下或释放可能被误认为是多次操作,进而引起系统的逻辑错误。
3.按键消抖的方法
为了消除按键抖动的影响,可以采取以下几种方法:
1)软件消抖:通过程序实现,主要有两种方法:延迟法和计数法。延迟法是在检测到按键状态变化后,延时一段时间再进行确认;计数法则是在检测到按键状态变化后,连续检测一定次数,如果状态没有发生变化,则确认为有效状态。
2)硬件消抖:通过电路实现,有多种方法。例如,应用施密特电路的回差特性配合积分电路实现按键消抖,或者应用锁存器的保持功能实现开关消抖。
3)综合消抖:结合软件和硬件两种方法来实现,以达到更好的消抖效果。
下面介绍软件消抖。
二、按键的软件消抖
一般有两种方法,1.延时几十毫秒之后再读。2.按键按下,延时10ms,再判断是否有中断,直到没中断。第 1 种方法太耗时,违背“中断要尽快处理”的原则,你的系统会很卡。在 GPIO 中断中并不立刻记录按键值,而是修改定时器超时时间,10ms 后 再处理。如果 10ms 内又发生了 GPIO 中断,那就认为是抖动,这时再次修改超时时间为 10ms。只有 10ms 之内再无 GPIO 中断发生,那么定时器的函数才会被调用。在定时器函数中记录按键值。
/* 定义一个软件定时器的结构体 */
struct soft_timer {
uint32_t timeout; // 定时器超时时间
void * args; // 传递给定时器函数的参数
void (*func)(void *); // 定时器超时后调用的函数
};
/* 全局变量 */
int g_key_cnt = 0; // 按键计数
/* 定义一个软件定时器实例,初始超时时间设置为最大(~0),无参数,超时函数为key_timeout_func */
struct soft_timer key_timer = {~0, NULL, key_timeout_func};
/* 定时器超时函数,每次调用时增加按键计数,并重置定时器超时时间为最大 */
void key_timeout_func(void *args)
{
g_key_cnt++;
key_timer.timeout = ~0;
}
/* 设置定时器超时时间 */
void mod_timer(struct soft_timer *pTimer, uint32_t timeout)
{
pTimer->timeout = HAL_GetTick() + timeout; // HAL_GetTick()获取当前系统时间,加上超时时间作为新的超时时间
}
/* 检查定时器是否超时,如果超时则调用超时函数 */
void check_timer(void)
{
if (key_timer.timeout <= HAL_GetTick())
{
key_timer.func(key_timer.args);
}
}
/* 外部中断回调函数,当检测到特定引脚(GPIO_PIN_14)的中断时,重置定时器超时时间 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_14)
{
mod_timer(&key_timer, 10); // 设置定时器超时时间为当前时间加10(单位取决于HAL_GetTick()的实现)
}
}
总结,这段代码的主要用途是在特定的GPIO引脚(是GPIO_PIN_14,按需更改引脚)发生外部中断时,启动一个10单位时间的定时器。如果在定时器超时之前该引脚再次发生中断,则重置定时器。定时器超时后,会调用key_timeout_func
函数,该函数会增加g_key_cnt
变量的值,表示按键事件被记录,经试验,能满足按键防抖处理要求。