1. 程式人生 > >兩種狀態機掃描按鍵,第二種只要三行!!!

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

狀況 變量 接下來 初始化 進一步 一次 amp 開始 not

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

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