兩種狀態機掃描按鍵,第二種只要三行!!!
從開始學51就接觸到按鍵掃描,起初接觸到郭天祥的delay濾波,方法雖然簡陋,但是確實有效。
用了一段時間後,偶然接觸到狀態機掃描按鍵。那會兒沒有啥數電知識懂不起狀態機,硬啃啃懂了,頓時覺得怎麽又這種機智的想法!
持續使用此方法將近一年半,期間自己也做了幾種擴展,也能正常表達出算法使用,但是唯一的缺點就是代碼比較長。
先貼我寫過的狀態機掃描按鍵的程序,包括獨立按鍵,以及矩陣式的按鍵。
註意:所有掃描均用定時器定時實現,通常是20ms調用一次掃描且檢測到低電平認為觸發按鍵
矩陣式(一):
1 sbit r1=P3^0; 2 sbit r2=P3^1; 3 sbit r3=P3^2; 4sbit 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日
兩種狀態機掃描按鍵,第二種只要三行!!!