羅立輝
【摘要】本文首先簡要介紹了獨(dú)立按鍵檢測主動查詢與中斷查詢這兩種方式的優(yōu)缺點(diǎn)。根據(jù)這兩種方式的優(yōu)缺點(diǎn),本文著重討論了具有跟大優(yōu)勢的中斷查詢方式的按鍵檢測方案的實(shí)現(xiàn)原理和過程。并且,針對按鍵普遍存在的抖動問題,本文由簡單到復(fù)雜的過渡方式,詳細(xì)深入地介紹進(jìn)行方案的優(yōu)化和改良的目的以及方法。
【關(guān)鍵詞】按鍵延時(shí)抖動掃描
1主動查詢方式的優(yōu)缺點(diǎn)
對于主動循環(huán)查詢方式,最大的優(yōu)點(diǎn)在于原理簡單,比較適合于MCU處理的任務(wù)不是特別多的情況。如果MCU大循環(huán)的輪詢周期較長,很有可能在按鍵按下的瞬間錯(cuò)過了。這是主動查詢方式的最大缺點(diǎn)。另外,該方式比較浪費(fèi)CPU的時(shí)間資源。檢測按鍵的處理程序需要有個(gè)延遲消抖的過程。對于機(jī)械彈性開關(guān),一般需要的的延遲消抖時(shí)間為10ms。很多程序都是使用一般的延時(shí)函數(shù)來實(shí)現(xiàn)消抖的目的。然而對于很多復(fù)雜的產(chǎn)品,CPU需要執(zhí)行繁重的任務(wù)。在10ms時(shí)間里,CPU可以執(zhí)行上千上萬條其他任務(wù)的匯編指令。
2中斷查詢的方式的優(yōu)缺點(diǎn)
中斷查詢方式主要缺點(diǎn)在于,該方式需要占用MCU一個(gè)外部中斷功能(有些功能簡單、價(jià)錢低廉的MCU,其IO引腳復(fù)用功能資源有限),以及處理程序稍微復(fù)雜一下。與主動查詢相比,中斷查詢方式則有響應(yīng)時(shí)間短、穩(wěn)定可靠等優(yōu)點(diǎn)。一旦有中斷響應(yīng),CPU一定立即響應(yīng)中斷請求。因此,中斷查詢方式不存在“錯(cuò)過”、“漏檢”的情況。按鍵檢測的原理圖設(shè)計(jì)圖1所示:
圖1 按鍵檢測的原理圖設(shè)計(jì)圖
在原理圖1設(shè)計(jì)中,為了檢驗(yàn)按鍵被按下,MCU在中斷服務(wù)程序里,通過程序翻轉(zhuǎn)LED亮滅狀態(tài),以便讀者更容易理解其中的原理機(jī)制。
經(jīng)過測試,每按一下按鍵,LDE燈都實(shí)現(xiàn)了翻轉(zhuǎn)的動作。該函數(shù)基本實(shí)現(xiàn)了預(yù)期的功能。在中斷函數(shù)里,筆者使用了簡單的的概率統(tǒng)計(jì)方法,提高準(zhǔn)確性。這個(gè)中斷服務(wù)程序運(yùn)行的時(shí)間大概為5us*20=0.1ms,遠(yuǎn)遠(yuǎn)少于相對通常的軟件延時(shí)方式所用的10ms,因此提高了CPU對資源的使用效率。但是,以上的中斷服務(wù)程序尚未徹底解決處理機(jī)械彈性按鍵本質(zhì)上所具有的脈沖抖動的問題。首先,筆者在此描述以上程序運(yùn)行起來后有何不良癥狀。有些情況下,按下一次按鍵,LED燈會翻轉(zhuǎn)2遍。有極少數(shù)情況下,LED甚至連續(xù)翻轉(zhuǎn)3到4次。這種現(xiàn)象不是用戶所期望的。這說明以上程序設(shè)計(jì)仍達(dá)不到穩(wěn)定的要求,很有必要進(jìn)行再深入的代碼優(yōu)化。
結(jié)合以上給出的中斷服務(wù)程序,我們就很容易理解LED連續(xù)翻轉(zhuǎn)的現(xiàn)象了。不論圖中提到的前沿抖動還是后沿抖動,均可能導(dǎo)致CPU再次、多次進(jìn)入中斷處理程序。因此,問題的重點(diǎn)在于,如何通過優(yōu)化算法和代碼,在按鍵抖動期間,避免多次進(jìn)入中斷,或者避免進(jìn)入中斷后重復(fù)執(zhí)行相同的任務(wù)。本文從實(shí)現(xiàn)的難易程度這個(gè)角度考慮,選擇了后者,即結(jié)合MCU定時(shí)器的功能,達(dá)到了“避免進(jìn)入中斷后重復(fù)執(zhí)行相同的任務(wù)”的目的。
以下筆者講解了在原有程序的基礎(chǔ)上,如何結(jié)合MCU定時(shí)器的功能,進(jìn)一步徹底解決按鍵抖動帶來的不穩(wěn)定問題??紤]到有部分讀者的基礎(chǔ)可能不是太好,為了降低本文設(shè)計(jì)思路的理解難度,筆者認(rèn)為很有必要先給讀者簡要介紹一下有關(guān)定時(shí)器幾個(gè)非常經(jīng)典的函數(shù)及其功能意義。關(guān)于定時(shí)器,有以下5個(gè)相關(guān)的函數(shù):
函數(shù)1:void clock_init(void)
對于函數(shù)1,顧名思義,該函數(shù)就是要對定時(shí)器temer進(jìn)行初始化,否則函數(shù)2~5無效。
函數(shù)2:void timer_set(struct timer *t, clock_time_t interval)
該函數(shù)用于設(shè)置某定時(shí)器timer需要的時(shí)間間隔interval大?。ㄒ詍s為單位)。這就好比如有個(gè)漏水的水龍頭。水龍頭間隔恒定時(shí)間里每一滴的滴水大小,相當(dāng)于函數(shù)里定時(shí)器的最小計(jì)數(shù)單位。水龍頭下面有個(gè)刻有刻度的水杯。而這里的interval,則相當(dāng)于水杯的容量。
函數(shù)3:int timer_expired(struct timer *t)//判斷定時(shí)器timer是否時(shí)間滿了,為真返回1,否則返回0。這就好比如,某人外出走開了,在她回來之前看到水杯的前一刻,她不知道水杯是否滿了,因?yàn)樗恢雷约弘x開了多長時(shí)間。而水杯里的水從完全沒水到剛好積滿這個(gè)時(shí)間差,永遠(yuǎn)都是固定的。如果一直沒人將水杯倒掉,超過這個(gè)時(shí)間差,則水杯將一直處于溢出的狀態(tài)。當(dāng)她回來查看水杯到底滿了還是沒滿的這個(gè)動作,相當(dāng)于程序執(zhí)行了該函數(shù)。她只有兩個(gè)可能的結(jié)果,要么水杯滿了,要么還沒滿。
函數(shù)4:void timer_reset(struct timer *t)//復(fù)位定時(shí)器timer。該函數(shù)相當(dāng)于水杯匯聚的滴水,不論水杯里的水有多滿,一律將水全倒掉,在將水杯放回水龍頭下面重新積水。
函數(shù)5:void timer_restart(struct timer *t)//重啟定時(shí)器timer。這個(gè)函數(shù)就好比如有個(gè)電子鬧鐘,有人將里面的電池拆下來后,又馬上將電池重新安裝回去。這樣,這個(gè)電子鬧鐘就“重啟”了。
由于優(yōu)化后的中斷服務(wù)程序中,添加了if(timer_expired(&key_timer))等相關(guān)語句,自按鍵被按下后,在接下來key_timer限定的50ms之內(nèi),除了第一次中斷函數(shù)能進(jìn)入該if語句里面執(zhí)行LED翻轉(zhuǎn)的語句,其它由抖動導(dǎo)致的中斷服務(wù)程序均不能進(jìn)入該if語句執(zhí)行LED翻轉(zhuǎn)語句。只有當(dāng)key_timer限定的50ms滿了之后,程序才可以接受檢驗(yàn)按鍵下一次被按下的動作。新程序重新下載到MCU后,經(jīng)測試部測試,已經(jīng)徹底解決了LED多次翻轉(zhuǎn)的問題。
3 結(jié)語
目前該技術(shù)方案應(yīng)用在lemuse專業(yè)音箱等產(chǎn)品中已經(jīng)超過2年時(shí)間。事實(shí)充分論證了本文介紹的整個(gè)按鍵檢測方案具有很強(qiáng)的實(shí)用性、成本低廉、準(zhǔn)確性高、可靠性高等優(yōu)點(diǎn)。