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