一.按键消抖
1.原理
在STM32单片机中,其硬件的按键设计有一些缺点,当我们按下按键时,并不会直接输出一个高或低电平,松开时也同理,如图
这是因为按键的机械设计,按下或松开时会有抖动,称为机械抖动,这样会对想通过按键实现的功能输出不稳定。
2.解决办法
解决方法有硬件消抖和软件消抖,这里我们只讨论软件消抖。
单次机械抖动的持续时间大约为10ms~20ms,在我们前期没有学习定时器和中断的情况下,只需要在按下按键和松开按键后面加一个简单的延时10ms~20ms函数就可以。
二.按键扫描函数
1.示例
先看一段代码
u8 key_scan(void)
{
u8 key = 0xff;
//检测按键按下
if((KEY1)||(!KEY2)||(!KEY3)||(!KEY4))
{
//延时消抖
delay_ms(20);
//再次检测按键按下
if(KEY1)
//赋值键值
key = 1;
if(!KEY2)
key = 2;
if(!KEY3)
key = 3;
if(!KEY4)
key = 4;
}
//等待按键抬起
while((KEY1)&&(!KEY2)&&(!KEY3)&&(!KEY4))
{
}
delay_ms(20);
//返回键值
return key;
}
在这段代码中按键KEY1是高电平触发,KEY2,KEY3,KEY4都是低电平触发。
乍一看,是不是这段代码就已经实现了按键扫描的功能了,当按下按键1时触发消抖然后变量key被赋值1,之后通过while循环来卡住程序判断按键有没有松开,最后返回key也就是1到函数,在主函数设置一个变量接住这个按键扫描函数,当这个变量为1时就可以实现你想实现的功能。其他按键也同理。
但是,如果你想在主函数中的按键控制功能后面再实现别的功能,比如实现一个流水灯功能。你会发现,当你按下某个按键没有松开时,该按键对应的功能被实现,但流水灯的程序却被等待按键抬起的程序卡住了没有运行。虽然说谁都不会闲着按着按键很久不松,但人类正常按下按键到松开的时间终归有大约100ms的时间,这对于人类来说时间很短,但对单片机来说可以执行很多很多次任务,因为单片机执行程序是微秒级的。对于那些项目级程序来说,这种程序可能会造成一些漏洞。
那我们能不能去掉那条等待按键抬起的程序呢,答案是不行的,因为去掉之后,我们按一次按键就会执行多次按键扫描函数,原因如之前所讲———按一次按键大概100ms左右,在这期间主函数循环早已执行很多次了。说到主函数,顺便提一下,按键扫描函数完成后是要放在主函数的while循环里面的,这是因为STM32需要不断读取GPIO引脚的状态,以检查是否有按键被按下。
2.标志位思想
我们可以在按键扫描函数里增加一个变量代表标志位。
u8 key_scan(void)
{
static u8 flag = 1;
u8 key = 0xff;
//检测按键按下
if(((KEY1)||(!KEY2)||(!KEY3)||(!KEY4)) && flag)
{
//延时消抖
delay_ms(20);
flag = 0;
//再次检测按键按下
if(KEY1)
//赋值键值
key = 1;
if(!KEY2)
key = 2;
if(!KEY3)
key = 3;
if(!KEY4)
key = 4;
}
//等待按键抬起
else if((!KEY1)&&(KEY2)&&(KEY3)&&(KEY4))
{
delay_ms(20);
flag = 1;
}
//返回键值
return key;
}
在上述程序中,我们可以看出,当我们按下某个按键没松开时,标志位flag进行锁定,所以只会返回一次按键对应键值,其他返回值则会返回初始值0xff(因为按键没松开,程序会循环执行,不断产生返回值),按键松开后则会解锁标志位,解锁标志位后才能让下一次按键按下能返回键值。这段代码则不会卡住主程序,因为代码里面没有任何的while函数而是选择结构。
3.思路
最后我们说一下按键扫描的思路
①检测按键是否按下
②延时10~15ms
③再检测按键是否按下
④等待按键抬起
⑤返回键值
该思路会卡住主程序,解决方案:增加标志位
通过按下按键锁定标志位,抬起按键解锁标志位
如果按键按下没有抬起,通过标志位锁定,不允许再识别按键
按键抬起标志位解锁,下次按按键又允许识别按键