单片机按键检测程序(定时器法消抖)

单片机按键检测程序(定时器法消抖)

相信大家在初学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;
}

不过现在不知道怎么把双击的功能加上去,之前写了一堆代码加上去也能实现,不过比较冗长,不喜欢。如果有大佬有比较简洁的双击触发的代码可以在评论区留言。抱拳\

以下是一个简单的 51 单片机定时器按键消抖程序: #include <reg51.h> sbit key = P1^0; // 定义按键引脚 sbit led = P1^1; // 定义 LED 引脚 void delay(unsigned int t) // 延时函数 { unsigned int i, j; for (i = 0; i < t; i++) for (j = 0; j < 125; j++); } void main() { unsigned char key_state = 0; // 按键状态 unsigned char key_count = 0; // 按键计数器 unsigned char key_press = 0; // 按键按下标志 TMOD = 0x01; // 定时器 0 工作在模式 1 TH0 = 0xFC; // 定时器 0 计数初值 TL0 = 0x67; TR0 = 1; // 启动定时器 0 while (1) { if (key == 0) // 检测按键是否按下 { key_count++; // 按键计数器加 1 if (key_count >= 10) // 如果按键计数器大于等于 10 { key_count = 10; // 按键计数器置为 10 key_press = 1; // 按键按下标志置为 1 } } else { if (key_count > 0) // 如果按键计数器大于 0 key_count--; // 按键计数器减 1 } if (TF0 == 1) // 检测定时器 0 是否溢出 { TF0 = 0; // 定时器 0 溢出标志清零 TH0 = 0xFC; // 定时器 0 计数初值 TL0 = 0x67; if (key_press == 1) // 如果按键按下 { key_press = 0; // 按键按下标志清零 key_state = !key_state; // 按键状态取反 led = key_state; // LED 状态与按键状态相同 } } delay(1); // 延时 1ms } } 这个程序使用了定时器 0 来实现按键消抖,当按键按下时,按键计数器加 1,如果按键计数器大于等于 10,则认为按键已经按下,按键按下标志置为 1;当按键松开时,按键计数器减 1;定时器 0 溢出时,检测按键按下标志,如果为 1,则将按键按下标志清零,按键状态取反,LED 状态与按键状态相同。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值