兩種狀態機掃描按鍵,第二種只要三行!!!


從開始學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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM