分析单片机中的按键扫描
单片机中按键检测有多种方式,可以是程序定时去扫描按键的电平状态,也可以开启外部中断,用外部中断去做按键响应。在实际项目中,不推荐使用外部中断做按键检测。因为机械按键不可避免地会有抖动问题,很容易造成一次按下多次响应。如果用外部中断的话,需要实时记录按键响应的时间,根据当前时间与上一次响应的时间之差做为是否需要响应的依据。否则只能是在中断里做延时,这又很容易造成系统崩溃。
在本文中,默认按键按下时为低电平。外部中断做按键响应的中断服务函数如下所示。一般的,按键抖动在 100ms 以内,所以外部中断发生时,如果当前时间与上一次响应时间之差大于 100ms,则说明是一个新的按键事件。系统时间可以由一个定时器来记录,设置一个计数器,由定时器从零开始每隔 1ms 向上计数一次。
void EXTI0_IRQHandler(void)
{
//上一次响应时间
static unsigned int s_iLastTimeMs = 0;
//当前系统时间
unsigned int sysTime;
//校验外部中断标志位
if(SET == exti_flag_get(EXTI_0))
{
//获取当前系统时间
sysTime = GetSysTime();
//当前系统时间距离上一次响应超过了 100ms,说明是一次新的按键事件
if((sysTime - s_iLastTimeMs) >= 100)
{
//按键响应
...
//记录当前响应时间
s_iLastTimeMs = sysTime;
}
//清除中断标志位
exti_flag_clear(EXTI_0);
}
}
当然,我们也可以让程序定时扫描按键电平状态,简单的按键扫描驱动如下所示。想要捕抓下降沿的话,需要拿当前按键状态和上一个按键状态做比较,如果上一个时刻按键为抬起而当前为按下状态,则说明发生了一个下降沿。最后别忘记了跟踪记录按键状态,保存当前键值。每隔 50ms 或 100ms 调用一次,可以起到不错的去抖效果。
int ScanKey(void)
{
//按键上一次的电平状态
static unsigned char s_iLastState = 1;
//按键当前的电平状态
unsigned char key;
//返回值
unsigned char ret = 0;
//获取按键当前电平状态
key = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
//检测到下降沿
if((0 == key) && (1 == s_iLastState)
{
ret = 1;
}
//保存当前按键电平状态
s_iLastState = key;
//返回检测结果
return ret;
}
带去抖的按键驱动如下所示,每隔 10ms 调用一次,即可起到 80ms 的软件去抖。如果记录下按键按下以及抬起的时间,还可以识别长按和段按。
int ScanKey(void)
{
//按键上一次的电平状态
static unsigned char s_iLastState = 0xFF;
//当前按键状态
static unsigned char s_iKeyValue = 0xFF;
//返回值
unsigned char ret = 0;
//获取按键当前电平状态
s_iKeyValue = (s_iKeyValue << 1) | gpio_input_bit_get(GPIOA, GPIO_PIN_0);
//检测到下降沿
if((0x00 == s_iKeyValue) && (0x00 != s_iLastState)
{
ret = 1;
}
//保存当前按键电平状态
s_iLastState = s_iKeyValue;
//返回检测结果
return ret;
}