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


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

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

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

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

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

    (补)程序的结构是这样的:

    void main(){
      //some initialization
    
      while (1){
        if (key_flag){//这个key_flag由定时器置1 大概20ms一次
          key_flag=0;
          keyscan();
          //按键判断及相应动作
       }
       //other task
    
     }    
    }

    矩阵式(一):

     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日

     相应的结构:

    void main(){
      //some initialization
    
        while (1){
            if (key_flag){//这个key_flag由定时器置1 大概20ms一次
                key_flag=0;
                keyscan();
                //按键判断及相应动作
                if (trg & 0x01)
                {
                    //1号按键单次按下对应的功能
                }
                if (cont & 0x01)
                {
                    if (++hold >= 150)//按下20ms*150=3s 因为程序20ms调用一次
                    {
                        hold=0;
                        //1号按键长按对应的功能
                    }
                }  
                if (trg&cont&0x01)
                {
                    //1号按键释放对应的功能
                }
            } 
            //other task 
        } 
    }

     date:2018-11-25 13:49:42

  • 相关阅读:
    python 基础文件操作
    python 基础之文件读操作
    python基础之省份三级菜单
    python 基础之字符串方法
    python 基础之字典一
    python 基础之简单购物车小程序实现
    c语言 四种方法调用数组
    c# windows编程控件学习-1
    c# windows编程控件学习-2
    C# 列主元素(Gauss)消去法 计算一元多次方程组
  • 原文地址:https://www.cnblogs.com/katachi/p/8964554.html
Copyright © 2020-2023  润新知