4. 代碼設(shè)計(jì)與說(shuō)明 :
/*************************************************
**** AVR 外部中斷使用范例 ***
**** ***
**** 策劃、整理與測(cè)試: 阿莫(armok) ******* 代碼設(shè)計(jì): HJJourAVR ***
**** 編譯器:WINAVR20050214 ***
**** www.OurAVR.com 2005.8.31 ***
*************************************************/
/*
本程序簡(jiǎn)單的示范了如何使用ATMEGA16的外部中斷
中斷的設(shè)置
按鍵的簡(jiǎn)單延時(shí)防抖動(dòng)
中斷的嵌套
變量在中斷中的應(yīng)用---如果變量會(huì)在中斷服務(wù)程序中被修改,須加volatile限定
本范例可直接使出廠狀態(tài)的新M16芯片,無(wú)需對(duì)芯片的熔絲位進(jìn)行配置。
出于簡(jiǎn)化程序考慮,各種數(shù)據(jù)沒(méi)有對(duì)外輸出,學(xué)習(xí)時(shí)建議使用JTAG ICE硬件仿真器
關(guān)于外部中斷作喚醒源的條件:(將會(huì)在后面的電源管理和睡眠模式范例中應(yīng)用)
而INT0和INT1的邊沿觸發(fā)中斷只能在 空閑模式起作用,即 CLKI/O不停止
INT0和INT1的低電平中斷,INT2在各種睡眠模式下都可以,因?yàn)檫@幾種中斷工作 于異步模式,不需要時(shí)鐘驅(qū)動(dòng)
官方的M16中文手冊(cè)對(duì)外部中斷的描敘存在多處錯(cuò)誤,請(qǐng)參考英文原版。*/
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
/*宏INTERRUPT 的用法與SIGNAL 類似,區(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定義 */
//注: 內(nèi)部函數(shù)_delay_ms() 最高延時(shí) 262.144mS@1MHz/* 該函數(shù)可以實(shí)現(xiàn)較精確的定時(shí),但用JTAG仿真時(shí)較麻煩---會(huì)進(jìn)入機(jī)器碼窗口(Disassembeler).注意跳開該語(yǔ)段。一旦JTAG仿真進(jìn)入該內(nèi)部函數(shù)語(yǔ)句,會(huì)變得像"死機(jī)"一樣(其實(shí)在運(yùn)行中),可以先[break],然后在后面的C語(yǔ)句設(shè)[breakpoint],[RUN]跳過(guò)*/// for()/while()語(yǔ)句計(jì)算延時(shí)時(shí)間較麻煩。// 為了使 _delay_ms()函數(shù)的延時(shí)正確,須在makefile中設(shè)定F_CPU為實(shí)際的系統(tǒng)時(shí)鐘頻// 本范例為1MHz內(nèi)部RC振蕩器 即 F_CPU=1000000
/*
C:\WinAVR\avr\include\avr\目錄包括所有芯片的定義和其他頭文件
其中iom16.h 定義ATMEGA16芯片的特性(中斷向量,寄存器,位定義...)
包括下面中斷服務(wù)程序的常量 SIG_INTERRUPTx ,PORTx,GICR.....
*/
//管腳定義
#define EXT_INT0 2 //PD2 按鍵0
#define EXT_INT1 3 //PD3 按鍵1
#define EXT_INT2 2 //PB2 按鍵2
#define LED0 0 //PB0
#define LED1 1 //PB1
#define LED2 3 //PB3
//宏定義
#define LED0_ON() PORTB|= (1<<LED0) //輸出高電平,燈亮
#define LED0_OFF() PORTB&=~(1<<LED0) //輸出低電平,燈滅
#define LED1_ON() PORTB|= (1<<LED1)
#define LED1_OFF() PORTB&=~(1<<LED1)
#define LED2_ON() PORTB|= (1<<LED2)
#define LED2_OFF() PORTB&=~(1<<LED2)
//51系列的高電平輸出能力很弱,低電平也僅能點(diǎn)亮LED.所以常見(jiàn)輸出低電平才燈亮的接法。
//AVR芯片的高低驅(qū)動(dòng)能力都很強(qiáng),甚至能推動(dòng)8字?jǐn)?shù)碼管的公共極,怎么接都沒(méi)問(wèn)題。
//全局變量
#define has_volatile 1 //這里是條件編譯
//可以修改has_volatile=1或0來(lái)看程序運(yùn)行的效果
#if has_volatile
volatile unsigned char FLAG; //全局變量,會(huì)在中斷服務(wù)程序中被修改,須加volatile限定
#else
unsigned char FLAG; //全局變量.
#endif
//仿真時(shí)在watch窗口,監(jiān)控這些變量。
SIGNAL(SIG_INTERRUPT0) //INT0中斷服務(wù)程序 { //硬件自動(dòng)清除INTF0標(biāo)志位 _delay_ms(10); //延時(shí) if ((PIND&(1<<EXT_INT0))==0) //重復(fù)檢測(cè),防抖動(dòng) LED0_ON(); //點(diǎn)亮LED0 loop_until_bit_is_set(PIND,EXT_INT0); //等待按鍵釋放(變?yōu)楦唠娖? _delay_ms(10); //延時(shí) 按鍵釋放時(shí)也會(huì)抖動(dòng)。 // 即使同時(shí)發(fā)生其它的中斷事件,如果在這里把相應(yīng)的中斷標(biāo)志位清除,那么該中斷將 不能觸發(fā)進(jìn)入中斷服務(wù) /* 注意 讀端口用 PINx 寫端口用 PORTx */ }
INTERRUPT(SIG_INTERRUPT1) //INT1中斷服務(wù)程序 { //硬件自動(dòng)清除INTF1標(biāo)志位 //這里全局中斷被打開,將允許其他中斷嵌套執(zhí)行 _delay_ms(10); if ((PIND&(1<<EXT_INT1))==0) LED1_ON(); //點(diǎn)亮LED1 loop_until_bit_is_set(PIND,EXT_INT1); _delay_ms(10); }SIGNAL(SIG_INTERRUPT2) //INT2中斷服務(wù)程序 { //硬件自動(dòng)清除INTF2標(biāo)志位 _delay_ms(10); if ((PINB&(1<<EXT_INT2))==0) { LED0_OFF(); //熄滅LED0 LED1_OFF(); //熄滅LED1 } loop_until_bit_is_set(PINB,EXT_INT2); FLAG=!FLAG; //修改全局變量 _delay_ms(100); }int main(void) { //上電默認(rèn)DDRx=0x00,PORTx=0x00 輸入,無(wú)上拉電阻 PORTA =0xFF; //不用的管腳使能內(nèi)部上拉電阻。 PORTC =0xFF; PORTD =0xFF; DDRB = (1<<LED2)|(1<<LED1)|(1<<LED0); //輸出 PORTB =~((1<<LED2)|(1<<LED1)|(1<<LED0)); //低電平,燈滅 //外部中斷INT0,1,2 做按鍵輸入,使能內(nèi)部上拉,就可以不用外接電阻了 MCUCR=(1<<ISC11)|(0<<ISC10)|(1<<ISC01)|(0<<ISC00); //注意該寄存器有多個(gè)功能 /* ISCx1:0=00 INTx引腳為低電平時(shí)產(chǎn)生中斷請(qǐng)求 ISCx1:0=01 INTx引腳上任意的邏輯電平變化都將引發(fā)中斷 ISCx1:0=10 INTx引腳的下降沿產(chǎn)生中斷請(qǐng)求 ISCx1:0=11 INTx引腳的上升沿產(chǎn)生中斷請(qǐng)求 */ MCUCSR&=~(1<<ISC2); //注意該寄存器有多個(gè)功能 /* ISC2=0 INT2引腳的下降沿產(chǎn)生異步中斷請(qǐng)求 ISC2=1 INT2引腳的上升沿產(chǎn)生異步中斷請(qǐng)求 */ GIFR=(1<<INTF1)|(1<<INTF0)|(1<<INTF2);//寫1清除標(biāo)志位,在使能中斷前最好先把對(duì)應(yīng) // 的標(biāo)志位清除,以免誤觸發(fā) GICR=(1<<INT1)|(1<<INT0)|(1<<INT2); //使能三個(gè)外部中斷
FLAG=0; sei(); //使能全局中斷 while (1) { while (FLAG==0); LED2_ON(); //如果FLAG不加volatile限定(即has_volatile=0), //程序?qū)⒂肋h(yuǎn)都運(yùn)行不到這里。 while (FLAG!=0); LED2_OFF(); } } /* 程序運(yùn)行效果 按下按鍵0,LED0亮。直到松手,其他按鍵才能起作用 按下按鍵1,LED1亮。其他按鍵隨時(shí)都能起作用 按下按鍵2,LED0/1都熄滅。直到松手,其他按鍵才能起作用 LED2是根據(jù)按鍵2的順序來(lái)亮滅,松手后變換,前提是FLAG加了volatile限