2.1.2 器件和原理
本例主要介紹AVR單片機(jī)外圍電路中按鍵去抖電路的設(shè)計(jì),分別介紹相應(yīng)的軟件和硬件解決方案。然后利用C語(yǔ)言編寫(xiě)通過(guò)按鍵控制發(fā)光二極管亮滅狀態(tài)的程序。
1、按鍵的去抖動(dòng)電路
(1)按鍵的響應(yīng)過(guò)程
我們?nèi)粘Kf(shuō)的按鍵,實(shí)際上是一個(gè)機(jī)械開(kāi)關(guān),本實(shí)例所用的按鍵外觀如圖2.1.1所示。理想的按鍵的閉合和斷開(kāi)時(shí),接觸點(diǎn)的電壓應(yīng)該立即變高或者變低,但
是由于機(jī)械觸點(diǎn)的彈性以及按鍵按動(dòng)時(shí)電壓突變等原因,在觸點(diǎn)閉合或斷開(kāi)的瞬間會(huì)出現(xiàn)電壓抖動(dòng)現(xiàn)象,如圖2.1.2所示。在發(fā)生抖動(dòng)的時(shí)間一般在
5-10ms。
一次按鍵處理過(guò)程如下:當(dāng)按鍵按下之后,相應(yīng)的按鍵接觸點(diǎn)的電壓以高低電平的方式輸入到單片機(jī)的I/O口。按鍵的閉合與斷開(kāi)是有一定時(shí)間的,一般為
0.1-1S。而AVR單片機(jī)的機(jī)器周期一般為1us甚至更短,在0.1-1S的時(shí)間段內(nèi),程序會(huì)檢測(cè)很多次按鍵的輸入電平,這樣單片機(jī)可能會(huì)認(rèn)為按鍵被
按下了多次,從而出現(xiàn)誤判。
圖2.1.1 按鍵開(kāi)關(guān) 圖2.1.2 按鍵閉合斷開(kāi)時(shí)的電壓波動(dòng)示意圖
(2)按鍵去抖動(dòng)的方法和原理
為了去除按鍵的抖動(dòng),保證單片機(jī)對(duì)按鍵的一次輸入只響應(yīng)一次,可以采用硬件和軟件兩種方法:硬件電路去抖動(dòng)是在外圍電路中加入去抖動(dòng)電路(如R-S觸發(fā)
器);軟件去抖動(dòng)是在程序中加入延時(shí)程序以跳過(guò)抖動(dòng)時(shí)間,等待信號(hào)穩(wěn)定后再次判斷按鍵的輸入電平,如果信號(hào)電平保持不變,則可以確認(rèn)一次按鍵按下。
●硬件去抖動(dòng)電路的原理
用R-S觸發(fā)器形成去抖電路是單片機(jī)外圍電路設(shè)計(jì)中常用的方法,這種方法可以減少單片機(jī)軟件對(duì)按鍵動(dòng)作的延時(shí)和計(jì)算。
先來(lái)了解一下R-S觸發(fā)器的基本工作原理和工作特點(diǎn)。R-S觸發(fā)器的基本構(gòu)成如圖2.1.3所示,這個(gè)電路有兩個(gè)與非門(mén)交叉耦合而成,/S、/R是信號(hào)輸入端,低電平有效。Q和/Q既表示觸發(fā)器狀態(tài),又是觸發(fā)器的輸出端。
圖2.1.3 R-S觸發(fā)器的基本原理
在啟動(dòng)過(guò)程中,/S端一旦下降到開(kāi)門(mén)電平,Q端電平就會(huì)上升,反饋到門(mén)B的輸入端,此時(shí)門(mén)B在/R的低電平作用下處于導(dǎo)通狀態(tài),/Q輸出高電平反饋到A的
輸入端,如果這時(shí)/S端電壓有一個(gè)高的跳動(dòng),則A門(mén)截止,Q段輸出低電平,這個(gè)低電平反饋到A的輸入端,使A門(mén)導(dǎo)通,Q端電平為高,這樣就保證了Q端電平
的穩(wěn)定,從而消除按鍵的抖動(dòng)。
典型的硬件去抖動(dòng)電路如圖2.1.4,74LS02按鍵輸出端口通過(guò)/Q端接入單片機(jī)的I/O口,74LS02構(gòu)成一個(gè)R-S觸發(fā)器電路實(shí)現(xiàn)按鍵的消抖。
●軟件消抖的原理和實(shí)現(xiàn)
軟件消抖的基本原理是在軟件中對(duì)按鍵進(jìn)行兩次檢測(cè)確認(rèn),記載第一次檢測(cè)到按鍵按下后,間隔10ms左右再次檢測(cè)按鍵是否按下,只有在兩次都檢測(cè)到按鍵按下時(shí)才最終確認(rèn)有鍵按下,這樣就避開(kāi)了按鍵的抖動(dòng)時(shí)間,從而消除了抖動(dòng)的影響。
圖2.1.4 74LS02實(shí)現(xiàn)的硬件消抖電路
在按鍵接口軟件的設(shè)計(jì)中,除了要考慮按鍵消抖外,一般還要判別按鍵的釋放,只有檢測(cè)到按鍵釋放后,才能確定為一次完整的按鍵動(dòng)作。
通用的案件檢測(cè)程序如下:
[code="c"]
Keyscan()
{
if(!key) //判斷按鍵是否按下,key=0表示按鍵按下
{
delayms(20); //延時(shí)20ms。避開(kāi)按鍵抖動(dòng)時(shí)間
if(!key) //再次判斷按鍵是否按下,
{
… //按鍵按下的處理程序
}
}
While(!key); //判斷按鍵是否放開(kāi),key=1表示按鍵釋放,退出按鍵處理函數(shù)
}
[/code]
2.1.3 電路
本例中的電路如圖2.1.5和2.1.6所示。
1、電路原理
圖2.1.5是按鍵檢測(cè)電路,兩個(gè)按鍵分別連接到單片機(jī)的PD6、PD7管腳,AVR單片機(jī)在程序里把PD6、PD7設(shè)置為帶上拉的端口,這樣按鍵沒(méi)有按
下時(shí),PD6、PD7處于高電平狀態(tài),當(dāng)按鍵按下時(shí)PD6、PD7被連接到地,電平狀態(tài)變?yōu)榈碗娖剑绦蛑袡z測(cè)到PD6、PD7的電平為低電平時(shí),就可以
認(rèn)為按鍵被按下了。
圖2.1.6為L(zhǎng)ED顯示電路,當(dāng)按鍵K3被按下時(shí),D1、D3、D5、D7點(diǎn)亮,D2、D4、D6、D8熄滅。當(dāng)按鍵K4被按下時(shí),D1、D3、D5、D7熄滅,D2、D4、D6、D8點(diǎn)亮。
2、元器件選擇
在這里列出和本例相關(guān)的、關(guān)鍵部分的器件名稱(chēng)及其在電路中的作用。
● ATmega16:?jiǎn)纹瑱C(jī),檢測(cè)按鍵按下情況并控制發(fā)光二極管的亮滅。
● D1-D8:發(fā)光二極管,指示按鍵狀態(tài)。
● RP1:阻值為330Ω的排阻,限流電阻。
● K3、K4:按鍵,當(dāng)按鍵按下時(shí),與按鍵連接的單片機(jī)端口的電平發(fā)生變化。
3、管腳連接
在這里列出和本例相關(guān)的、關(guān)鍵部分的單片機(jī)端口與外圍電路的連接。
● PB0-PB7:連接8個(gè)發(fā)光二極管LED1-LED8,控制發(fā)光二極管的亮滅。
● PD6、PD7:連接按鍵K3、K4,檢測(cè)兩個(gè)按鍵的狀態(tài)。
2.1.4 程序設(shè)計(jì)
1、程序功能
● 按鍵軟件消抖
本例中采用軟件消抖的方法,在程序中加入軟件延時(shí),去除按鍵的抖動(dòng)。
● 按鍵檢測(cè)
通過(guò)將單片機(jī)的PD6、PD7口設(shè)置為輸入狀態(tài),同時(shí)使能這兩個(gè)口的內(nèi)部上拉電阻(因?yàn)檫@兩個(gè)口在按鍵沒(méi)有按下時(shí)處于懸空狀態(tài),易受外界干擾,所以必須將其內(nèi)部上來(lái)電阻使能,使其平時(shí)處于高電平狀態(tài)),檢測(cè)按鍵是否按下。
通過(guò)將單片機(jī)的PB0-PB7口設(shè)置為輸出狀態(tài),根據(jù)K3、K4兩個(gè)按鍵的按下情況,控制不同的發(fā)光二極管點(diǎn)亮或熄滅。
● AVR單片機(jī)端口輸入狀態(tài)的讀取
AVR單片機(jī)端口配備有3個(gè)寄存器,分別是方向控制寄存器DDRx,數(shù)據(jù)寄存器PORTx,和輸入引腳寄存器PINx(x=A\B\C\D)。當(dāng)I/O工作在輸入方式,要讀取外部引腳上的電平時(shí),應(yīng)讀取PINxn的值,而不是PORTxn的值。
2、主要變量和函數(shù)說(shuō)明
無(wú)
3、使用WINAVR開(kāi)發(fā)環(huán)境,makefile文件同前面的例子,直接復(fù)制到本實(shí)例程序的文件夾中即可。
4、程序代碼
[code="c"]
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
PORTB = 0X00; //輸出低,LED全部熄滅
DDRB = 0Xff; //PB端口置為輸出
PORTD = 0Xc0; //一定要使能上拉電阻,否則會(huì)有干擾
DDRD = 0X3F; //K3、K4按鍵(PD6、PD7)設(shè)置為輸入端口
while(1)
{
if(!(PIND & (1 << PD6))) //判斷按鍵是否按下
{
_delay_ms(20); //判斷按鍵按下,延時(shí)一會(huì)再判斷是否按下, 以消除干擾
if(!(PIND & (1 << PD6))) // 按鍵真正按下后,進(jìn)行相應(yīng)處理
{
PORTB = 0X55; // 按鍵按下,燈亮
while(!(PIND & (1 << PD6)));//等待按鍵釋放
//PORTB = 0X55; // 把這句話(huà)從上面移到這里,按鍵釋放后,燈才點(diǎn)亮
}
}
if(!(PIND & (1 << PD7))) //判斷按鍵是否按下
{
_delay_ms(20); //判斷按鍵按下,延時(shí)一會(huì)再判斷是否按下, 以消除干擾
if(!(PIND & (1 << PD7))) // 按鍵真正按下后,進(jìn)行相應(yīng)處理
{
PORTB = 0Xaa; //
while(!(PIND & (1 << PD7))); //
//PORTB = 0Xaa; //
}
}
}
}