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

本文探讨了51单片机按键抖动问题的解决方法,通过定时器消除机械抖动,提出了一种高效的按键检测程序,支持短按、长按和延时长按功能,并提供了封装后的简洁代码。作者还分享了优化后的函数和双击功能的讨论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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

### C51 按键及延时函数 在嵌入式系统中,特别是单片机应用里,按键检测是一个常见的任务。然而由于机械开关本身的特性,在按下和释放瞬间会产生电压波动(即“抖动”),这可能导致误读输入状态。因此需要加入适当的软件处理来除这种影响。 下面给出一段典型的基于C51单片机语言编写的按键程序示例: ```c #include <reg52.h> // 定义按键端口位 sbit KEY = P3^0; void Delay(unsigned int time) { unsigned int i; while (time--) { // 延迟一段时间以等待稳定 for(i=647;i>0;i--); } } unsigned char KeyScan(void){ unsigned char keyvalue = 0; if(KEY == 0){ // 如果检测到有键按下的情况,则进入判断是否有效以及返回对应值部分。 Delay(1); // 简易的硬件延时防止接触不良造成的误判 while (!KEY); // 防止连续触发导致多次响应,等到松开再继续下一步操作. keyvalue = 1; // 可根据实际情况设置不同的keyvalue代表不同功能按钮 } return keyvalue; } ``` 上述代码实现了基本的功能需求: - `Delay()` 函数用于生成指定长度的时间延迟; - `KeyScan()` 函数负责监测特定I/O引脚的状态变化,并结合短时间内的重复检查机制过滤掉由物理触点闭合引起的小范围跳变;当确认确实存在一次有效的按键动作之后才给予回应并结束本次查询过程。 需要注意的是实际项目当中还需要考虑更多细节问题如电源启动瞬间不稳定、外界电磁干扰等对信号采集环节的影响因素,在必要时候可以引入更复杂的算或者额外电路辅助完成同样的目的。 #### 实际应用建议 对于一些对抗干扰性能要求较高的场合,除了采用合适的滤波措施外还可以尝试增加看门狗定时器复位机制保证系统的可靠性运行。另外也可以利用PWM输出或者其他方式模拟低频方波驱动LED指示灯显示当前工作模式供用户直观了解设备内部状况方便调试维护。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值