1.代码及解析

我们先贴出代码,下面再进行解析原理

u8 KEY_Scan(u8 mode,u16 TIMES)
{  
    static u8 key_up=1; //按键松开标志
    static u16 times;
    if(mode)key_up=1;   //如果mode等于1,支持连按    
    if(key_up&&(KEY4==0||KEY8==0||KEY12==0||KEY16==0))//只要在key_up等于1时,其中一个按键被按下就可以进入执行代码
    {
        times++;        //记录进入低电平的时间
        if(times>=TIMES)//抖动的时间已经过去
        { 
            times=0;
            key_up=0;
            if(KEY4==0)return 4;
            else if(KEY8==0)return 8;
            else if(KEY12==0)return 12;
            else if(KEY16==0)return 16;
        }
    }
    else if(KEY4==1&&KEY8==1&&KEY12==1&&KEY16==1)key_up=1;      
    return 0;// 无按键按下
}

1.假设我们传入的参数mode为0,进入函数,第一次初始化时key_up为1,然后没有去执行“if(mode)key_up=1;”,此时若没有按键按下,则满足

“else if(KEY4==1&&KEY8==1&&KEY12==1&&KEY16==1)key_up=1;”,所以key_up还是等于1,返回值为0。


2.假设有按键按下,持续够一定的低电平时间了(抖动时间过去了),清零times,让key_up等于0,然后判断此时是哪个按键按下就返回对应的值。


3.返回对应的值之后,如果我们一直按着不放,第二次执行这个函数就会因为key_up在前一次函数执行中已经等于0,所以我们就算按着按键不放也进入不了

“if(key_up&&(KEY4==0||KEY8==0||KEY12==0||KEY16==0))”,那么一次按键动作只能有一次返回值为4、8、12或16的机会,其他时候都是返回0。如果我们按键松手了,那就满足

“else if(KEY4==1&&KEY8==1&&KEY12==1&&KEY16==1)key_up=1;”,这样key_up恢复为1了,下次按键动作又能够进入

“if(key_up&&(KEY4==0||KEY8==0||KEY12==0||KEY16==0))”从而可以返回对应的按键值。不支持连按模式就讲解完了。


4.参数mode为1时,总会执行“if(mode)key_up=1;”,所以按键按着不放函数的执行都会进入

“if(key_up&&(KEY4==0||KEY8==0||KEY12==0||KEY16==0))”,这样返回的按键值的机会比不支持连按时候还要多,

这就是mode等于1时呈现的支持连按功能。

原理解析就讲解完了,可以看到,该代码在不支持连按模式下是按下之后就执行返回值了的,而不是像以前一样要抬起按键之后才会执行返回值的语句,所以不管我们的按键手速是快是慢,程序都会在最快时间内去执行返回值的语句。

2.补充

我们不再使用“#define TIMES 1000”,因为有时“KEY_Scan()”在各种不同的循环体里扫描返回值,有些循环一次执行时间很快,有些却很慢,我们在第五章已经分析过这些情况了,所以TIMES的值需要随机应变。我们决定让TIMES作为按键程序的第二个参数,这样在某些循环体里如果循环一次的时间很快,我们调为“KEY_Scan(0,1000);”,循环一次的时间很慢就改为“KEY_Scan(0,300);”

3.测试代码

#include <reg52.h> 
#include <function.h>
sbit KEY4  = P2^3;
sbit KEY8  = P2^2;
sbit KEY12 = P2^1;
sbit KEY16 = P2^0;
 
u8 KEY_Scan(u8 mode,u16 TIMES)
{  
    static u8 key_up=1; //按键松开标志
    static u16 times;
    if(mode)key_up=1;   //如果mode等于1,支持连按    
    if(key_up&&(KEY4==0||KEY8==0||KEY12==0||KEY16==0))//只要在key_up等于1时,其中一个按键被按下就可以进入执行代码
    {
        times++;        //记录进入低电平的时间
        if(times>=TIMES)//抖动的时间已经过去
        { 
            times=0;
            key_up=0;
            if(KEY4==0)return 4;
            else if(KEY8==0)return 8;
            else if(KEY12==0)return 12;
            else if(KEY16==0)return 16;
        }
    }
    else if(KEY4==1&&KEY8==1&&KEY12==1&&KEY16==1)key_up=1;      
    return 0;// 无按键按下
}
 
void KEY_Init()
{   
    P2=0X7F;//让P2.7输出低电平,其他输出高电平,这样就可以使能4个按键了
}
 
void main()
{  
    u8 key;    //用来读取按键动作的返回值
    LED_Init();//初始化LED硬件模块
    KEY_Init();//初始化按键模块
    P0=0xFE;   //先点亮LED2
    while(1)
    {      
        key=KEY_Scan(0,1000); //不支持连按模式,判断阈值为1000
        if(key==4)LED2=!LED2; //执行功能代码
        if(key==8)LED4=!LED4; //执行功能代码
        if(key==12)LED6=!LED6;//执行功能代码
        if(key==16)LED8=!LED8;//执行功能代码
    }
}

把“KEY_Scan(0,1000);”改为“KEY_Scan(1,1000);”就是支持连按了。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)