两种状态机扫描按键,第二种只要三行!!!

Posted katachi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了两种状态机扫描按键,第二种只要三行!!!相关的知识,希望对你有一定的参考价值。

从开始学51就接触到按键扫描,起初接触到郭天祥的delay滤波,方法虽然简陋,但是确实有效。

用了一段时间后,偶然接触到状态机扫描按键。那会儿没有啥数电知识懂不起状态机,硬啃啃懂了,顿时觉得怎么又这种机智的想法!

持续使用此方法将近一年半,期间自己也做了几种扩展,也能正常表达出算法使用,但是唯一的缺点就是代码比较长。

先贴我写过的状态机扫描按键的程序,包括独立按键,以及矩阵式的按键。

注意:所有扫描均用定时器定时实现,通常是20ms调用一次扫描且检测到低电平认为触发按键


 

矩阵式(一):

 1 sbit r1=P3^0;
 2 sbit r2=P3^1;
 3 sbit r3=P3^2;
 4 sbit r4=P3^3;
 5 sbit c1=P4^4;
 6 sbit c2=P4^2;
 7 sbit c3=P3^5;
 8 sbit c4=P3^4;
 9 
10 unsigned char matrix_scan()
11 {
12     static unsigned char state=0x00;
13     unsigned char keyval=16;
14     
15     switch (state)
16     {
17         case 0x00:
18             r1=r2=r3=r4=0;
19             c1=c2=c3=c4=1;
20             if (!(c1&c2&c3&c4)) 
21                 state=0x01;
22             break;
23         case 0x01:
24             r1=r2=r3=r4=0;
25             c1=c2=c3=c4=1;
26             if (!(c1&c2&c3&c4)) 
27             {
28                 r1=0;
29                 r2=r3=r4=c1=c2=c3=c4=1;
30                 if (!c1) keyval=0;
31                 else if (!c2) keyval=1;
32                 else if (!c3) keyval=2;
33                 else if (!c4) keyval=3;
34                 
35                 r2=0;
36                 r1=r3=r4=c1=c2=c3=c4=1;
37                 if (!c1) keyval=4;
38                 else if (!c2) keyval=5;
39                 else if (!c3) keyval=6;
40                 else if (!c4) keyval=7;        
41                 
42                 r3=0;
43                 r2=r1=r4=c1=c2=c3=c4=1;
44                 if (!c1) keyval=8;
45                 else if (!c2) keyval=9;
46                 else if (!c3) keyval=10;
47                 else if (!c4) keyval=11;
48                 
49                 r4=0;
50                 r2=r3=r1=c1=c2=c3=c4=1;
51                 if (!c1) keyval=12;
52                 else if (!c2) keyval=13;
53                 else if (!c3) keyval=14;
54                 else if (!c4) keyval=15;
55                 
56                 state=0x02;            
57                 r1=r2=r3=r4=0;
58                 c1=c2=c3=c4=1;
59             }
60             break;
61         case 0x02:
62             if (c1&c2&c3&c4)
63                 state=0x00;
64             break;    
65     }
66     return keyval;
67 }

特点:行列式的按键它的引脚不一定需要连续,如上的p42p44。实际应用中可能不常见,但是蓝桥杯的板子上是这样的

矩阵式(二):

 1 unsigned char matrix_scan()
 2 {
 3     static signed char state_cnt = 0x00;//状态记录
 4     unsigned char key_state=0;//键值记录
 5     
 6     switch(state_cnt)
 7     {
 8         case 0x00://闲置
 9             P3 = 0xf0;            //高四位高电平低四位低电平
10             if (P3 != 0xf0)
11             {
12                 state_cnt = 0x01;//进入触发状态
13                 break;
14             }
15         case 0x01://触发
16             if (P3 != 0xf0)//确实有按键触发
17             {
18                 state_cnt = 0x02;//进入释放状态
19                 key_state = P3&0xf0;  
20                 P3 = 0x0f;
21                 key_state |= P3&0x0f;
22                 P3 = 0xf0;
23                 break;
24             }
25             else//毛刺等干扰
26             {
27                 state_cnt = 0x00;
28                 break;
29             }
30         case 0x02://释放
31             if (P3 == 0xf0)//上次置的电平
32             {
33                 state_cnt = 0x00;//完成一次按键动作
34                 break;
35             }
36         default :break;
37     }
38     return key_state;//返回键值
39 }

