劉 宸,黃世瑜
(四川職業(yè)技術(shù)學(xué)院,四川 遂寧 629000)
無論是獨(dú)立式按鍵還是矩陣式鍵盤,都需要使用按鍵。然而,機(jī)械式按鍵在按下和釋放時(shí)都存在一定時(shí)間的機(jī)械抖動(dòng),使得輸出電平不穩(wěn)定,從而導(dǎo)致程序錯(cuò)誤動(dòng)作。為了消除抖動(dòng)帶來的不良影響,通常使用軟件消抖[1]。鍵盤識(shí)別過程中,軟件消抖的處理方法是本文主要的闡述對(duì)象。在獲取鍵位置碼、譯碼以及按鍵功能的過程中,每個(gè)步驟都有多種處理方法。本文在實(shí)現(xiàn)鍵盤識(shí)別的步驟中,將逐步介紹它們的特點(diǎn)及對(duì)應(yīng)的應(yīng)用場(chǎng)合,以尋求最優(yōu)的算法。這里,本文的程序代碼采用C430語言編寫。
首先,需要識(shí)別是否按下按鍵。與獨(dú)立式按鍵一樣,依靠識(shí)別與按鍵相連引腳的電平高低。其次,需要識(shí)別矩陣鍵盤中哪一位按鍵被按下,即要識(shí)別按鍵的位置碼。矩陣鍵盤結(jié)構(gòu)如圖1所示。
要獲取鍵盤中某個(gè)按鍵的位置碼,有反轉(zhuǎn)法和掃描法兩種方式。其中,掃描法是用行線作為輸出,列線作為輸入(交換行和列線的輸入、輸出方向亦可),行線逐行輸出低電平‘0’。若某行有鍵按下,列線則定會(huì)有低電平‘0’輸入;如果沒有按鍵,則列線輸入值全部為高電平‘1’。如果有鍵按下,由列線和行線讀取的坐標(biāo)位置就能確定相應(yīng)位置的按鍵有被按下[2]。
本文采用反轉(zhuǎn)法,即列線和行線交替輸入和輸出,分兩步獲得鍵盤中的位置碼,并且在多個(gè)按鍵同時(shí)被按下時(shí),也能準(zhǔn)確識(shí)別出組合鍵。
反轉(zhuǎn)法實(shí)例程序段:
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 矩陣鍵盤結(jié)構(gòu)
通過1.1的步驟獲得按鍵的行號(hào)和列號(hào)合成的位置碼,如0xed這樣的十六進(jìn)制數(shù)。但是,要轉(zhuǎn)換為有象征意義的鍵盤編號(hào),還要進(jìn)行鍵盤譯碼。鍵盤的編號(hào)與鍵盤的位置碼沒有直接的運(yùn)算關(guān)系,故采用查表法將位置碼轉(zhuǎn)變?yōu)閷?duì)應(yīng)的鍵號(hào)。
譯碼程序段如下:
const char keytab[]={0x11,0x21,0x41,0x81,0x12,0x22,0x 42,0x82,0x14,0x24,0x44,0x84,0x18,0x28,0x48,0x88};//位置碼常量數(shù)組
...//將合成的位置碼key,用查表法與位置碼數(shù)組進(jìn)行逐一比對(duì)。
for(i=0;i<16;i++)
{if(key==keytab[i])
break;
}
由于鍵盤的邏輯布局按設(shè)計(jì)和功能要求不同,會(huì)導(dǎo)致每個(gè)按鍵的坐標(biāo)位置和鍵盤編號(hào)的對(duì)應(yīng)關(guān)系發(fā)生變化。如果每次都要重新尋找對(duì)應(yīng)關(guān)系來修改keytab[]數(shù)組,將會(huì)使程序不具有通用性,且浪費(fèi)時(shí)間、效率低下。所以,可再通過一個(gè)翻譯數(shù)組,實(shí)現(xiàn)任意布局的鍵盤編號(hào)。
const char keynum[]=//翻譯數(shù)組,實(shí)現(xiàn)任意布局
{ 1,2,3,4,
5,6,7 ,8,
9,0,14 ,15,
10,11,12 ,13,16};
...//得到上一步驟的鍵號(hào)i(0~16)
return keynum[i];//返回值是將鍵號(hào)i通過翻譯數(shù)組翻譯后的值。
1.3.1 傳統(tǒng)方法
這里不討論硬件消抖,只討論軟件消抖。經(jīng)典鍵盤消抖程序采用延時(shí)再檢測(cè)的流程,首先查詢鍵盤是否有鍵按下,若無鍵按下,則不執(zhí)行功能。當(dāng)有鍵按下時(shí),調(diào)用延時(shí)函數(shù)或者調(diào)用其他函數(shù),目的都是等待鍵盤電平抖動(dòng)的時(shí)間過去后再次查詢。若此時(shí)仍然有鍵按下,說明確實(shí)有鍵可靠穩(wěn)定地按住,并去除了后沿抖動(dòng)的可能[3]。然后,按照鍵盤上各按鍵的功能規(guī)劃執(zhí)行對(duì)應(yīng)的功能程序。最后,為了避免按住按鍵時(shí)的重復(fù)執(zhí)行功能,需要繼續(xù)循環(huán)檢測(cè)鍵盤,直到按鍵松開為止。
對(duì)應(yīng)的程序段如下:
display();//其他任務(wù)
key=inkey();//讀入鍵盤編碼
if(key<16)//有按鍵
{display();//延時(shí)消抖,并顯示
if(key==inkey())//兩次讀入鍵盤編碼相同
{
key_action(key);//執(zhí)行按鍵功能
while(key==inkey()){display();}//按住時(shí)等待松手
}
}
這段程序能夠較好地實(shí)現(xiàn)鍵盤消抖。如果系統(tǒng)的任務(wù)相對(duì)單一,使用這段程序可以完成任務(wù),但缺點(diǎn)是調(diào)用延時(shí)函數(shù)和等待松手循環(huán)查詢時(shí),程序指針陷入有限循環(huán)中,無暇處理其他任務(wù),如讀取傳感器數(shù)據(jù)、輸出控制執(zhí)行部件等。雖然采用中斷系統(tǒng)可以處理這些實(shí)時(shí)任務(wù),由主程序調(diào)用鍵盤檢測(cè)的函數(shù),但這樣會(huì)導(dǎo)致在主程序無限循環(huán)中一直有鍵盤檢測(cè)的任務(wù),而系統(tǒng)則無法進(jìn)入低功耗模式。然而,在便攜式設(shè)備中不能進(jìn)入低功耗模式,將是無法想象的。當(dāng)然,可以把鍵盤檢測(cè)函數(shù)放在定時(shí)中斷服務(wù)函數(shù),由于中斷源的優(yōu)先順序問題,也會(huì)導(dǎo)致程序指針停留在此而無法執(zhí)行其他函數(shù)。
1.3.2 對(duì)電平計(jì)時(shí)的矩陣鍵盤檢測(cè)方法
從以上分析看出,傳統(tǒng)算法中執(zhí)行矩陣鍵盤檢測(cè)與其他任務(wù)的運(yùn)行存在矛盾,有諸多弊端,而本文提出的對(duì)電平計(jì)時(shí)的矩陣鍵盤檢測(cè)方法可以解決這些問題。程序要求每隔幾毫秒讀取一次矩陣鍵盤的值,可以放在主函數(shù),最好放在定時(shí)中斷服務(wù)程序,目的是間隔抽樣檢測(cè)鍵盤輸入端口的電平。
程序流程圖如圖2所示,方法是間隔時(shí)間抽樣檢測(cè),用一個(gè)計(jì)時(shí)值統(tǒng)計(jì)低電平保持的時(shí)間。首先讀取輸入端口數(shù)據(jù),判斷是否按鍵。當(dāng)有按鍵時(shí),計(jì)時(shí)值+1;否則,計(jì)時(shí)值清零。若連續(xù)多次都檢測(cè)到低電平,計(jì)時(shí)值會(huì)累加,表示已經(jīng)度過鍵盤的抖動(dòng)時(shí)間,可以穩(wěn)定可靠地按住按鍵。識(shí)別為短按時(shí),需要低電平穩(wěn)定10 ms以上。若間隔時(shí)間為2 ms,則計(jì)時(shí)值需要10/2=5次。當(dāng)計(jì)時(shí)值累加到5時(shí),滿足短按識(shí)別條件,就可以執(zhí)行對(duì)應(yīng)按鍵的功能。如果是長(zhǎng)按還可實(shí)現(xiàn)連擊的效果,則當(dāng)計(jì)時(shí)值累加到500時(shí),即低電平已經(jīng)穩(wěn)定2 ms×500=1 s,可視為長(zhǎng)按有效。此時(shí),將計(jì)時(shí)值回?fù)艿?00,并執(zhí)行相應(yīng)功能。如果繼續(xù)長(zhǎng)按,計(jì)時(shí)值又從400累加到500時(shí),又一次滿足長(zhǎng)按條件,但周期只有2 ms×(500-400)=0.2 s,即長(zhǎng)按1 s后每隔0.2 s就視為連擊一次,可以實(shí)現(xiàn)電視遙控器上的連加連減效果。返回鍵值的時(shí)間點(diǎn)只有5(短按)和500(長(zhǎng)按),太小的是按鍵抖動(dòng)時(shí)期,其他次數(shù)都不滿足按鍵條件,返回按鍵無效的鍵值。整個(gè)檢測(cè)按鍵的程序里沒有延時(shí)等待,因此程序指針不會(huì)停留在這里,轉(zhuǎn)而執(zhí)行其他任務(wù)。

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