對(duì)于單片機(jī)來(lái)講,不同的中斷源,產(chǎn)生什么類(lèi)型的中斷信號(hào)能夠觸發(fā)申請(qǐng)中斷,取決于芯片內(nèi)部的硬件結(jié)構(gòu),而且通常也可以通過(guò)用戶(hù)的軟件來(lái)設(shè)定。
單片機(jī)的硬件系統(tǒng)會(huì)自動(dòng)對(duì)這些中斷信號(hào)進(jìn)行檢測(cè)。一旦檢測(cè)到規(guī)定的信號(hào)出現(xiàn),將會(huì)把相應(yīng)的中斷標(biāo)志位置“1”(在I/O空間的控制或狀態(tài)寄存器中),通知CPU進(jìn)行處理。
● 中斷向量
中斷源發(fā)出的請(qǐng)求信號(hào)被CPU檢測(cè)到之后,如果單片機(jī)的中斷控制系統(tǒng)允許響應(yīng)中斷,CPU會(huì)自動(dòng)轉(zhuǎn)移,執(zhí)行一個(gè)固定的程序空間地址中的指令。這個(gè)固定的地址稱(chēng)作中斷入口地址,也叫做中斷向量。中斷入口地址往往是由單片機(jī)內(nèi)部硬件決定的。
一個(gè)單片機(jī)有若干個(gè)中斷源,每個(gè)中斷源都有著自己的中斷向量。這些中斷向量一般在程序存儲(chǔ)空間中占用一個(gè)連續(xù)的地址空間段,稱(chēng)為中斷向量區(qū)。由于一個(gè)中斷
向量通常僅占幾個(gè)字節(jié)或一條指令的長(zhǎng)度,所以在中斷向量區(qū)一般不放置中斷服務(wù)程序的。中斷服務(wù)程序一般放置在程序存儲(chǔ)器的其它地方,而在中斷向量處放置一
條跳轉(zhuǎn)到中斷服務(wù)程序的指令。這樣,CPU響應(yīng)中斷后,首先自動(dòng)轉(zhuǎn)向執(zhí)行中斷向量中的轉(zhuǎn)移指令,再跳轉(zhuǎn)執(zhí)行中斷服務(wù)程序。
● 中斷優(yōu)先級(jí)
單片機(jī)系統(tǒng)一般有多個(gè)中斷源,當(dāng)某一時(shí)刻同時(shí)有多個(gè)中斷產(chǎn)生時(shí),單片機(jī)該如何處理呢?這就有了中斷優(yōu)先級(jí)的概念。
通常,單片機(jī)可以接收若干個(gè)中斷源發(fā)出的中斷請(qǐng)求。但在同一時(shí)刻,MCU只能響應(yīng)這些中斷請(qǐng)求中的其中一個(gè)。為了避免MCU同時(shí)響應(yīng)多個(gè)中斷請(qǐng)求帶來(lái)的混
亂,在單片機(jī)中為每一個(gè)中斷源賦予一個(gè)特定的中斷優(yōu)先級(jí)。一旦有多個(gè)中斷請(qǐng)求信號(hào),MCU先響應(yīng)中斷優(yōu)先級(jí)高的中斷請(qǐng)求,然后再逐次響應(yīng)優(yōu)先級(jí)次一級(jí)的中
斷。中斷優(yōu)先級(jí)也反映了各個(gè)中斷源的重要程度,同時(shí)也是分析中斷嵌套的基礎(chǔ)。
對(duì)于中斷優(yōu)先級(jí)的確定,通常是由單片機(jī)的硬件結(jié)構(gòu)規(guī)定的。一般的確定規(guī)則方式為兩種:
實(shí)際上,MCU在兩種情況下需要對(duì)中斷的優(yōu)先級(jí)進(jìn)行判斷:
第一種情況為同時(shí)有兩(多)個(gè)中斷源申請(qǐng)中斷。在這種情況下,MCU首先響應(yīng)中斷優(yōu)先級(jí)最高的那個(gè)中斷,而將其它的中斷掛起。待優(yōu)先級(jí)最高的中斷服務(wù)程序執(zhí)行完成返回后,再順序響應(yīng)優(yōu)先級(jí)較低的中斷。
第二種情況是當(dāng)MCU正處于響應(yīng)一個(gè)中斷的過(guò)程中。如已經(jīng)響應(yīng)了某個(gè)中斷,正在執(zhí)行為其服務(wù)的中斷程序時(shí),此時(shí)又產(chǎn)生一個(gè)其它的中斷申請(qǐng),這種情況也稱(chēng)作中斷嵌套。
● 中斷嵌套
對(duì)于中斷嵌套的處理,不同的單片機(jī)處理的方式是不同的,應(yīng)根據(jù)所使用單片機(jī)的特點(diǎn)正確實(shí)現(xiàn)中斷嵌套的處理。
按照通常的規(guī)則,當(dāng)MCU正在響應(yīng)一個(gè)中斷B的過(guò)程中,又產(chǎn)生一個(gè)其它的中斷A申請(qǐng)時(shí),如果這個(gè)新產(chǎn)生中斷A的優(yōu)先級(jí)比正在響應(yīng)的中斷B優(yōu)先級(jí)高的話(huà),就
應(yīng)該暫停當(dāng)前的中斷B的處理,轉(zhuǎn)入響應(yīng)高優(yōu)先級(jí)的中斷A,待高優(yōu)先級(jí)中斷A處理完成后,再返回原來(lái)的中斷B的處理過(guò)程。如果新產(chǎn)生中斷A的優(yōu)先級(jí)比正在處
理中斷B的優(yōu)先級(jí)低(或相同),則應(yīng)在處理完當(dāng)前的中斷B后,再響應(yīng)那個(gè)后產(chǎn)生的中斷A申請(qǐng)(如果中斷A條件還成立的話(huà))。
一些單片機(jī)(如8051結(jié)構(gòu))的硬件能夠自動(dòng)實(shí)現(xiàn)中斷嵌套的處理,既單片機(jī)內(nèi)部的硬件電路能夠識(shí)別中斷的優(yōu)先級(jí),并根據(jù)優(yōu)先級(jí)的高低,自動(dòng)完成對(duì)高優(yōu)先級(jí)中斷的優(yōu)先響應(yīng),實(shí)現(xiàn)中斷的嵌套處理。
而另一類(lèi)的單片機(jī),如我們正在學(xué)習(xí)的AVR單片機(jī),其硬件系統(tǒng)不支持自動(dòng)實(shí)現(xiàn)中斷嵌套的處理。如果在系統(tǒng)設(shè)計(jì)中,必須使用中斷嵌套處理,則需要由用戶(hù)編寫(xiě)相應(yīng)的程序,通過(guò)軟件設(shè)置來(lái)實(shí)現(xiàn)中斷嵌套的功能。
● 中斷控制(屏蔽)
單片機(jī)擁有眾多中斷源,但在某一具體設(shè)計(jì)中通常并不需要使用所有的中斷源,或者在系統(tǒng)軟件運(yùn)行的某些關(guān)鍵階段不允許中斷打斷現(xiàn)行程序的運(yùn)行,這就需要一套
軟件可控制的中斷屏蔽/允許系統(tǒng)。在單片機(jī)的I/O寄存器中,通常存在一些特殊的標(biāo)志位用于控制開(kāi)放或關(guān)閉(屏蔽)MCU對(duì)中斷響應(yīng)處理,這些標(biāo)志稱(chēng)為中
斷屏蔽標(biāo)志位或中斷允許控制位。用戶(hù)程序可以改變這些標(biāo)志位的設(shè)置,在需要的時(shí)候允許MCU響應(yīng)中斷,而在不需要的時(shí)候則將中斷請(qǐng)求信號(hào)屏蔽(注意:不是
取消),此時(shí)盡管產(chǎn)生了中斷請(qǐng)求信號(hào),MCU也不會(huì)響應(yīng)中斷請(qǐng)求。
從對(duì)中斷源的控制角度講,中斷源還可分成2類(lèi):
● 中斷響應(yīng)條件
單片機(jī)在工作時(shí),在每個(gè)機(jī)器周期都會(huì)查詢(xún)一下各個(gè)中斷源的中斷標(biāo)記,從而判斷是否有中斷申請(qǐng),如果中斷標(biāo)志為1,說(shuō)明有中斷請(qǐng)求發(fā)生。
綜合前面的介紹,我們可以知道,在單片機(jī)中,對(duì)應(yīng)每一個(gè)中斷源都有一個(gè)相應(yīng)的中斷標(biāo)志位,該中斷標(biāo)志位將占據(jù)中斷控制寄存器中的一位。當(dāng)單片機(jī)檢測(cè)到某一中斷源產(chǎn)生符合條件的中斷信號(hào)時(shí),其硬件會(huì)自動(dòng)將該中斷源對(duì)應(yīng)的中斷標(biāo)志位置“1”,這就意味著有中斷信號(hào)產(chǎn)生了,向MCU申請(qǐng)中斷。
但中斷標(biāo)志位的置“1”,并不代表MCU一定響應(yīng)該中斷。為了合理控制中斷響應(yīng),在單片機(jī)內(nèi)部還有相關(guān)的用于中斷控制的中斷允許標(biāo)志位。最重要的一個(gè)中斷允許標(biāo)志位是全局中斷允許標(biāo)志位。當(dāng)該標(biāo)志位為“0”,表示禁止MCU響應(yīng)所有的可屏蔽中斷的響應(yīng)。此時(shí)不管有否中斷產(chǎn)生,MCU不會(huì)響應(yīng)任何的中斷請(qǐng)求。只有全局中斷允許標(biāo)志位為“1”,才允許單片機(jī)響應(yīng)中斷。
MCU響應(yīng)中斷請(qǐng)求的第二個(gè)條件是每個(gè)中斷源所具有的各自獨(dú)立的中斷允許標(biāo)志位。當(dāng)某個(gè)中斷允許標(biāo)志位為“0”時(shí),表示MCU不響應(yīng)該中斷的中斷申請(qǐng)。
從上面的中斷響應(yīng)條件看出,只有當(dāng)全局中斷允許標(biāo)志位為“1”(由用戶(hù)軟件設(shè)置),中斷A允許標(biāo)志位為“1”(由用戶(hù)軟件設(shè)置),中斷A標(biāo)志位為“1”
(符合中斷條件時(shí)由硬件自動(dòng)設(shè)置或由用戶(hù)軟件設(shè)置)時(shí),MCU才會(huì)響應(yīng)中斷A的請(qǐng)求信號(hào)(如果有多個(gè)中斷請(qǐng)求信號(hào)同時(shí)存在的情況下,還要根據(jù)中斷A的優(yōu)先
級(jí)來(lái)確定)。
用戶(hù)程序?qū)善帘沃袛嗟目刂?,一般是通過(guò)設(shè)置相應(yīng)的中斷控制寄存器來(lái)實(shí)現(xiàn)的。除了設(shè)置中斷的響應(yīng)條件,用戶(hù)程序還需要通過(guò)中斷控制器來(lái)設(shè)置中斷的其他特性,如:中斷觸發(fā)信號(hào)的類(lèi)型、中斷的優(yōu)先級(jí)、中斷信號(hào)產(chǎn)生的條件等等。
●中斷響應(yīng)過(guò)程(中斷服務(wù)程序)
當(dāng)所有的中斷響應(yīng)條件都滿(mǎn)足了之后,就要進(jìn)入中斷響應(yīng)過(guò)程進(jìn)行相應(yīng)處理了。單片機(jī)響應(yīng)中斷后,首先要把當(dāng)前指令的下一條指令的地址送入堆棧(保護(hù)斷點(diǎn)),
然后根據(jù)中斷標(biāo)記,將相應(yīng)的中斷入口地址送入程序指針,程序轉(zhuǎn)到中斷入口處繼續(xù)執(zhí)行(中斷服務(wù)程序),中斷程序執(zhí)行完后,單片機(jī)再把堆棧中保存的地址取
出,程序從剛才的中斷處繼續(xù)向下執(zhí)行。
需要注意的是,單片機(jī)硬件所做的保護(hù)工作只是保護(hù)了程序的一個(gè)指令地址,如果中斷響應(yīng)過(guò)程中修改了一些寄存器和變量的值,就需要在中斷響應(yīng)程序里面自己加以保護(hù)。
2、AVR單片機(jī)的外部中斷
AVR單片機(jī)有很多中斷,在后面的實(shí)例中我們會(huì)逐一介紹。本例中只介紹AVR單片機(jī)的外部中斷,
ATmega16有INT0、INT1和INT23個(gè)外部中斷源,分別由芯片外部引腳PD2、PD3、PB2上的電平的變化或狀態(tài)作為中斷觸發(fā)信號(hào)。
●外部中斷觸發(fā)方式和特點(diǎn)
INT0、INT1、INT2的中斷觸發(fā)方式取決于用戶(hù)程序?qū)CU控制寄存器MCUCR以及MCU控制與狀態(tài)寄存器MCUCSR的設(shè)定。其中,INT0和INT1支持4種中斷觸發(fā)方式:上升沿觸發(fā)、下降沿觸發(fā)、任意電平變化觸發(fā)、低電平觸發(fā)。
INT2支持上升沿觸發(fā)和下降沿觸發(fā)。
任意電平變化觸發(fā)表示只要引腳上有邏輯電平的變化就會(huì)產(chǎn)生中斷申請(qǐng)(不管是上升沿還是下降沿都引起中斷觸發(fā))。在這4種觸發(fā)方式中,還有以下的一些不同的特點(diǎn):
●低電平觸發(fā)是不帶中斷標(biāo)志類(lèi)型的,即只要中斷輸入引腳PD2或PD3保持低電平,那
么將一直會(huì)產(chǎn)生中斷申請(qǐng)。
●MCU對(duì)INT0和INT1的引腳上的上升沿或下降沿變化的識(shí)別(觸發(fā)),需要I/O時(shí)鐘信號(hào)的存在(由I/O時(shí)鐘同步檢測(cè)),屬于同步邊沿觸發(fā)的中斷類(lèi)型。
●MCU對(duì)INT2的引腳上的上升沿或下降沿變化的識(shí)別(觸發(fā)),以及低電平的識(shí)別(觸發(fā))是通過(guò)異步方式檢測(cè)的,不需要I/O時(shí)鐘信號(hào)的存在。因此,這
類(lèi)觸發(fā)類(lèi)型的中斷經(jīng)常作為外部喚醒源,用于將處在Idle休眠模式,以及處在各種其它休眠模式的MCU喚醒。這是由于除了在空閑(Idel)模式
時(shí),I/O時(shí)鐘信號(hào)還保持繼續(xù)工作,在其它各種休眠模式下,I/O時(shí)鐘信號(hào)均是處在暫停狀態(tài)的。
●如果使用低電平觸發(fā)方式的中斷作為喚醒源,將MCU從掉電模式(Power-down)中喚醒時(shí),電平拉低后仍需要維持一段時(shí)間才能將MCU喚醒,這是
為了提高了MCU的抗噪性能。拉低的觸發(fā)電平將由看門(mén)狗的時(shí)鐘信號(hào)采樣兩次(在通常的5V電源和25℃時(shí),看門(mén)狗的時(shí)鐘周期為1μs)。如果電平拉低保持
2次采樣周期的時(shí)間,或者一直保持到MCU啟動(dòng)延時(shí)(start-up
time)過(guò)程之后,MCU將被喚醒并進(jìn)入中斷服務(wù)。如果該電平的保持時(shí)間能夠滿(mǎn)足看門(mén)狗時(shí)鐘的兩次采樣,但在啟動(dòng)延時(shí)(start-up
time)過(guò)程完成之前就消失了,那么MCU仍將被喚醒,但不會(huì)觸發(fā)中斷進(jìn)入中斷服務(wù)程序。所以,為了保證既能將MCU喚醒,又能觸發(fā)中斷,中斷觸發(fā)電平
必須維持足夠長(zhǎng)的時(shí)間。
●如果設(shè)置了允許響應(yīng)外部中斷的請(qǐng)求,那么即便是引腳PD2、PD3、PB2設(shè)置為輸出方式工作,引腳上的電平變化也會(huì)產(chǎn)生外部中斷觸發(fā)請(qǐng)求。這一特性為用戶(hù)提供了使用軟件產(chǎn)生中斷的途徑。
(3)與外部中斷相關(guān)的寄存器和標(biāo)志位
在ATmega16中,除了寄存器SREG中的全局中斷允許標(biāo)志位I外,與外部中斷有關(guān)的寄存器有4個(gè),共有11個(gè)標(biāo)志位。分別是:MCU控制寄存器—MCUCR、MCU控制和狀態(tài)寄存器—MCUCSR、通用中斷控制寄存器—GICR、通用中斷標(biāo)志寄存器—GIFR。其作用分別是3個(gè)外部中斷各自的中斷標(biāo)志位、中斷允許控制位和用于定義外部中斷的觸發(fā)類(lèi)型。
具體寄存器各個(gè)標(biāo)志位的意義和如何設(shè)置,請(qǐng)查閱相關(guān)ATmega16的數(shù)據(jù)手冊(cè),在此不做過(guò)多描述。
需要注意的是:在系統(tǒng)程序的初始化部分中對(duì)外部中斷進(jìn)行設(shè)置時(shí)(定義或改變觸發(fā)方式),應(yīng)先將GICR寄存器中該中斷的中斷允許位清零,禁止MCU響應(yīng)該中斷后再設(shè)置ISCn位。
而在開(kāi)放中斷允許前,一般應(yīng)通過(guò)向GIFR寄存器中的中斷標(biāo)志位INTFn寫(xiě)入邏輯“1”,將該中斷的中斷標(biāo)志位清除,然后開(kāi)放中斷。這樣可以防止在改變ISCn的過(guò)程中誤觸發(fā)中斷。
3、按鍵電路
按鍵電路與上一實(shí)例相同,在此略去。
4、外部中斷程序的編寫(xiě)
我們已經(jīng)知道,要實(shí)現(xiàn)中斷程序,首先要在主程序里面對(duì)相關(guān)中斷寄存器進(jìn)行中斷產(chǎn)生條件的設(shè)置。然后就是編寫(xiě)中斷服務(wù)程序。
本例中中斷寄存器的設(shè)置如下:
MCUCR |= (1 << ISC11) | (1 << ISC01) | (1 << ISC00);
//INT0設(shè)置為上升沿中斷,INT1為下降沿中斷請(qǐng)求
GICR |= (1 << INT0) | (1 << INT1); //允許INT0、INT1中斷
GIFR |= (1 << INTF1) | (1 << INTF0); //清除INT0、INT1中斷標(biāo)志位
sei(); //使能全局中斷
中斷服務(wù)程序的編寫(xiě)具有一定的格式,在不同編譯環(huán)境下各不相同,在WINAVR(GCC)環(huán)境下有兩種方式,分別是:
● SIGNAL(中斷向量名)
{
… //中斷服務(wù)程序內(nèi)容
}
● ISR(中斷向量名 )
{
… //中斷服務(wù)程序內(nèi)容
}
在這兩種方式中,需要分別添加頭文件:#include <avr/signal.h>和#include <avr/interrupt.h>。
宏INTERRUPT 的用法與SIGNAL 類(lèi)似,區(qū)別在于SIGNAL 執(zhí)行時(shí)全局中斷觸發(fā)位被清除、其他中斷被禁止;INTERRUPT 執(zhí)行時(shí)全局中斷觸發(fā)位被置位、其他中斷可嵌套執(zhí)行。
另外avr-libc 提供兩個(gè)API 函數(shù)用于置位和清零全局中斷觸發(fā)位,它們是經(jīng)常用到的,
分別是:void sei(void) 和void cli(void) 由interrupt.h定義
在本實(shí)例中,我們采用包含頭文件#include <avr/interrupt.h>,的方式,使用ISR(中斷向量名 ){…}來(lái)編寫(xiě)中斷函數(shù)。
2.2.3 電路
本實(shí)例的按鍵電路如圖2.2.1所示,數(shù)碼管接口電路與實(shí)例1.3中的電路相同,在此不再給出。
圖2.2.1 按鍵電路
1、電路原理
本實(shí)例用K1、K2兩個(gè)按鍵分別連接到單片機(jī)的PD2、PD3端口,PD2、PD3同時(shí)也是單片機(jī)的外部中斷INT0、INT1的兩個(gè)引腳。當(dāng)按鍵按下時(shí),外部中斷INT0、INT1的兩個(gè)引腳的電平發(fā)生變化,從而產(chǎn)生外部中斷。
2、元器件選擇
在這里列出和本例相關(guān)的、關(guān)鍵部分的器件名稱(chēng)及其在電路中的作用。
● ATmega16:?jiǎn)纹瑱C(jī),檢測(cè)按鍵按下情況并控制數(shù)碼管顯示數(shù)字。
●數(shù)碼管:顯示按鍵狀態(tài)。
● R10:阻值為10K的電阻,下拉限流電阻。
● K1、K2:按鍵,當(dāng)按鍵按下時(shí),與按鍵連接的單片機(jī)端口的電平發(fā)生變化,產(chǎn)生外部中斷。
3、管腳連接
在這里列出和本例相關(guān)的、關(guān)鍵部分的單片機(jī)端口與外圍電路的連接。
● PB0-PB7:連接數(shù)碼管的8個(gè)段,控制數(shù)碼管的顯示。
● PD2、PD3:連接按鍵K1、K2,檢測(cè)兩個(gè)按鍵的狀態(tài)。
●PC6:數(shù)碼管選通端口,該端口通過(guò)三極管9013控制數(shù)碼管的選通,當(dāng)PC6輸出高電平時(shí),數(shù)碼管選通。
2.1.4 程序設(shè)計(jì)
1、程序功能
程序的功能是控制一個(gè)8段數(shù)碼管顯示“0”-“F”16個(gè)十六進(jìn)制的數(shù)字。當(dāng)系統(tǒng)上電時(shí),顯示“0”。K1鍵的作用是加“1”控制鍵:按1次K1鍵,顯示
數(shù)字加1,依次類(lèi)推。當(dāng)?shù)?5次按K1鍵時(shí),顯示“F”,第16次按K1鍵,顯示又從“0”開(kāi)始。K2鍵的作用是減1控制鍵:按1次K1鍵,顯示數(shù)字減
1,減到“0”后,再?gòu)摹癋”開(kāi)始。
● 按鍵開(kāi)關(guān)的軟件去抖和釋放
上一個(gè)實(shí)例已經(jīng)講過(guò)這種方法,本例不再重復(fù)。
● 單片機(jī)外部中斷的編程
在本例中,需要使用單片機(jī)的INT0、INT1,所以在程序中需要對(duì)相應(yīng)的寄存器進(jìn)行設(shè)置,并且編寫(xiě)中斷服務(wù)程序。
● 控制1位數(shù)碼管的顯示
在INT0中斷服務(wù)程序中,當(dāng)按鍵K1按下一次,數(shù)碼管顯示的數(shù)字加1;在INT1的中斷服務(wù)程序中,當(dāng)按鍵K2按下一次,數(shù)碼管顯示的數(shù)字減1。
2、主要變量和函數(shù)說(shuō)明
本例中需要編寫(xiě)中斷函數(shù),函數(shù)的功能是:當(dāng)有外部中斷發(fā)生時(shí),程序跳轉(zhuǎn)到中斷函數(shù)執(zhí)行數(shù)碼管顯示的程序,處理完后跳回主程序繼續(xù)執(zhí)行。
程序中用到變量Counter和 Disp_Buff[16] 。變量Counter用于指示按鍵按下的次數(shù),數(shù)組 Disp_Buff[16]存放數(shù)碼管顯示的字形編碼。
3、使用WINAVR開(kāi)發(fā)環(huán)境,makefile文件同前面的例子,直接復(fù)制到本實(shí)例程序的文件夾中即可。
4、程序代碼
[code="c"]
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> //中斷函數(shù)頭文件
unsigned char Disp_Buff[16] = {0xaf,0xa0,0xc7,0xe6,0xe8,0x6e,0x6f,0xa2,
0xef,0xee,0xeb,0x6d,0x0f,0xe5,0x4f,0x4b};
//數(shù)碼管字型碼表顯示:0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F
volatile unsigned char Counter; //按鍵按下次數(shù)變量,如果在中斷中調(diào)用全局變量,必須加
//volatile來(lái)定義,否則變量不會(huì)變化
int main(void)
{
PORTB = 0X00; //
DDRB = 0Xff; //
PORTC &= ~(1 << PC6); //配置數(shù)碼管0的位選通口為低電平,不導(dǎo)通數(shù)碼管
DDRC |= (1 << PC6); ///配置數(shù)碼管0的位選通口為輸出,選通數(shù)碼管0
PORTD = 0X08; //一定要使能K2的上拉電阻,否則會(huì)有干擾
DDRD = 0XF3; //K1、K2按鍵(PD2、PD3)設(shè)置為輸入端口
MCUCR |= (1 << ISC11) | (1 << ISC01) | (1 << ISC00);
//INT0設(shè)置為上升沿中斷,INT1為下降沿中斷請(qǐng)求
GICR |= (1 << INT0) | (1 << INT1); //允許INT0、INT1中斷
GIFR |= (1 << INTF1) | (1 << INTF0); //清除INT0、INT1中斷標(biāo)志位
Counter = 0; //按鍵按下次數(shù)變量清零
PORTC |= (1 << PC6); //選通數(shù)碼管0
sei(); //使能全局中斷
while(1)
{
PORTB = Disp_Buff[Counter]; //數(shù)碼管顯示按鍵按下次數(shù)
}
}
//外部中斷0函數(shù),當(dāng)按鍵K1按下后,進(jìn)入此中斷
ISR(INT0_vect )
{
_delay_ms(20); //按鍵按下,延時(shí)一會(huì)再判斷是否按下, 以消除干擾
if((PIND & (1 << PD2))) // 按鍵真正按下后,進(jìn)行相應(yīng)處理
{
if(++Counter >= 16) Counter = 0; //次數(shù)大于15,清零
while((PIND & (1 << PD2)));//等待按鍵釋放
}
}
//外部中斷1函數(shù),當(dāng)按鍵K2按下后,進(jìn)入此中斷
ISR(INT1_vect)
{
_delay_ms(20); //判斷按鍵按下,延時(shí)一會(huì)再判斷是否按下, 以消除干擾
if(!(PIND & (1 << PD3))) // 按鍵真正按下后,進(jìn)行相應(yīng)處理
{
if(Counter) --Counter; // 次數(shù)減1
else Counter = 15; // 次數(shù)為零則改成15
while(!(PIND & (1 << PD3))); //
}
}
在語(yǔ)言C語(yǔ)言編寫(xiě)單片機(jī)程序過(guò)程,如果要使用外部中斷服務(wù)程序時(shí),要盡量減少中斷服務(wù)程序的內(nèi)容和長(zhǎng)度。因?yàn)樵谥鞒绦蛑锌赡苓€要相應(yīng)別的中斷,如果一個(gè)中斷服務(wù)程序過(guò)長(zhǎng),很可能會(huì)影響到主程序?qū)ζ渌袛嗟捻憫?yīng)。
常用的處理方法是:在中斷服務(wù)程序中只改變變量的值,或者設(shè)置各種標(biāo)志,在主程序里面對(duì)變量或標(biāo)志進(jìn)行判斷和處理。本實(shí)例為了演示的方便,在中斷程序中進(jìn)
行了所有的操作。應(yīng)該說(shuō)明的是,這種方法是極不可取的。更為合理的方法是:在中斷服務(wù)程序里面只改變Counter的值。其余部分都放到主程序里面進(jìn)行處
理。
另外需要特別強(qiáng)調(diào)的一點(diǎn)是,在用WINAVR編寫(xiě)中斷服務(wù)程序時(shí),如果中斷服務(wù)程序中用到了全局變量,則在定義全局變量時(shí),必須在變量的數(shù)據(jù)類(lèi)型前加
volatile來(lái)定義,否則該變量在中斷服務(wù)程序中不會(huì)變化。這是因?yàn)樵谟肅語(yǔ)言編寫(xiě)單片機(jī)程序時(shí),都會(huì)用到編譯器的“優(yōu)化”代碼功能,以使程序更加簡(jiǎn)
潔、緊湊。但是畢竟編譯器的優(yōu)化是很死板的,他會(huì)把一些對(duì)變量的讀操作優(yōu)化掉。這樣就導(dǎo)致在全局中斷中使用的變量被優(yōu)化成一個(gè)靜止變量,即該變量的值不再
改變。所以我們要把這些變量定義為volatile,意思是提示編譯器:該變量是很容易變化的,不準(zhǔn)對(duì)該變量的讀取進(jìn)行優(yōu)化。這樣在中斷中每次對(duì)變量的讀
寫(xiě)就都可以正確的執(zhí)行了。