单片机按键检测程序(定时器法消抖)
相信大家在初学51单片机的时候按键检测为了消抖都会加一些空循环做软件延时。这样做可是浪费了CPU的不少资源。对于51这种低端单片机做一些功能很多的项目时软件延时的方法不仅浪费CPU资源而且软件还会莫名其妙的出现很多BUG。所以我们今天就试着剖析一下按键抖动的问题。
下面我们分析一下按键的抖动
我已经先把图上的这个时间标出来了,按键按下的前抖动和按键松开的后抖动是按键本身的机械特性决定的,这不是人能控制的。而中间的那个稳定闭合区是我们所能控制,不过以我们人体的反应和控制能力,这个时间应该不会低于二十毫秒。为了严谨我在知乎上找到了这个
有点扯远了。我们从图1上看,按键按下去一个周期大约需要40ms。假设我们20ms读取一次按键的引脚寄存器,只要有按键按下我们就必定能读取到按键按下,所以我们在定时器里每20ms读取一次引脚寄存器的值并对值做处理就可以了。
找到问题和解决方法后我们就可以写代码了。不过在我们日常所做的项目中需要有短按,长按,延时长按等操作。为了以后可以做项目好移植,我们把这些功能封装成函数放在定时器里20ms循环就可以。
废话不说了,开始上代码:
/*
P3.0,P3.0,P3.0,P3.3 接四个按键
0x01,0x02,0x04,0x08 四个按键所对应的键值码
up_trg-按键抬起(在未触发延时长按时间内松开按键只有一次按键值)
down_cont-按键长按按下(只要按下就一直有按键值)
delay_down_cont-按键延时长按按下(在按下后延时一段时间一直有按键值)
delay_value-按键延时触发时间 (与此函数的轮询执行时间有关)
例如:要延时0.8s,此函数轮询时间为20ms轮询一次,则delay_value = 40;
*/
u8 up_trg = 0, down_cont = 0, delay_down_cont = 0,delay_value = 40;//全局变量
void sole_keys ()
{
static u8 i = 0;
u8 read_data = (P3 ^ 0xff) & 0x0f;
if (read_data != 0x00)
{
if(++i > delay_value) delay_down_cont = read_data;
}
else if (i < delay_value) up_trg = down_cont,delay_down_cont = 0,i = 0;
else i = 0;
down_cont = read_data;
switch(up_trg)//短按所要执行的功能
{
case 0x01 : if (++PWM == 100) PWM = 0; break;
case 0x02 : if (--PWM == 1) PWM = 99; break;
case 0x04 : ; break;
case 0x08 : ; break;
}
switch(down_cont)//长按所要执行的功能
{
case 0x01 : ; break;
case 0x02 : ; break;
case 0x04 : ; break;
case 0x08 : ; break;
}
switch(delay_down_cont)//延时长按所要执行的功能
{
case 0x01 : ; break;
case 0x02 : ; break;
case 0x04 : ; break;
case 0x08 : ; break;
}
}
这样我们就可以把这个sole_keys ()函数直接放在20ms的定时器中断就可以了。
这个函数的代码是参考这个大佬写的程序改的:
新型的按键扫描程序,仅三行程序
实测很好用,我大二大三写51按键的程序就是用的这个。当然矩阵按键也可以写,只需改一行代码就可以了,只是键值码就不一样了,大家可以自行推一下。
现在,在一个项目中为了其他功能有更好的实时性,我是用的时间片轮的方法来轮询这个按键程序。中断程序运行的是对实时性比较高的代码。
(哦,时间片轮的方法也是从这 “时间片轮询法” 嫖的,想用的话可以看这个大佬的文章。)
为了方便调用,所以我就写成了这样;
void sole_keys (u8 *up_trg, u8 *down_cont, u8 *delay_down_cont, u8 delay_value)
{
static u8 i = 0;
u8 read_data = (P3 ^ 0xff) & 0x0f;
if (read_data != 0x00)
{
if(++i > delay_value)*delay_down_cont = read_data;
}
else if (i < delay_value) *up_trg = *down_cont,*delay_down_cont = 0,i = 0;
else i = 0;
*down_cont = read_data;
}
不过现在不知道怎么把双击的功能加上去,之前写了一堆代码加上去也能实现,不过比较冗长,不喜欢。如果有大佬有比较简洁的双击触发的代码可以在评论区留言。抱拳\