劉 宸,黃世瑜
(四川職業技術學院,四川 遂寧 629000)
無論是獨立式按鍵還是矩陣式鍵盤,都需要使用按鍵。然而,機械式按鍵在按下和釋放時都存在一定時間的機械抖動,使得輸出電平不穩定,從而導致程序錯誤動作。為了消除抖動帶來的不良影響,通常使用軟件消抖[1]。鍵盤識別過程中,軟件消抖的處理方法是本文主要的闡述對象。在獲取鍵位置碼、譯碼以及按鍵功能的過程中,每個步驟都有多種處理方法。本文在實現鍵盤識別的步驟中,將逐步介紹它們的特點及對應的應用場合,以尋求最優的算法。這里,本文的程序代碼采用C430語言編寫。
首先,需要識別是否按下按鍵。與獨立式按鍵一樣,依靠識別與按鍵相連引腳的電平高低。其次,需要識別矩陣鍵盤中哪一位按鍵被按下,即要識別按鍵的位置碼。矩陣鍵盤結構如圖1所示。
要獲取鍵盤中某個按鍵的位置碼,有反轉法和掃描法兩種方式。其中,掃描法是用行線作為輸出,列線作為輸入(交換行和列線的輸入、輸出方向亦可),行線逐行輸出低電平‘0’。若某行有鍵按下,列線則定會有低電平‘0’輸入;如果沒有按鍵,則列線輸入值全部為高電平‘1’。如果有鍵按下,由列線和行線讀取的坐標位置就能確定相應位置的按鍵有被按下[2]。
本文采用反轉法,即列線和行線交替輸入和輸出,分兩步獲得鍵盤中的位置碼,并且在多個按鍵同時被按下時,也能準確識別出組合鍵。
反轉法實例程序段:
P3DIR=0X0F;//高4位輸入,低4位輸出
P3OUT=0XF0;//行,低4位輸出0
y=P3IN;//得列碼,如0xe0
P3DIR=0XF0;//高4位輸出,低4位輸入
P3OUT=0X0F;//列,高4位輸出0
x=P3IN;//得行碼,如0x0d
x=x+y;//合成位置碼,如0xed

圖1 矩陣鍵盤結構
通過1.1的步驟獲得按鍵的行號和列號合成的位置碼,如0xed這樣的十六進制數。但是,要轉換為有象征意義的鍵盤編號,還要進行鍵盤譯碼。鍵盤的編號與鍵盤的位置碼沒有直接的運算關系,故采用查表法將位置碼轉變為對應的鍵號。
譯碼程序段如下:
const char keytab[]={0x11,0x21,0x41,0x81,0x12,0x22,0x 42,0x82,0x14,0x24,0x44,0x84,0x18,0x28,0x48,0x88};//位置碼常量數組
...//將合成的位置碼key,用查表法與位置碼數組進行逐一比對。
for(i=0;i<16;i++)
{if(key==keytab[i])
break;
}
由于鍵盤的邏輯布局按設計和功能要求不同,會導致每個按鍵的坐標位置和鍵盤編號的對應關系發生變化。如果每次都要重新尋找對應關系來修改keytab[]數組,將會使程序不具有通用性,且浪費時間、效率低下。所以,可再通過一個翻譯數組,實現任意布局的鍵盤編號。
const char keynum[]=//翻譯數組,實現任意布局
{ 1,2,3,4,
5,6,7 ,8,
9,0,14 ,15,
10,11,12 ,13,16};
...//得到上一步驟的鍵號i(0~16)
return keynum[i];//返回值是將鍵號i通過翻譯數組翻譯后的值。
1.3.1 傳統方法
這里不討論硬件消抖,只討論軟件消抖。經典鍵盤消抖程序采用延時再檢測的流程,首先查詢鍵盤是否有鍵按下,若無鍵按下,則不執行功能。當有鍵按下時,調用延時函數或者調用其他函數,目的都是等待鍵盤電平抖動的時間過去后再次查詢。若此時仍然有鍵按下,說明確實有鍵可靠穩定地按住,并去除了后沿抖動的可能[3]。然后,按照鍵盤上各按鍵的功能規劃執行對應的功能程序。最后,為了避免按住按鍵時的重復執行功能,需要繼續循環檢測鍵盤,直到按鍵松開為止。
對應的程序段如下:
display();//其他任務
key=inkey();//讀入鍵盤編碼
if(key<16)//有按鍵
{display();//延時消抖,并顯示
if(key==inkey())//兩次讀入鍵盤編碼相同
{
key_action(key);//執行按鍵功能
while(key==inkey()){display();}//按住時等待松手
}
}
這段程序能夠較好地實現鍵盤消抖。如果系統的任務相對單一,使用這段程序可以完成任務,但缺點是調用延時函數和等待松手循環查詢時,程序指針陷入有限循環中,無暇處理其他任務,如讀取傳感器數據、輸出控制執行部件等。雖然采用中斷系統可以處理這些實時任務,由主程序調用鍵盤檢測的函數,但這樣會導致在主程序無限循環中一直有鍵盤檢測的任務,而系統則無法進入低功耗模式。然而,在便攜式設備中不能進入低功耗模式,將是無法想象的。當然,可以把鍵盤檢測函數放在定時中斷服務函數,由于中斷源的優先順序問題,也會導致程序指針停留在此而無法執行其他函數。
1.3.2 對電平計時的矩陣鍵盤檢測方法
從以上分析看出,傳統算法中執行矩陣鍵盤檢測與其他任務的運行存在矛盾,有諸多弊端,而本文提出的對電平計時的矩陣鍵盤檢測方法可以解決這些問題。程序要求每隔幾毫秒讀取一次矩陣鍵盤的值,可以放在主函數,最好放在定時中斷服務程序,目的是間隔抽樣檢測鍵盤輸入端口的電平。
程序流程圖如圖2所示,方法是間隔時間抽樣檢測,用一個計時值統計低電平保持的時間。首先讀取輸入端口數據,判斷是否按鍵。當有按鍵時,計時值+1;否則,計時值清零。若連續多次都檢測到低電平,計時值會累加,表示已經度過鍵盤的抖動時間,可以穩定可靠地按住按鍵。識別為短按時,需要低電平穩定10 ms以上。若間隔時間為2 ms,則計時值需要10/2=5次。當計時值累加到5時,滿足短按識別條件,就可以執行對應按鍵的功能。如果是長按還可實現連擊的效果,則當計時值累加到500時,即低電平已經穩定2 ms×500=1 s,可視為長按有效。此時,將計時值回撥到400,并執行相應功能。如果繼續長按,計時值又從400累加到500時,又一次滿足長按條件,但周期只有2 ms×(500-400)=0.2 s,即長按1 s后每隔0.2 s就視為連擊一次,可以實現電視遙控器上的連加連減效果。返回鍵值的時間點只有5(短按)和500(長按),太小的是按鍵抖動時期,其他次數都不滿足按鍵條件,返回按鍵無效的鍵值。整個檢測按鍵的程序里沒有延時等待,因此程序指針不會停留在這里,轉而執行其他任務。

