8.4 按鍵 8.4.1 獨(dú)立按鍵 常用的按鍵電路有兩種形式,獨(dú)立式按鍵和矩陣式按鍵,獨(dú)立式按鍵比較簡(jiǎn)單,它們各自與獨(dú)立的輸入線相連接,如圖 8-6 所示。 圖 8-6 獨(dú)立式按鍵原理圖 4 條輸入線接到單片機(jī)的 IO 口上,當(dāng)按鍵 K1 按下時(shí),+5V 通過(guò)電阻 R1 然后再通過(guò)按鍵K1 最終進(jìn)入GND 形成一條通路,那么這條線路的全部電壓都加到了R1 這個(gè)電阻上,KeyIn1 這個(gè)引腳就是個(gè)低電平。當(dāng)松開按鍵后,線路斷開,就不會(huì)有電流通過(guò),那么 KeyIn1 和+5V 就應(yīng)該是等電位,是一個(gè)高電平。我們就可以通過(guò) KeyIn1 這個(gè) IO 口的高低電平來(lái)判斷是否有按鍵按下。 準(zhǔn)雙向IO 口,如果要正常讀取外部信號(hào)的狀態(tài),必須首先得保證自己內(nèi)部輸出的是 1,如果內(nèi)部輸出 0,則無(wú)論外部信號(hào)是 1 還是 0,這個(gè)引腳讀進(jìn)來(lái)的都是 0。 8.4.2 矩陣按鍵 在某一個(gè)系統(tǒng)設(shè)計(jì)中,如果需要使用很多的按鍵時(shí),做成獨(dú)立按鍵會(huì)大量占用IO 口,因此我們引入了矩陣按鍵的設(shè)計(jì)。如圖 8-8 所示,是我們的 KST-51 開發(fā)板上的矩陣按鍵電路原理圖,使用 8 個(gè) IO 口來(lái)實(shí)現(xiàn)了 16 個(gè)按鍵。 圖 8-8 矩陣按鍵原理圖 如果獨(dú)立按鍵理解了,矩陣按鍵也不難理解,那么我們一起來(lái)分析一下。圖8-8 中,一共有 4 組按鍵,我們只看其中一組,如圖 8-9 所示。大家認(rèn)真看一下,如果 KeyOut1 輸出一個(gè)低電平,KeyOut1 就相當(dāng)于是 GND,是否相當(dāng)于 4 個(gè)獨(dú)立按鍵呢。當(dāng)然這時(shí)候 KeyOut2、KeyOut3、KeyOut4 都必須輸出高電平,它們都輸出高電平才能保證與它們相連的三路按鍵 不會(huì)對(duì)這一路產(chǎn)生干擾,大家可以對(duì)照兩張?jiān)韴D分析一下。 圖 8-9 矩陣按鍵變獨(dú)立按鍵示意圖 8.4.4 按鍵消抖 通常按鍵所用的開關(guān)都是機(jī)械彈性開關(guān),當(dāng)機(jī)械觸點(diǎn)斷開、閉合時(shí),由于機(jī)械觸點(diǎn)的彈性作用,一個(gè)按鍵開關(guān)在閉合時(shí)不會(huì)馬上就穩(wěn)定的接通,在斷開時(shí)也不會(huì)一下子徹底斷開,而是在閉合和斷開的瞬間伴隨了一連串的抖動(dòng),如圖 8-10 所示。 圖 8-10 按鍵抖動(dòng)狀態(tài)圖按鍵穩(wěn)定閉合時(shí)間長(zhǎng)短是由操作人員決定的,通常都會(huì)在 100ms 以上,刻意快速按的話能達(dá)到 40-50ms 左右,很難再低了。抖動(dòng)時(shí)間是由按鍵的機(jī)械特性決定的,一般都會(huì)在 10ms 以內(nèi),為了確保程序?qū)Π存I的一次閉合或者一次斷開只響應(yīng)一次,必須進(jìn)行按鍵的消抖處理先等待一個(gè)10ms 左右的延時(shí)時(shí)間,讓抖動(dòng)消失后再進(jìn)行一次按鍵狀態(tài)檢測(cè),如果與剛才檢測(cè)到的狀態(tài)相同,就可以確認(rèn)按鍵已經(jīng)穩(wěn)定的動(dòng)作了。將上一個(gè)的程 序稍加改動(dòng),得到新的帶消抖功能的程序如下。 那么消抖操作所需要的延時(shí)該怎么處理呢?其實(shí)除了這種簡(jiǎn)單的延時(shí),我們還有更優(yōu)異的方法來(lái)處理按鍵抖動(dòng)問(wèn)題。舉個(gè)例子:我們啟用一個(gè)定時(shí)中斷,每 2ms 進(jìn)一次中斷,掃描一次按鍵狀態(tài)并且存儲(chǔ)起來(lái),連續(xù)掃描8 次后,看看這連續(xù)8 次的按鍵狀態(tài)是否是一致的。8 次按鍵的時(shí)間大概是 16ms,這 16ms 內(nèi)如果按鍵狀態(tài)一直保持一致,那就可以確定現(xiàn)在按鍵處于穩(wěn)定的階段,而非處于抖動(dòng)的階段,如圖 8-12。 圖 8-12 按鍵連續(xù)掃描判斷 假如左邊時(shí)間是起始 0 時(shí)刻,每經(jīng)過(guò) 2ms 左移一次,每移動(dòng)一次,判斷當(dāng)前連續(xù)的 8 次
按鍵狀態(tài)是不是全 1 或者全 0,如果是全 1 則判定為彈起,如果是全 0 則判定為按下,如果0 和 1 交錯(cuò),就認(rèn)為是抖動(dòng),不做任何判定。想一下,這樣是不是比簡(jiǎn)單的延時(shí)更加可靠?利用這種方法,就可以避免通過(guò)延時(shí)消抖占用單片機(jī)執(zhí)行時(shí)間,而是轉(zhuǎn)化成了一種按鍵狀態(tài)判定而非按鍵過(guò)程判定,我們只對(duì)當(dāng)前按鍵的連續(xù) 16ms 的 8 次狀態(tài)進(jìn)行判斷,而不再關(guān)心它在這16ms 內(nèi)都做了什么事情. 8.4.5 矩陣按鍵的掃描 我們講獨(dú)立按鍵掃描的時(shí)候,大家已經(jīng)簡(jiǎn)單認(rèn)識(shí)了矩陣按鍵是什么樣子了。矩陣按鍵相當(dāng)于 4 組每組各 4 個(gè)獨(dú)立按鍵,一共是 16 個(gè)按鍵。那我們?nèi)绾螀^(qū)分這些按鍵呢?想一下我們生活所在的地球,要想確定我們所在的位置,就要借助經(jīng)緯線,而矩陣按鍵就是通過(guò)行線 和列線來(lái)確定哪個(gè)按鍵被按下的。那么在程序中我們又如何進(jìn)行這項(xiàng)操作呢? 前邊講過(guò),按鍵按下通常都會(huì)保持 100ms 以上,如果在按鍵掃描中斷中,我們每次讓矩陣按鍵的一個(gè) KeyOut 輸出低電平,其它三個(gè)輸出高電平,判斷當(dāng)前所有 KeyIn 的狀態(tài),下次中斷時(shí)再讓下一個(gè) KeyOut 輸出低電平,其它三個(gè)輸出高電平,再次判斷所有 KeyIn,通過(guò)快速的中斷不停的循環(huán)進(jìn)行判斷,就可以最終確定哪個(gè)按鍵按下了,這個(gè)原理是不是跟數(shù)碼 管動(dòng)態(tài)掃描有點(diǎn)類似?數(shù)碼管我們?cè)趧?dòng)態(tài)賦值,而按鍵這里我們?cè)趧?dòng)態(tài)讀取狀態(tài)。至于掃描 間隔時(shí)間和消抖時(shí)間,因?yàn)楝F(xiàn)在有 4 個(gè) KeyOut 輸出,要中斷 4 次才能完成一次全部按鍵的掃描,顯然再采用 2ms 中斷判斷 8 次掃描值的方式時(shí)間就太長(zhǎng)了2*4*8=64ms),那么我們就改用 1ms 中斷判斷 4 次采樣值,這樣消抖時(shí)間還是 16ms(1*4*4)。下 /**************按鍵掃描*********************** * 函數(shù)名稱:Keyscan * 函數(shù)參數(shù):void * 函數(shù)功能:完成矩陣按鍵或單獨(dú)按鍵的掃描,矩陣按鍵使用二維數(shù)組;單獨(dú)按鍵使用一維數(shù)組 * 作者:ZYD * 日期:2018 */ void Keyscan(void) //本函數(shù)要循環(huán)4次起作用 { uint8i; staticuint8 keyout = 0; staticuint8 keybuf[4][4] = { {0xff,0xff,0xff,0xff,}, //可用緩沖區(qū) {0xff,0xff,0xff,0xff,}, {0xff,0xff,0xff,0xff,}, {0xff,0xff,0xff,0xff,}, }; /***********按鍵的out伏低電平,掃描低電平***********/ switch(keyout) { case0:P2=0x7f;break; //0111 1111 case1:P2=0xbf;break; //1011 1111 case2:P2=0xdf;break; //1101 1111 case3:P2=0xef;break; //1110 1111 default:break; } //P21= 1; //單獨(dú)賦值,保證數(shù)碼管最后一位正常 /************按鍵按列掃描移位,一個(gè)語(yǔ)句完成一列*******/ keybuf[keyout][0]<<=1;keybuf[keyout][0]|=P20; //in1 // keybuf[keyout][1]<<=1;keybuf[keyout][1]|=P21; //in2 // keybuf[keyout][2]<<=1;keybuf[keyout][2]|=P22; //in3 // keybuf[keyout][3]<<=1;keybuf[keyout][3]|=P23; //in4 P21 = 0; //單獨(dú)賦值,保證數(shù)碼管最后一位正常 for(i=0;i<4;i++) //4次中斷的取樣 { if((keybuf[keyout]&0x0f)== 0x0f) //只要掃描不連續(xù)的四位就可以,形成16ms消抖 { //4*4MS內(nèi)都是0000,就成立了 keysta[keyout]= 1; } elseif((keybuf[keyout]&0x0f) == 0x00) keysta[keyout]= 0; else {} } keyout++; keyout&=0x03;//與字來(lái)清零 }
|