龔利英
(惠州經(jīng)濟(jì)職業(yè)技術(shù)學(xué)院,廣東惠州,516057)
鍵盤是單片機(jī)嵌入式系統(tǒng)中常見(jiàn)且非常重要的人機(jī)接口。從編碼的功能上,鍵盤可以分成全編碼鍵盤和非編碼鍵盤兩種。全編碼鍵盤是由硬件完成鍵盤識(shí)別功能的,它通過(guò)識(shí)別鍵是否按下以及所按下鍵的位置,由全編碼電路產(chǎn)生一個(gè)唯一對(duì)應(yīng)的編碼信息(如ASCII碼)。非編碼鍵盤是由軟件完成鍵盤識(shí)別功能,它利用簡(jiǎn)單的硬件和一套專用鍵盤編碼程序來(lái)識(shí)別按鍵的位置,然后由CPU將位置碼通過(guò)查表程序轉(zhuǎn)換成相應(yīng)的編碼信息。在單片機(jī)系統(tǒng)中,用的最多的是非編碼鍵盤,其電路結(jié)構(gòu)根據(jù)按鍵硬件連接方式可分為獨(dú)立式按鍵、矩陣式按鍵和ADC按鍵等。在實(shí)際工程應(yīng)用中,由于要考慮成本等因素,系統(tǒng)中的按鍵電路通常按鍵數(shù)目較少,需要軟件程序的設(shè)計(jì)來(lái)實(shí)現(xiàn)嵌入式系統(tǒng)對(duì)按鍵復(fù)雜功能需求,使用軟件來(lái)檢測(cè)按鍵的一般思路是根據(jù)輸入端口的狀態(tài)及電平的持續(xù)時(shí)間,做出判斷從而做出正確的動(dòng)作。因此軟件程序是非常的重要的,如果程序編寫不合理就會(huì)出現(xiàn)按鍵失靈或誤操作等情況。按鍵按下,按鍵抬起,按鍵持續(xù)按住一定時(shí)間等狀態(tài)是一種有限狀態(tài)的集合,本文針對(duì)單片機(jī)應(yīng)用系統(tǒng)中按鍵結(jié)構(gòu)形式的多樣性,利用有限狀態(tài)機(jī)設(shè)計(jì)一種高兼容性的按鍵檢測(cè)系統(tǒng),以此降低按鍵程序設(shè)計(jì)難度,減少工程人員在產(chǎn)品開(kāi)發(fā)中的工作量。
圖1 有限狀態(tài)機(jī)的轉(zhuǎn)換示意圖
有限狀態(tài)機(jī)是指在外界條件的作用下,在有限個(gè)狀態(tài)之間進(jìn)行狀態(tài)轉(zhuǎn)移的數(shù)學(xué)模型,如圖1所示,其中Q表示有限狀態(tài),e表示觸發(fā)條件。在實(shí)際應(yīng)用中,根據(jù)邏輯功能,有限狀態(tài)機(jī)有Moore型和Mealy型兩種類型。Moore型有限狀態(tài)機(jī)某時(shí)刻的輸出,只取決于該時(shí)刻的輸入,與前一個(gè)狀態(tài)沒(méi)有關(guān)系,可將該類型狀態(tài)機(jī)看作組合邏輯電路;Mealy型有限狀態(tài)機(jī)該時(shí)刻的輸出不僅取決于該時(shí)刻的輸入,還與前一個(gè)狀態(tài)有關(guān),該種狀態(tài)機(jī)可視為時(shí)序邏輯電路。
CPU通過(guò)檢測(cè)按鍵的狀態(tài)接收用戶發(fā)出的指令,并做出相應(yīng)的動(dòng)作。這個(gè)過(guò)程通常由軟件實(shí)現(xiàn),一般情況下按鍵檢測(cè)事件可分為:按鍵按下、按鍵按下后松開(kāi)、按鍵短按住、按鍵短按住后松開(kāi)、按鍵短按住后重復(fù)、按鍵長(zhǎng)按住、按鍵長(zhǎng)按住后松開(kāi)、按鍵長(zhǎng)按住后重復(fù),其中事件觸發(fā)時(shí)間可自行定義。
在單片機(jī)應(yīng)用系統(tǒng)中,一般按鍵數(shù)目較少,可以通過(guò)對(duì)按鍵時(shí)間的長(zhǎng)短對(duì)事件進(jìn)行劃分,即在指定時(shí)間范圍為一個(gè)事件。因此程序設(shè)計(jì)的關(guān)鍵是實(shí)現(xiàn)按鍵時(shí)間與事件狀態(tài)的轉(zhuǎn)換,轉(zhuǎn)換關(guān)系如圖2所示。
按鍵檢測(cè)程序每個(gè)系統(tǒng)節(jié)拍執(zhí)行一次。按鍵檢測(cè)步驟如下:
(1) 在沒(méi)有檢測(cè)到按鍵按下時(shí),設(shè)定按鍵狀態(tài)為NONE和前次按鍵值為NOKEY;
(2) 在檢測(cè)到有按鍵按下并且按下的按鍵與前一次檢測(cè)到的按鍵值不一樣時(shí),更新前次按鍵值為新的鍵值,初始化兩個(gè)按鍵計(jì)數(shù)器keyCntr和keyCntrLong的值為0;
(3) 在檢測(cè)到有按鍵按下并且按下的按鍵與前一次檢測(cè)到的按鍵值一樣時(shí),兩個(gè)按鍵計(jì)數(shù)器keyCntr和keyCntrLong的值自增1:
若當(dāng)前按鍵狀態(tài)是NONE,在keyCntr計(jì)數(shù)值超過(guò)按鍵消抖時(shí)間 KEY_TIME_PRESS后,觸發(fā)按鍵按下PRESS事件,按鍵狀態(tài)更新為PRESS;
若當(dāng)前按鍵狀態(tài)是PRESS,在keyCntr計(jì)數(shù)值超過(guò)按鍵短按住時(shí)間;KEY_TIME_SHORT_HOLD后,觸發(fā)按鍵短按住SHORT_HOLD事件,按鍵狀態(tài)更新為SHORT_HOLD,同時(shí)keyCntr減去一個(gè)按鍵短按住后重復(fù)時(shí)間KEY_TIME_SHORT_HOLD_REPEAT;
圖2 按鍵狀態(tài)分析
若當(dāng)前按鍵狀態(tài)是SHORT_HOLD_REPEAT,在keyCntr計(jì)數(shù)值超過(guò)按鍵短按住時(shí)間KEY_TIME_SHORT_HOLD后,再次觸發(fā)SHORT_HOLD_REPEAT事件,同時(shí)keyCntr減去一個(gè)按鍵短按住后重復(fù)時(shí)間KEY_TIME_SHORT_HOLD_REPEAT;此時(shí)如果keyCntrLong計(jì)數(shù)值也超過(guò)按鍵長(zhǎng)按住時(shí)間KEY_TIME_LONG_HOLD,則觸發(fā)LONG_HOLD事件,按鍵狀態(tài)更新為L(zhǎng)ONG_HOLD,同時(shí)keyCntrLong減去一個(gè)按鍵長(zhǎng)按住后重復(fù)時(shí)間KEY_TIME_LONG_HOLD_REPEAT;
若當(dāng)前按鍵狀態(tài)是LONG_HOLD,在keyCntrLong計(jì)數(shù)值超過(guò)按鍵長(zhǎng)按住時(shí)間KEY_TIME_LONG_HOLD后,觸發(fā)LONG_HOLD_REPEAT事件,按鍵狀態(tài)更新為L(zhǎng)ONG_HOLD_REPEAT,同時(shí)keyCntrLong減去一個(gè)按鍵長(zhǎng)按住后重復(fù)時(shí)間KEY_TIME_LONG_HOLD_REPEAT;
若當(dāng)前按鍵狀態(tài)是LONG_HOLD_REPEAT,在keyCntrLong計(jì)數(shù)值超過(guò)按鍵長(zhǎng)按住時(shí)間KEY_TIME_LONG_HOLD后,再次觸發(fā)LONG_HOLD_REPEAT事件,同時(shí)keyCntrLong減去一個(gè)按鍵長(zhǎng)按住后重復(fù)時(shí)間KEY_TIME_LONG_HOLD_REPEAT;
(4)在檢測(cè)到按鍵松開(kāi)時(shí):
若當(dāng)前按鍵狀態(tài)是PRESS,觸發(fā)PRESS_RELEASE事件;
若當(dāng)前按鍵狀態(tài)是SHORT_HOLD或SHORT_HOLD_REPEAT,觸發(fā)SHORT_HOLD_RELEASE事件;
若當(dāng)前按鍵狀態(tài)是LONG_HOLD或LONG_HOLD_REPEAT,觸發(fā)LONG_HOLD_RELEASE事件;
并且將按鍵狀態(tài)更新為NONE,前次按鍵值為NOKEY。
程序設(shè)計(jì)的關(guān)鍵點(diǎn)是識(shí)別當(dāng)前的狀態(tài)及觸發(fā)的外部條件,進(jìn)行下一狀態(tài)的轉(zhuǎn)換,程序流程如圖3所示。該程序思路適合獨(dú)立式按鍵、矩陣式按鍵和ADC按鍵等多種結(jié)構(gòu)形式的按鍵電路,在檢測(cè)端口狀態(tài)時(shí),若是獨(dú)立式按鍵就直接讀取端口;若是矩陣式按鍵則需要設(shè)置相應(yīng)端口輸出輸入狀態(tài)后再讀取端口,注意防止損壞端口的可能性;若是ADC按鍵則要切換相應(yīng)ADC通道后再讀取端口,同時(shí)要保證通道切換的正確性,且需要多次讀取平均值,如果是組合按鍵的成員必須在不同的ADC端口上。
圖3 按鍵檢測(cè)流程
按鍵狀態(tài)的識(shí)別及狀態(tài)的轉(zhuǎn)換,最終目的是讓對(duì)應(yīng)的事件得到響應(yīng),程序設(shè)計(jì)上通過(guò)將按鍵值、按鍵狀態(tài)和按鍵事件一一進(jìn)行匹配,使用一個(gè)二維數(shù)組將三者一一對(duì)應(yīng)起來(lái)。按鍵值索引keyIndex,按鍵狀態(tài)索引keyStateIndex,按鍵事件表KeyEventTable。
KeyEventTable [keyIndex][ keyStateIndex]=
{
// keyIndex=0
{KEY0_NONE, KEY0_PRESS, KEY0_PRESS_RELEASE, KEY0_SHORT_HOLD, KEY0_SHORT_HOLD_RELEASE, KEY0_SHORT_HOLD_REPEAT, KEY0_LONG_HOLD, KEY0_LONG_HOLD_RELEASE, KEY0_LONG_HOLD_REPEAT},
// keyIndex=1
{KEY1_NONE, KEY1_PRESS, KEY1_PRESS_RELEASE, KEY1_SHORT_HOLD, KEY1_SHORT_HOLD_RELEASE, KEY1_SHORT_HOLD_REPEAT, KEY1_LONG_HOLD, KEY1_LONG_HOLD_RELEASE, KEY1_LONG_HOLD_REPEAT},
……
};
本文設(shè)計(jì)了一種高兼容性的按鍵檢測(cè)程序。程序的思路是預(yù)先將所有按鍵可能產(chǎn)生的按鍵動(dòng)作編上編號(hào),并用二維數(shù)組將編號(hào)和對(duì)應(yīng)的執(zhí)行函數(shù)聯(lián)系到一起,然后在程序運(yùn)行時(shí),根據(jù)當(dāng)前檢測(cè)到的按鍵值和按鍵狀態(tài)值,查找到對(duì)應(yīng)的按鍵事件執(zhí)行函數(shù)并執(zhí)行之。
此程序設(shè)計(jì)具有較高的兼容性和實(shí)用性,可應(yīng)用于單片機(jī)嵌入式系統(tǒng)中的獨(dú)立式按鍵、鍵盤按鍵、ADC按鍵。在使用此程序進(jìn)行單片機(jī)系統(tǒng)設(shè)計(jì)中應(yīng)注意如下幾個(gè)問(wèn)題:
(1)需要占用一部分ROM空間來(lái)存放按鍵事件表;
(2)各個(gè)按鍵值、按鍵狀態(tài)和按鍵事件表的排序需要一一對(duì)應(yīng),如果對(duì)應(yīng)出錯(cuò),則相應(yīng)的按鍵功能必然出錯(cuò);
(3)按鍵事件沒(méi)有超出255個(gè)時(shí),按鍵事件表的大小就等于按鍵事件數(shù)目,字節(jié)如果超出了255個(gè),則按鍵事件表的大小將需要占用按鍵事件數(shù)*2字節(jié)的ROM空間,實(shí)際應(yīng)用中應(yīng)該對(duì)按鍵事件數(shù)目加以限制。