特点:代码相对上一个较短,但是需要io连续

接下来是独立式:


第一种:

void key_scan()
{/*定义静态变量state_cnt记住状态*/
    static unsigned char state_cnt = 0x00;    //初始化为闲置状态

    switch (state_cnt)
    {
        case 0x00:/*闲置状态*/
            if (key==0)//低电平可能有按键按下    进入状态1
            {
                state_cnt = 0x01;
                break;
            }
        case 0x01:/*触发状态*/
            if (key==0)//低电平确实有按键按下    执行动作并进入状态2
            {
                state_cnt = 0x02;
                //->此处放置要执行的函数等<-//
                led=!led;
                break;
            }
            else            //高电平为毛刺等    还原状态0
            {
                state_cnt = 0x00;
                break;
            }
        case 0x02:/*待释放状态*/
            if (key==1)//高电平为一个按键动作结束
            {
                state_cnt = 0x00;
                break;
            }
        default :break;
    }    
}

特点:和第二个矩阵扫描的原理相同, 一次仅能扫描一个按键,也可以更改代码实现扫描一行或者一列,但是肯定必我接下来介绍的这种刚刚发现的代码多!!

重头戏:

1 unsigned char trg,cont;
2 void keyscan(void)
3 {
4      unsigned char tmp=P2^0xff;
5      trg=tmp&(tmp^cont);
6      cont=tmp;
7 }

你没有看错!!就是这三行代码,实现了扫描连续的独立按键。

下面简单的分析一下:

第一行定义两个静态变量,trg是trigger,cont是continue,字面意思理解。先说:trig只会在第一次按下的时候置一,cont只要当前次扫描到某一个按键触发,那么对应的cont里的那一位就为1

第四行用数电解释一波:P2和0xff的抑或,用公式展开可以得到:tmp=(P2 AND 0) OR [(NOT P2) AND 0xff]  (NOT P2是非P2的意思,可能不标准,就是按位取反)

           进一步化简就是tmp=NOT P2 也就是将读取的P2所有io取反。

第五行:直接贴出化简结果  (NOT P2)AND(NOT cont)

第六行:cont更新值

合起来就是trg的某一位只会在那一位对应的按键第一次检测到按下的时候为1,然后cont会在那个按键检测到按下的时候一直为1。

一旦第二次检测到该按键还是按下,trg为0,cont仍为1。大致的状况就是这样, 然后就随意扩展使用了。

trg可以检测按下一次,取某一位相与就可以了,如:

if (trg & 0x01)
{
    //1号按键对应的功能
}

cont可以检测长按甚至检测按下多久,如:

if (cont & 0x01)
{
    if (++hold >= 150)//按下20ms*150=3s 因为程序20ms调用一次
    {
        hold=0;
        //func
    }
}    

相应的按键释放则检测trg和cont那一位皆为0。如:

if (trg&cont&0x01)
{
//relax func
}

date:21点22分2018年4月27日

 

以上是关于两种状态机扫描按键,第二种只要三行!!!的主要内容,如果未能解决你的问题,请参考以下文章

多按键状态机的实现

STM32_按键_外部中断_定时器扫描_循环扫描_FIFO机制

GPIO—按键轮询

51单片机实现矩阵键盘行扫描

KVM 克隆系统

单片机的键盘接口连接分为哪几类?说明各自的工作原理。键盘消抖是啥有哪几种方法