圖2 對電平計時的矩陣鍵盤檢測方法
讀取按鍵值的程序段如下:
if(x!=0xFF)//x是合成的位置碼,若按下時x!=0xFF
{
cnt++;//統計低電平的時間
if(cnt==5)//連續檢測到5次低電平,表示抖動已經過去,已進入穩定期5*2ms=10ms
{ key=x; }//20 ms時,給返回值,執行功能
else if(cnt>500)//按住不松手 500×2 ms=1 s以上,長按,就連續執行功能
{ cnt=400; key=x;}//從400增加到500,共100次的時間0.2 s
else key=0xff;//其他次數時,按了鍵,但沒達到條件
}
else {cnt=0; key=0xff;}//沒按鍵,抬手時,時間計數cnt清0
經過上述步驟后,能讀取到穩定可靠的鍵盤數據。按鍵的位置碼被翻譯數組翻譯后的鍵值是0~16。當有按鍵時,鍵值是0~15;沒有按鍵或無效時,鍵值是16。按鍵的功能可根據鍵值分別對應編寫。
void keyaction()
{ char key;
key=keyscan();//讀取鍵盤數據
if(key<16)//有按鍵,則執行功能
{ if(key<10){n=key;}
switch(key)
{
case 10: if(n<15)n++;break;
case 11: if(n>0)n--;break;
case 12: if(n<15)n+=2; break;
case 13: if(n>0)n-=2; break;
}
P1OUT=table[n];//靜態顯示
}
}
此函數可放在主函數循環間隔調用,也可在定時中斷服務程序中定時調用。
while(1)
{ delay(2);//間隔2 ms讀取按鍵
keyaction();}//按鍵識別及執行功能
測試通過搭建真實硬件電路和Proteus仿真的實驗驗證。實驗結果證明,本文提出的間隔抽樣統計低電平時間的矩陣鍵盤識別方法,能有效消除抖動帶來的影響,還能使程序指針不停留地執行其他任務。該方法沒有依靠延時等待,不浪費系統時間,實現了任意布局,并區分出了短按和長按,具有優越性。
為解決矩陣鍵盤識別傳統方法中鍵盤檢測與多任務實時運行存在的諸多問題,本文提出了一種抽樣檢測對電平計時的矩陣鍵盤識別方法。在不靠延時的前提下,既能夠可靠地消除抖動,又能夠保證程序順暢地運行其他任務,并實現了鍵盤功能的任意布局,避免了鍵盤邏輯值的復雜計算和更改布局帶來的排序問題。同時,能嚴格區分短按和長按,稍作修改亦可識別雙擊。因此,這是一種通用性和效率更高的矩陣鍵盤識別方法。
參考文獻:
[1] 劉 宸.兩種基于電平計時的按鍵檢測方法[J].四川職業技術學院學報,2017,27(4):151-153.
[2] 鄭玉章,徐愛鈞.嵌入式開發過程中按鍵檢測算法的改進[J].單片機與嵌入式系統應用,2014,14(8):73-75.
[3] 王海珍.按鍵檢測算法創新在嵌入式開發中的應用[J].電子制作,2017(4):98,100.