解析單片機(jī)鍵盤(pán)去抖程序
用單片機(jī)或ARM做的產(chǎn)品經(jīng)常會(huì)遇到有鍵盤(pán)輸入的產(chǎn)品,而鍵盤(pán)輸入有一個(gè)繞不過(guò)去的問(wèn)題就是:鍵盤(pán)去抖。見(jiàn)下圖
當(dāng)按鍵開(kāi)關(guān)閉合或者斷開(kāi)時(shí)各有一段電平不穩(wěn)定的時(shí)期,按鍵開(kāi)關(guān)在閉合時(shí)不會(huì)馬上就穩(wěn)定的接通,在斷開(kāi)時(shí)也不會(huì)一下子徹底斷開(kāi),而是在閉合和斷開(kāi)的瞬間伴隨了一連串的電平抖動(dòng)。這種抖動(dòng)一般都在10ms左右。為了確保程序?qū)Π存I的一次閉合或者一次斷開(kāi)只響應(yīng)一次,必須進(jìn)行按鍵的去抖處理。當(dāng)檢測(cè)到按鍵狀態(tài)變化時(shí),不是立即去響應(yīng)動(dòng)作,而是先等待閉合或斷開(kāi)穩(wěn)定后再進(jìn)行處理。
按鍵去抖方法可分為硬件去抖和軟件去抖,硬件去抖不在本文的討論中,本文只討論軟件去抖。
一般的軟件去抖就是程序在檢測(cè)到按鍵閉合或斷開(kāi)時(shí)調(diào)用一段延時(shí)子程序(在C語(yǔ)言中叫函數(shù)),程序在此死等10ms或更長(zhǎng)。延時(shí)過(guò)后再檢測(cè)按鍵的狀態(tài)是否與延時(shí)前的狀態(tài)一致,若一致就執(zhí)行鍵盤(pán)程序部分,若不一致,則跳過(guò)執(zhí)行鍵盤(pán)程序。
這種方法在程序工作量不是很大時(shí)是沒(méi)有問(wèn)題的。但在一些CPU負(fù)荷量比較大的程序中,特別是在一些程序運(yùn)轉(zhuǎn)中有比較多的在不確定時(shí)間就會(huì)發(fā)生的中斷的情況下(外部中斷、串口中斷、定時(shí)器中斷等),在這里死等,就有可能造成某部分程序不能很好地被執(zhí)行,甚至程序跑飛等嚴(yán)重問(wèn)題。
本人經(jīng)過(guò)多年的編程,總結(jié)了一套解決這問(wèn)題的方法,供大家參考。
程序是用51匯編語(yǔ)言寫(xiě)的,大家若要用C語(yǔ)言編寫(xiě),參考這流程圖改一下就成。
先解釋這流程圖中的變量和子程序:
KSTEP: 步進(jìn)指示變量,當(dāng)程序從主程序進(jìn)入到此子程序后,立刻根據(jù)這KSTEP的值跳到相應(yīng)的程序段。
KEYSCAN:讀鍵盤(pán)子程序,若你的按鍵數(shù)量不多的話(huà),直接讀IO口。按鍵數(shù)量多的話(huà),就要用矩陣方式讀鍵盤(pán),這里不作贅述。
HASK: 位變量,讀鍵盤(pán)子程序中的位變量,當(dāng)讀鍵盤(pán)子程序KEYSCAN檢測(cè)到有鍵閉合時(shí)置“1”,反之置“0”。
R2: 鍵值變量,讀鍵盤(pán)子程序KEYSCAN讀出的鍵值。
KVALU: 鍵值變量,R2的鍵值送到這里,供此子程序下一次判斷或主程序使用。
K20MS: 20ms計(jì)時(shí)器變量,當(dāng)?shù)谝淮螜z測(cè)到有鍵閉合時(shí)往里面送值10。程序初始化中設(shè)定定時(shí)器中斷為2ms時(shí)間間隔。進(jìn)入定時(shí)器中斷后,首先判斷K20MS是否為0?若為0則直接退出定時(shí)器中斷;若不為0則將K20MS減1后再退出定時(shí)器中斷。這樣K20MS變量從10減到0時(shí)間為20ms。鍵斷開(kāi)時(shí)也是一樣地執(zhí)行。
KAVA: 位變量,告訴主程序:鍵閉合(斷開(kāi))有效。
程序解釋?zhuān)?/p>
1. 程序初始化時(shí)KSTEP的值為0,所以一進(jìn)入本子程序,程序馬上就跳到標(biāo)號(hào)KSC0處,在此處調(diào)用讀鍵盤(pán)子程序KEYSCAN。
1.1 從KEYSCAN出來(lái)后,若位變量HASK的值為0,說(shuō)明沒(méi)有鍵閉合,程序直接跳到標(biāo)號(hào)RET處退出。
1.2 若位變量HASK的值為1,就是有鍵閉合,此時(shí)將數(shù)值1送入步進(jìn)指示變量KSTEP中,便于下次進(jìn)入本子程序時(shí),程序直接跳到標(biāo)號(hào)KSC1處。再將從KEYSCAN子程序讀出來(lái)的鍵值送入變量KVALU中,用于下次再調(diào)用讀鍵盤(pán)子程序KEYSCAN時(shí)與R2讀出的鍵值進(jìn)行比較。
最后將數(shù)值10送入20ms計(jì)時(shí)器變量K20MS中,用于2ms定時(shí)器中斷后減1,然后退出子程序。
2. 當(dāng)主程序再次調(diào)用本子程序時(shí),程序馬上就跳到標(biāo)號(hào)KSC1處。
2.1 在此處首先判別20ms計(jì)時(shí)器變量K20MS是否減到0(也就是判別20ms延時(shí)到了沒(méi)有?),若K20MS不為0(20ms延時(shí)還沒(méi)有到),則立即退出。
2.2 若K20MS為0(說(shuō)明20ms延時(shí)時(shí)間到了),再次調(diào)用讀鍵盤(pán)子程序KEYSCAN。調(diào)用KEYSCAN子程序后,再次判別位變量HASK是否有效?
2.2.1 若HASK無(wú)效,說(shuō)明上次(KSC0處)可能是受到一次干擾。于是復(fù)位KSTEP(清0),退出。使下次調(diào)用本程序時(shí),又從頭開(kāi)始。
2.2.2 若HASK有效,則將這次從KEYSCAN讀出的鍵值與上次讀出并存在KVALU中的鍵值進(jìn)行比較。
2.2.2.1 若比較值不同,則程序跳到標(biāo)號(hào)KE1處,將新的鍵值存入KAVALU中,20ms后再調(diào)用KEYSCAN子程序,再次比較。
2.2.2.2 若比較值相同,則說(shuō)明本次鍵閉合有效,于是置位KAVA(當(dāng)主程序是鍵按下執(zhí)行時(shí)),告訴主程序,鍵閉合有效,可以執(zhí)行此鍵所要做的程序了。同時(shí)將數(shù)值2送入步進(jìn)指示變量KSTEP中,便于下次進(jìn)入本子程序時(shí),程序直接跳到標(biāo)號(hào)KSC2處。最后將數(shù)值10送入20ms計(jì)時(shí)器變量K20MS中,在下次進(jìn)入KSC2標(biāo)號(hào)處,也得等20ms之后再判別鍵是否斷開(kāi)。
3. 現(xiàn)在主程序調(diào)用本子程序時(shí),程序馬上就跳到標(biāo)號(hào)KSC2處,在此也一樣,首先判別20ms計(jì)時(shí)器變量K20MS是否減到0(也就是判別20ms延時(shí)到了沒(méi)有?),若K20MS不為0(20ms延時(shí)還沒(méi)有到),則立即退出。若K20MS為0,調(diào)用讀鍵盤(pán)子程序KEYSCAN。
調(diào)用KEYSCAN子程序后,判別位變量HASK是否有效?
3.1 若HASK無(wú)效,說(shuō)明按鍵可能被釋放斷開(kāi),于是將數(shù)值3送入步進(jìn)指示變量KSTEP中,便于下次進(jìn)入本子程序時(shí)程序可以直接跳到標(biāo)號(hào)KSC3處。最后將數(shù)值10送入20ms計(jì)時(shí)器變量K20MS中,在下次進(jìn)入KSC3標(biāo)號(hào)處,也得等20ms之后再判別鍵是否繼續(xù)斷開(kāi)狀態(tài)。
3.2 若HASK有效,說(shuō)明按鍵繼續(xù)閉合狀態(tài),再比較KEYSCAN讀出的鍵值與上次讀出在KVALU中的鍵值進(jìn)行比較。
3.2.1 若比較值不同,則程序跳到標(biāo)號(hào)KE0處,重新開(kāi)始。
3.2.1 若比較值相同,則說(shuō)明按鍵還沒(méi)有斷開(kāi),繼續(xù)將數(shù)值10送入20ms計(jì)時(shí)器變量K20MS中,等20ms之后再進(jìn)入標(biāo)號(hào)KSC2處,再次判別按鍵是否斷開(kāi)。
4. 當(dāng)主程序調(diào)用本子程序時(shí),程序程序馬上跳到標(biāo)號(hào)KSC3處,還是首先判別20ms計(jì)時(shí)器變量K20MS是否減到0,若K20MS不為0(20ms延時(shí)還沒(méi)有到),則立即退出。若K20MS為0,調(diào)用讀鍵盤(pán)子程序KEYSCAN。
調(diào)用KEYSCAN子程序后,判別位變量HASK是否有效?
4.1 若HASK無(wú)效,說(shuō)明按鍵已經(jīng)完全釋放斷開(kāi),于是將數(shù)值0送入步進(jìn)指示變量KSTEP中,便于下次進(jìn)入本子程序時(shí),程序從頭開(kāi)始,同時(shí)置位KAVA(當(dāng)主程序是鍵釋放執(zhí)行時(shí)),告訴主程序,鍵釋放有效,可以執(zhí)行此鍵所要做的程序了。[!--empirenews.page--]
4.2 如果位變量HASK繼續(xù)有效,說(shuō)明又有鍵閉合了(雖然這種概率比較小,但程序得編進(jìn)去),根據(jù)新鍵值與老鍵值的相同與不同,分別跳到標(biāo)號(hào)KE3處,或者標(biāo)號(hào)KE0處執(zhí)行。
說(shuō)明:KE3標(biāo)號(hào)和KE7標(biāo)號(hào)下面都有SETB KAVA,實(shí)際編程時(shí)只用一次,根據(jù)你的主程序是在鍵按下執(zhí)行還是鍵釋放執(zhí)行選用。
本程序的特點(diǎn)就是:在等鍵閉合或斷開(kāi)去抖的那20ms時(shí)間,不是死等,而是做好標(biāo)記及置好必要的變量值后立即退出到主程序去做其他事情。程序每次從進(jìn)入到退出這個(gè)子程序中所花的時(shí)間一般為十幾微秒(不含讀鍵盤(pán)子程序KEYSCAN所花的時(shí)間,KEYSCAN花的時(shí)間根據(jù)按鍵數(shù)量的多少而不同,一般為幾個(gè)微秒到幾十微秒)。