陳曉麗
(珠海格力電器股份有限公司 廣東珠海 519070)
在單片機(jī)控制系統(tǒng)中,按鍵是最常用的輸入設(shè)備,是人-機(jī)接口中非常重要的組成部分,家用電器或其它消費(fèi)性電子產(chǎn)品中就經(jīng)常使用按鍵來(lái)實(shí)現(xiàn)功能控制或選擇、配置信息參數(shù)等。隨著產(chǎn)品功能越來(lái)越豐富,按鍵的作用越來(lái)越多樣化,同一個(gè)按鍵被賦予了多種功能(即一鍵多功能),比如:短按一個(gè)按鍵是功能模式切換,而長(zhǎng)按這一個(gè)按鍵又可以是關(guān)機(jī)的功能,這使得按鍵的檢測(cè)和處理更加復(fù)雜,也更加重要。本文講述了一種按鍵的多種狀態(tài)判斷方法和相應(yīng)的按鍵消抖處理方法。
按鍵都是以開(kāi)關(guān)狀態(tài)來(lái)輸入數(shù)據(jù)的。根據(jù)硬件電路上對(duì)按鍵檢測(cè)的不同來(lái)區(qū)分,可以將按鍵檢測(cè)電路分為兩種類(lèi)型,一類(lèi)是I/O掃描式按鍵電路,另一類(lèi)是AD采樣式按鍵電路。這兩種電路在按鍵檢測(cè)原理上存在區(qū)別,I/O掃描式按鍵檢測(cè)是根據(jù)I/O的高低電平來(lái)判斷是否有鍵按下,而AD采樣式按鍵檢測(cè)是根據(jù)I/O的AD采樣值范圍來(lái)判斷是否有鍵按下。雖然兩類(lèi)按鍵的檢測(cè)方法不同,但是按鍵的處理方法是一樣的,包括狀態(tài)判斷處理和消抖處理。
為了實(shí)現(xiàn)一個(gè)按鍵有多種功能,可以根據(jù)操作按鍵的不同狀態(tài)來(lái)進(jìn)行處理,這就需要判斷出按鍵的各種狀態(tài)。按鍵的狀態(tài)有多種類(lèi)型,包括短按、長(zhǎng)按、連按、短按釋放、長(zhǎng)按釋放等。
這里介紹一種程序上判斷按鍵多種狀態(tài)的方法。
程序代碼如下:
#define NO_KEY 0xFF //按鍵沒(méi)有按下
#define TASK_KEY_LONG 0x20//按鍵長(zhǎng)按
#define TASK_KEY_HOLD 0x40//按鍵連按
#define TASK_KEY_SHORT_UP 0x80//按鍵短按后釋放
#define TASK_KEY_LONG_UP 0x60//按鍵長(zhǎng)按后釋放
#define KEY_SCAN_TIMES 3 //短按鍵的次數(shù)
#define KEY_LONG_TIMES 150 //長(zhǎng)按鍵的次數(shù)
#define KEY_HOLD_TIMES 35 //連按鍵的次數(shù)
void KeyScan(void)
{
static unsigned char keyCounter; //用于區(qū)分短按、長(zhǎng)按、連按等
static unsigned char keyValue; //按鍵穩(wěn)定狀態(tài)值
unsigned char keyTmp; //當(dāng)前按鍵狀態(tài)
unsigned char key_return = NO_KEY;
static bool long_key; //按鍵長(zhǎng)按標(biāo)志
keyTmp = KeyDetect(); //獲取當(dāng)前的按鍵
狀態(tài),keyTmp的每一位對(duì)應(yīng)一個(gè)按鍵狀態(tài),0表
示按鍵按下
if (keyTmp == NO_KEY) //釋放按鍵或者
按鍵沒(méi)有按下
{
if (long_key == 1)
{
key_return = keyValue | TASK_KEY_
LONG_UP; //長(zhǎng)按后釋放按鍵
}
else if ((keyCounter < KEY_LONG_
TIMES ) && (keyCounter > KEY_SCAN_
TIMES))
{
key_return = keyValue | TASK_KEY_
SHORT_UP; //短按后釋放按鍵
}
keyValue = NO_KEY;
long_key = 0;
keyCounter = 0;
}
else if ((keyTmp != keyValue))
{
keyValue = keyTmp;
keyCounter = 0;
long_key = 0;
}
else
{
keyCounter++;
if (keyCounter == KEY_SCAN_TIMES)
{
key_return = keyValue; //短按
}
else if (keyCounter == KEY_LONG_
TIMES )
{
if (long_key == FALSE)
{
long_key = TRUE;
key_return = keyValue | TASK_KEY_LONG; //長(zhǎng)按
}
}
else if (keyCounter == (KEY_LONG_TIMES + KEY_HOLD_TIMES) )
{
key_return = keyValue | TASK_KEY_HOLD; //連按
keyCounter = KEY_LONG_TIMES;
}
}
if ( key_return != NO_KEY)
{
PutMsg(key_return); //產(chǎn)生有按鍵的消息
}
}
函數(shù)KeyScan()的原理:在定時(shí)器的5ms中斷處理中調(diào)用這個(gè)函數(shù),通過(guò)按鍵掃描函數(shù)KeyDetect()獲取按鍵狀態(tài)keyTmp,它的每一位對(duì)應(yīng)一個(gè)按鍵狀態(tài),0表示按鍵按下,1表示按鍵沒(méi)有按下。當(dāng)keyTmp為0xFF時(shí),表示沒(méi)有任何按鍵按下;keyCounter用于累計(jì)按鍵按下的時(shí)間,短按時(shí)間為3×5ms=15ms,也就是說(shuō)當(dāng)檢測(cè)按鍵按下的時(shí)間超過(guò)15ms,就會(huì)發(fā)出一個(gè)短按的消息。同理,長(zhǎng)按時(shí)間為150×5ms=750ms,連按消息是在長(zhǎng)按后35×5=175ms后發(fā)出的,如果按鍵一直按著,就會(huì)每過(guò)175ms發(fā)出一個(gè)連按消息了;而短按釋放的消息就要求按鍵短按和按鍵釋放兩個(gè)條件都先后滿(mǎn)足,長(zhǎng)按釋放的消息也如此。當(dāng)有按鍵按下時(shí),這個(gè)函數(shù)會(huì)產(chǎn)生一個(gè)按鍵消息key_return,它是按鍵類(lèi)型keyValue同按鍵狀態(tài)(包括短按、長(zhǎng)按、連按、短按釋放、長(zhǎng)按釋放)進(jìn)行或運(yùn)算的結(jié)果。這樣處理的優(yōu)點(diǎn):不管有多少個(gè)按鍵,每個(gè)按鍵都可以產(chǎn)生出這5類(lèi)狀態(tài)的消息,按鍵數(shù)量有增加或刪減時(shí),都不用更改代碼,方便維護(hù)。
常用的按鍵為機(jī)械彈性按鍵,也稱(chēng)輕觸開(kāi)關(guān)。機(jī)械按鍵由于觸點(diǎn)的彈性及電壓突跳等原因,在觸點(diǎn)閉合和斷開(kāi)的瞬間會(huì)出現(xiàn)抖動(dòng)。一個(gè)按鍵在閉合時(shí)不會(huì)馬上穩(wěn)定地接通,同樣,在斷開(kāi)時(shí)也不會(huì)一下子斷開(kāi),在閉合和斷開(kāi)的瞬間均伴隨有一連串的抖動(dòng),如圖1所示。
抖動(dòng)時(shí)間的長(zhǎng)短由按鍵的機(jī)械特性來(lái)決定,一般為5~10ms。這種抖動(dòng)對(duì)人來(lái)說(shuō)是感覺(jué)不到的,但對(duì)單片機(jī)來(lái)說(shuō),則是完全可以感知的,因?yàn)閱纹瑱C(jī)的處理速度在微秒級(jí)。按鍵穩(wěn)定閉合時(shí)間的長(zhǎng)短則由操作人員的按鍵動(dòng)作決定的,一般為幾百個(gè)毫秒至數(shù)秒。如果不對(duì)按鍵消除抖動(dòng)則會(huì)引起程序處理的誤操作。為了避免這種抖動(dòng)現(xiàn)象,需進(jìn)行按鍵消抖處理。
按鍵消抖有“硬件消抖”和“軟件消抖”兩種方式。簡(jiǎn)單的硬件消抖是在按鍵接入單片機(jī)的引腳位置并入一個(gè)小電容,利用電容的充放電原理來(lái)實(shí)現(xiàn)消除因抖動(dòng)所產(chǎn)生的毛刺。簡(jiǎn)單的軟件消抖就是用加固定軟件延時(shí)的方法來(lái)去除抖動(dòng),具體方法就是在有按鍵按下時(shí),利用軟件延時(shí)一段時(shí)間后再次檢測(cè)按鍵是否按下,實(shí)際上是避開(kāi)了按鍵按下時(shí)的抖動(dòng)時(shí)間。
這種硬件上并電容或軟件上延時(shí)的處理方式比較簡(jiǎn)單,而且確實(shí)也起到了一定的消抖作用。但是在實(shí)際的生產(chǎn)使用過(guò)程中,按鍵千差萬(wàn)別,不同廠家的物料也參差不齊,各種按鍵操作時(shí)產(chǎn)生的抖動(dòng)時(shí)間不會(huì)是一致的。這樣就會(huì)出現(xiàn)一些問(wèn)題了,電容過(guò)小或延時(shí)過(guò)短,可能會(huì)有按鍵誤動(dòng)作的問(wèn)題;而電容過(guò)大或延時(shí)過(guò)長(zhǎng),會(huì)有按鍵操作后反應(yīng)遲鈍的問(wèn)題。下面介紹一種新的消抖處理方法,抖動(dòng)時(shí)間的長(zhǎng)或短,對(duì)按鍵的操作效果沒(méi)有任何影響。
程序代碼如下:
#define KEY_SHAKE_TIMES 8 //按鍵防抖的掃描次數(shù)
static unsigned char Count; //用于累計(jì)按鍵狀態(tài)穩(wěn)定的次數(shù)
static unsigned char keyStatus; //前次按鍵狀態(tài)
if(keyTmp != keyStatus) //當(dāng)前按鍵狀態(tài)是否與前次按鍵狀態(tài)相同
{
keyStatus = keyTmp;
Count = 0;
return;
}
else
{
if (Count < 8)
{
Count++;
return;
}
}
只要在前面介紹的函數(shù)KeyScan()中獲取keyTmp值后增加這段代碼就可以了。它的原理:每次掃描中都將當(dāng)前按鍵狀態(tài)keyTmp與前次按鍵狀態(tài)keyStatus進(jìn)行比較,如果比較的兩次狀態(tài)不同,表示按鍵處于抖動(dòng)中,會(huì)將用于累計(jì)按鍵狀態(tài)穩(wěn)定次數(shù)的變量Count清零,只有當(dāng)連續(xù)8次比較的狀態(tài)都相同時(shí),才認(rèn)為狀態(tài)穩(wěn)定了,即沒(méi)有抖動(dòng)了。這包括了按鍵按下時(shí)的抖動(dòng)處理和按鍵釋放的抖動(dòng)處理。其中Count設(shè)為8次,每5ms調(diào)用一次,這就是40ms,它的設(shè)定,與抖動(dòng)時(shí)間長(zhǎng)短無(wú)關(guān),只要大于抖動(dòng)周期就可以了。這種處理方法的優(yōu)點(diǎn)在于:它與抖動(dòng)過(guò)程的時(shí)間長(zhǎng)短無(wú)關(guān),而且處理代碼簡(jiǎn)單,不用區(qū)分按下抖動(dòng)或釋放抖動(dòng)。
本文介紹的這種單片機(jī)按鍵狀態(tài)檢測(cè)和消抖處理方法,與一般的按鍵檢測(cè)方法相比,能檢測(cè)更多類(lèi)型的的按鍵狀態(tài),按鍵數(shù)量有增加或刪減時(shí),也不用更改代碼,適用范圍廣,且代碼簡(jiǎn)單,方便維護(hù)。采用的按鍵消抖方法也有很大的改進(jìn),與抖動(dòng)過(guò)程的時(shí)間長(zhǎng)短無(wú)關(guān),不用區(qū)分按下抖動(dòng)或釋放抖動(dòng)。
[1]譚浩強(qiáng) C程序設(shè)計(jì) 清華大學(xué)出版社
[2]劉天時(shí),劉賞,付春 一種單片機(jī)鍵盤(pán)電路設(shè)計(jì)與消抖處理 計(jì)算機(jī)與網(wǎng)絡(luò)2012年 第10期
[3]李運(yùn)兵 微控制器中按鍵處理技巧及應(yīng)用 計(jì)算機(jī)應(yīng)用系統(tǒng) 2010年第2期