唐 飛, 查長禮
(安慶師范學院 物理與電氣工程學院,安徽 安慶 246133)
按鍵是用戶與嵌入式系統(tǒng)進行交互的常用設備,因其簡單實用、成本低,因而得到了廣泛應用。嵌入式系統(tǒng)因體積所限,一般使用非編碼按鍵,依靠程序識別按鍵的動作和按鍵編碼。按鍵控制程序應能夠管理按鍵按下、按鍵防抖、鍵值判別、按鍵彈起等任務,并識別單擊、雙擊、長按、連發(fā)等按鍵模式。
當今的嵌入式系統(tǒng)體積越來越小,需要實現(xiàn)一鍵多“能”,因此,識別按鍵的單擊、雙擊、長按的功能也越來越受到重視。
嵌入式系統(tǒng)工作時,用戶使用按鍵的時間對于系統(tǒng)而言是隨機的,因此,系統(tǒng)需要采用一定的策略對按鍵進行掃描,以識別按鍵的動作。常用按鍵掃描方法有以下幾種[1]。
利用CPU的空閑時間不斷對按鍵進行掃描,直到檢測到按鍵動作,轉去處理按鍵為止。此種方法簡單易行,程序容易編寫,但CPU效率低,CPU繁忙時,按鍵不能得到及時響應,實時性差。
按鍵按下時觸發(fā)中斷,CPU暫停當前執(zhí)行程序,轉向處理按鍵,處理完畢再返回主程序運行。此種方法CPU的利用率高,但占用了中斷系統(tǒng)資源,中斷系統(tǒng)發(fā)生沖突時不能及時響應按鍵動作。
借鑒了操作系統(tǒng)中時間片的思想,使用定時器每隔一定時間對包括按鍵在內的各個任務進行掃描,如按鍵動作,則處理按鍵,否則執(zhí)行下一項任務。該方法CPU利用率高,各個任務劃分時間片輪流執(zhí)行,不會因為某個任務占用CPU時間過長造成其它任務沒有響應。
綜上所述,定時器掃描法在處理多任務時具有較大的優(yōu)勢,因此在嵌入式系統(tǒng)中應優(yōu)先使用,同時,該方法還需要配合有限狀態(tài)機才能達到理想的效果。
有限狀態(tài)機(Finite-State Machine,F(xiàn)SM)或有限狀態(tài)自動機簡稱狀態(tài)機,是表示有限個狀態(tài)以及在這些狀態(tài)之間的轉移和動作等行為的數(shù)學模型[2]。有限狀態(tài)機的思想廣泛應用于硬件控制電路設計,也是軟件上常用的一種處理方法。它把復雜的控制邏輯分解成有限個穩(wěn)定狀態(tài),在每個狀態(tài)上判斷事件,變連續(xù)處理為離散數(shù)字處理,符合計算機的工作特點。同時,有限狀態(tài)機具有有限個狀態(tài),所以可以在實際的工程上實現(xiàn)。
有限狀態(tài)機可歸納為4個要素,即現(xiàn)態(tài)、條件、動作、次態(tài)?,F(xiàn)態(tài)和條件是事件起因,動作和次態(tài)是最后的結果。當條件被滿足時將會觸發(fā)做動作,動作執(zhí)行完畢后,可以遷移到新的狀態(tài)(次態(tài)),也可以保持原狀態(tài)。
依據(jù)有限狀態(tài)機的狀態(tài)轉移關系和轉移條件,可以把一個非常復雜的事件變成一個依據(jù)狀態(tài)編碼內容進行轉移的多分支的結構,很容易用C語言來實現(xiàn)。因此,把有限狀態(tài)機作為一種方法導入程序設計中,實現(xiàn)程序對流程和行為的準確分析和表達,縮短了系統(tǒng)的開發(fā)時間,增強了系統(tǒng)的可靠性[3-7]。
在嵌入式系統(tǒng)中,按鍵識別的任務是確定按鍵的鍵值和按鍵動作模式。鍵值可由程序判別并分配,常用的按鍵模式分為單鍵模式和復鍵模式兩類。單鍵模式一次按鍵只輸出一個有效按鍵,而復鍵模式一次按鍵可以輸出多個有效按鍵,通常通過按鍵按下時間的長短來區(qū)別。單鍵類一般有3種模式:琴鍵模式、單發(fā)模式和乒乓模式;復鍵類一般有長按模式、連發(fā)模式和組合鍵模式[8]。具體區(qū)別見表1。
表1 按鍵模式分類
在STM32系統(tǒng)中,單發(fā)模式、長按模式和連發(fā)模式較為常用,單發(fā)模式組合還可形成雙擊動作。進一步分析每一次的按鍵動作,也可以看作一個狀態(tài)機,每次的擊鍵動作使按鍵形成了彈起、抖動、短按、長按和釋放等狀態(tài)。當按鍵按下之后觸發(fā)動作,動作執(zhí)行完畢之后按鍵遷移到新的狀態(tài),直至最終確定按鍵的模式。按鍵的狀態(tài)遷移如圖1所示[9]。
圖1 按鍵狀態(tài)轉換圖
嵌入式系統(tǒng)中,基于Cortex-M3架構的32位ARM處理器發(fā)展迅速,STM32微控制器是意法半導體(ST Microelectronics)公司推出的基于Cortex-M3內核的系列微處理器,具有高性能、低成本、低功耗的特點,得到了廣泛應用。因此,研究基于STM32的按鍵驅動程序具有十分積極的意義[10]。
GPIO(General Purpose Input Output)是STM32的輸入、輸出設備,STM32提供了80個雙向GPIO口,分布在A~E這5個端口中。文中設按鍵接于GPIOA.0口,通過讀取GPIOA.0口的狀態(tài)即可檢測出按鍵的狀態(tài)。STM32的按鍵接口如圖2所示。
圖2 STM32的按鍵接口
根據(jù)上文分析,程序采用定時掃描法定時對按鍵進行掃描。綜合考慮按鍵的響應速度和其它任務需求,確定按鍵掃描時間為10ms。因此,需要使用STM32中的SysTick Timer進行定時,配置相應的時鐘,產(chǎn)生中斷標志位,控制主程序每隔10ms掃描一次按鍵。
STM32通過GPIO口掃描按鍵主要有以下幾個步驟:
1)開啟所用端口的時鐘;
2)配置GPIOA.0口為上拉輸入模式;
3)配置SysTick時鐘,使之每10ms產(chǎn)生一個標志,控制對按鍵的掃描;
4)調用按鍵掃描函數(shù)掃描按鍵。
按鍵的每次擊鍵動作可分為4個狀態(tài),按鍵的動作模式分為3種,分別見表2和表3。
表2 按鍵狀態(tài)表
表3 按鍵模式表
主程序調用函數(shù)KeyScan()掃描按鍵,若按鍵動作,則判明按鍵狀態(tài),在動作的觸發(fā)下完成按鍵狀態(tài)的遷移,返回按鍵動作模式的類型。具體描述如下:
1)按鍵初始為彈起狀態(tài),標記為狀態(tài)0。每隔10ms主程序調用函數(shù)掃描按鍵,若GPIOA.0電平為1,則按鍵未按下,按鍵狀態(tài)保持在狀態(tài)0;若GPIOA.0電平為0,則按鍵按下,按鍵狀態(tài)遷移至防抖狀態(tài)。
2)按鍵進入防抖狀態(tài),標記為狀態(tài)1。每隔10ms主程序再次掃描按鍵,若GPIOA.0電平為1,則上次得到的按鍵按下信息是由于抖動或外界干擾造成的誤判,按鍵狀態(tài)返回狀態(tài)0;若GPIOA.0電平為0,表明按鍵確實按下,按鍵狀態(tài)遷移至短按狀態(tài)。
3)按鍵進入短按狀態(tài),標記為狀態(tài)2,同時啟動計數(shù)變量對按鍵按下的時間開始計數(shù)。每隔10ms主程序掃描一次按鍵,若GPIOA.0電平為1,則按鍵已經(jīng)彈起,按鍵狀態(tài)返回狀態(tài)0,同時返回按鍵動作模式為單擊S;若GPIOA.0電平為0,表明按鍵持續(xù)按下,按鍵狀態(tài)保持在短按狀態(tài),同時計數(shù)變量開始加1計數(shù),若計時時間(計數(shù)值乘以10ms)大于1s而按鍵狀態(tài)仍維持在狀態(tài)2,表明按鍵動作模式為長按,按鍵狀態(tài)轉移至長按狀態(tài)。
4)按鍵進入長按狀態(tài),標記為狀態(tài)3。每隔10ms主程序掃描一次按鍵,若GPIOA.0電平為1,則按鍵已經(jīng)彈起,按鍵狀態(tài)返回狀態(tài)0,此次長按狀態(tài)結束,返回按鍵動作模式為長按L;若GPIOA.0電平為0,表明該長按狀態(tài)持續(xù),按鍵保持在長按狀態(tài)3。
按鍵掃描的函數(shù)如下所示,返回按鍵模式類型。其中,變量Key存儲讀取的按鍵引腳電平,變量KeyState表示按鍵的狀態(tài),變量KeyPress-Time記錄按鍵按下時的計數(shù)數(shù)值,計數(shù)值乘以10ms即為按鍵按下的時間,變量KeyPressStyle表示按鍵模式的類型。因變量KeyState和Key-PressTime在函數(shù)每次調用時需要保存上一次調用時的數(shù)值,因此將其設置為靜態(tài)變量。
#define N 0#define S 1#define L 2#define C 3#define KEY_ON 0
unsigned char KeyScan()
{
static unsigned char KeyState=0,KeyPress-Time=0;//按鍵狀態(tài),按鍵按下時間
unsigned char Key,KeyPressStyle;//按鍵動作模式
KeyPressStyle=N;//按鍵動作模式初始化為未正常按
Key=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==KEY_ON//檢測按鍵電平
switch(KeyState)
{
case 0://彈起狀態(tài)
if(!Key)KeyState=1;//按鍵按下則轉移至狀態(tài)1 else KeyState=0;//按鍵彈起返回至狀態(tài)0
break;
case 1://防抖狀態(tài)
if(!Key){KeyState=2;KeyPress-Time=0;}//按鍵按下則轉移至狀態(tài)2,計時變量清零
else KeyState=0;
break;
case 2://短按狀態(tài)
if(Key){KeyState=0;KeyPressStyle=S;}//按鍵彈起返回狀態(tài)0,按鍵動作模式為單擊
else//按鍵保持按下
{
KeyPressTime++;//計數(shù)變量加1
if(KeyPressTime>50)KeyState=3;//按鍵按下時間大于1秒,狀態(tài)轉移至狀態(tài)3
}
break;
case 3://長按狀態(tài)
if(Key)KeyState=0;//按鍵彈起返回狀態(tài)0,按鍵動作模式為長按
else KeyPressStyle=L;//按鍵動作模式為長按
break;
}
return KeyPressStyle;//返回按鍵動作模式
}
該函數(shù)由定時器控制,每隔10ms執(zhí)行一次,每次執(zhí)行時讀取GPIOA.0口的電平,存儲在變量Key中,然后進入由switch語句構成的狀態(tài)機中,根據(jù)按鍵狀態(tài)變量KeyState和Key的數(shù)值,確定輸出的按鍵動作類型KeyPressStyle和下一個狀態(tài)KeyState,返回給主調函數(shù)使用,返回值為0,表明按鍵無動作,返回值為1,表明按鍵單擊動作,返回值為2,表明按鍵長按動作。主程序中調用該函數(shù),編寫程序即可實現(xiàn)按鍵的單擊、長按、連發(fā)、雙擊等功能,極大地擴展了單個按鍵的作用。
介紹了有限狀態(tài)機的原理和在嵌入式系統(tǒng)上進行程序設計的方法,并在此基礎上研究了基于定時器的按鍵掃描識別方法。將有限狀態(tài)機的思想引入按鍵識別的程序設計中,把按鍵過程劃分為多個狀態(tài),以用戶的按鍵動作驅動在按鍵各個狀態(tài)之間的遷移,在STM32平臺上實現(xiàn)了對單個按鍵單擊、長按、連擊的判別,擴展了單個按鍵的應用。整個過程高效簡潔,降低了系統(tǒng)的復雜性,提升了系統(tǒng)的可靠性,是一種適合工程實際應用的方案。
[1]章樂多,蘭琴麗.嵌入式設備的按鍵設計優(yōu)化研究[J].廣西輕工業(yè),2011(7):77-78.
[2]百度百科.有限狀態(tài)機[EB/OL].[2013-01-21].http://baike.baidu.com/view/115336.htm.
[3]劉媛媛.51單片機用有限狀態(tài)機算法實現(xiàn)順序控制[J].機械工程與自動化,2011(4):42-44.
[4]何劍宇,劉兢兢.有限狀態(tài)機建模在嵌入式按鍵設計中的應用[J].沈陽師范大學學報:自然科學版,2012,30(2):168-171.
[5]秦國棟.有限狀態(tài)機的嵌入式Linux按鍵驅動設計[J].單片機與嵌入式系統(tǒng)應用,2010(4):79-81.
[6]黃新林,王鋼,劉春剛.有限狀態(tài)機在單片機編程中的應用[J].哈爾濱理工大學學報,2008,13(4):7-9.
[7]管庶安.單片機程序的狀態(tài)機模型[J].武漢工業(yè)學院學報,2004,23(2):1-2.
[8]肖看,朱光喜,劉文予.FPGA按鍵模式的研究與設計[J].電子技術應用,2008(10):45-47.
[9]馬潮.AVR單片機嵌入式系統(tǒng)原理與應用實踐[M].北京:北京航空航天大學出版社,2007.
[10]蒙博宇.STM32自學筆記[M].北京:北京航空航天大學出版社,2012.