鍍金池/ 教程/ 數(shù)據(jù)庫/ 6.6 單片機(jī)中斷系統(tǒng)
2.1 單片機(jī)內(nèi)部資源(內(nèi)部結(jié)構(gòu))
5.1 單片機(jī)邏輯電路與邏輯運(yùn)算
6.1 C 語言數(shù)組
5.7 單片機(jī)數(shù)碼管的靜態(tài)顯示
4.8 單片機(jī) LED 流水燈程序[詳細(xì)]
6.7 單片機(jī)中斷的優(yōu)先級
5.2 單片機(jī)定時器介紹
1. 如何學(xué)習(xí)單片機(jī)
1.3 單片機(jī)學(xué)習(xí)的準(zhǔn)備工作
4.3 C 語言基本運(yùn)算符
5.5 單片機(jī)中 LED 數(shù)碼管的介紹
2.6 第一個單片機(jī)程序(C 語言編寫)
3.5 單片機(jī)中 74HC138 三八譯碼器的應(yīng)用
3.2 單片機(jī)中去耦電容的應(yīng)用
6.4 單片機(jī)數(shù)碼管動態(tài)顯示程序[帶解釋]
1.5 Keil uVision4簡單使用教程
第一章問題匯總
1.2 如何學(xué)習(xí)單片機(jī)
4.4 C 語言 for 循環(huán)語句
6.5 單片機(jī)數(shù)碼管顯示消隱
單片機(jī)上的發(fā)光二極管(LED 燈)
5. 單片機(jī)定時器與數(shù)碼管基礎(chǔ)
5.3 單片機(jī)定時器的寄存器
5.6 單片機(jī)數(shù)碼管的真值表
6.2 C 語言 if 語句
3.3 三極管的的概念及其工作原理
4. C 語言基礎(chǔ)及流水燈的實(shí)現(xiàn)
點(diǎn)亮你的 LED 燈
4.6 C 語言函數(shù)的簡單介紹
2.4 51單片機(jī)特殊功能寄存器和位定義
6.6 單片機(jī)中斷系統(tǒng)
3.1 電磁干擾 EMI
4.1 二進(jìn)制、十進(jìn)制和十六進(jìn)制
4.5 C 語言 while 循環(huán)語句
3. 單片機(jī)硬件基礎(chǔ)知識學(xué)習(xí)
2.5 使用 Keil uVision 新建一個工程
1.4 單片機(jī)開發(fā)環(huán)境搭建--Keil uVision4安裝教程
4.7 單片機(jī)延時方法(Keil 軟件延時)
6. 單片機(jī)的中斷與數(shù)碼管動態(tài)顯示
3.6 單片機(jī) LED 燈閃爍程序
6.3 C 語言 switch 語句
1.1 學(xué)習(xí)什么單片機(jī)
4.2 C 語言變量類型和范圍
5.4 單片機(jī)中定時器的應(yīng)用
2.7 將程序下載到單片機(jī)
3.4 單片機(jī)中三極管的應(yīng)用
2.2 單片機(jī)最小系統(tǒng)[配圖][超詳細(xì)]

6.6 單片機(jī)中斷系統(tǒng)

中斷的產(chǎn)生背景

請?jiān)O(shè)想這樣一個場景:此刻我正在廚房用煤氣燒一壺水,而燒開一壺水剛好需要10分鐘,我是一個主體,燒水是一個目的,而且我只能時時刻刻在這里燒水,因?yàn)橐坏┧_了,溢出來澆滅煤氣的話,有可能引發(fā)一場災(zāi)難。但就在這個時候呢,我又聽到了電視里傳來《天龍八部》的主題歌,馬上就要開演了,我真想奪門而出,去看我最喜歡的電視劇。然而,聽到這個水壺發(fā)出的“咕嘟”的聲音,我清楚:除非等水燒開了,否則我是無法享受我喜歡的電視劇的。

這里邊主體只有一個我,而我要做的有兩件事情,一個是看電視,一個是燒水,而電視和燒水是兩個獨(dú)立的客體,它們是同時進(jìn)行的。其中燒水需要10分鐘,但不需要了解燒水的過程,只需要得到水燒開的這樣一個結(jié)果就行了,提下水壺和關(guān)閉煤氣只需要幾秒的時間而已。所以我們采取的辦法就是:燒水的時候,定上一個鬧鐘,定時10分鐘,然后我就可以安心看電視了。當(dāng)10分鐘時間到了,鬧鐘響了,此刻水也燒開了,我就過去把煤氣滅掉,然后繼續(xù)回來看電視就可以了。

這個場景和單片機(jī)有什么關(guān)系呢?

在單片機(jī)的程序處理過程中也有很多類似的場景,當(dāng)單片機(jī)正在專心致志的做一件事情(看電視)的時候,總會有一件或者多件緊迫或者不緊迫的事情發(fā)生,需要我們?nèi)リP(guān)注,有一些需要我們停下手頭的工作去馬上去處理(比如水開了),只有處理完了,才能回頭繼續(xù)完成剛才的工作(看電視)。這種情況下單片機(jī)的中斷系統(tǒng)就該發(fā)揮它的強(qiáng)大作用了,合理巧妙的利用中斷,不僅可以使我們獲得處理突發(fā)狀況的能力,而且可以使單片機(jī)能夠“同時”完成多項(xiàng)任務(wù)。

定時器中斷的應(yīng)用

在第五章我們學(xué)過了定時器,而實(shí)際上定時器一般用法都是采取中斷方式來做的,我是故意在第五章用查詢法,就是使用 if(TF0==1)這樣的語句先用定時器,目的是明確告訴同學(xué)們,定時器和中斷不是一回事,定時器是單片機(jī)模塊的一個資源,確確實(shí)實(shí)存在的一個模塊,而中斷,是單片機(jī)的一種運(yùn)行機(jī)制。尤其是初學(xué)者們,很多人會誤以為定時器和中斷是一個東西,只有定時器才會觸發(fā)中斷,但實(shí)際上很多事件都會觸發(fā)中斷的,除了“燒水”,還有“有人按門鈴”,“來電話了”等等。

標(biāo)準(zhǔn)51單片機(jī)中控制中斷的寄存器有兩個,一個是中斷使能寄存器,另一個是中斷優(yōu)先級寄存器,這里先介紹中斷使能寄存器,如表6-1和表6-2所示。隨著一些增強(qiáng)型51單片機(jī)的問世,可能會有增加的寄存器,大家理解了我們這里所講的,其它的通過自己研讀數(shù)據(jù)手冊就可以理解明白并且用起來了。

表6-1 IE——中斷使能寄存器的位分配(地址 0xA8、可位尋址)

7 6 5 4 3 2 1 0
符號 EA -- ET2 ES ET1 EX1 ET0 EX0
復(fù)位值 0 -- 0 0 0 0 0 0

表 6-2 IE——中斷使能寄存器的位描述

符號 描述
7 EA 總中斷使能位,相當(dāng)于總開關(guān)
6 -- --
5 ET2 定時器2中斷使能
4 ES 串口中斷使能
3 ET1 定時器1中斷使能
2 EX1 外部中斷1使能
1 ET0 定時器0中斷使能
0 EX0 外部中斷0使能

中斷使能寄存器 IE 的位0~5控制了6個中斷使能,而第6位沒有用到,第7位是總開關(guān)??傞_關(guān)就相當(dāng)于我們家里或者學(xué)生宿舍里的那個電源總閘門,而0~5位這6個位相當(dāng)于每個分開關(guān)。那么也就是說,我們只要用到中斷,就要寫 EA = 1 這一句,打開中斷總開關(guān),然后用到哪個分中斷,再打開相對應(yīng)的控制位就可以了。

我們現(xiàn)在就把前面的數(shù)碼管動態(tài)顯示的程序改用中斷再實(shí)現(xiàn)出來,同時數(shù)碼管顯示抖動和“鬼影”也一并處理掉了。程序運(yùn)行的流程跟圖6-1所示的流程圖是基本一致的,但因?yàn)榧尤肓酥袛?,所以整個流程被分成了兩部分,秒計(jì)數(shù)和轉(zhuǎn)換為數(shù)碼管顯示字符的部分還留在主循環(huán)內(nèi),而動態(tài)掃描部分則移到了中斷函數(shù)內(nèi),并加入了消隱的處理。下面來看程序:

#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = { //數(shù)碼管顯示字符轉(zhuǎn)換表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = { //數(shù)碼管顯示緩沖區(qū),初值 0xFF 確保啟動時都不亮
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

unsigned char i = 0;  //動態(tài)掃描的索引
unsigned int cnt = 0;  //記錄 T0 中斷次數(shù)

void main(){
    unsigned long sec = 0;  //記錄經(jīng)過的秒數(shù)
    EA = 1;  //使能總中斷
    ENLED = 0;  //使能 U3,選擇控制數(shù)碼管
    ADDR3 = 1;  //因?yàn)樾枰獎討B(tài)改變 ADDR0-2 的值,所以不需要再初始化了
    TMOD = 0x01;  //設(shè)置 T0 為模式1
    TH0 = 0xFC;  //為 T0 賦初值 0xFC67,定時 1 ms
    TL0 = 0x67;
    ET0 = 1;  //使能 T0 中斷
    TR0 = 1;  //啟動 T0

    while (1){
        if (cnt >= 1000){  //判斷 T0 溢出是否達(dá)到1000次
            cnt = 0;  //達(dá)到1000次后計(jì)數(shù)值清零
            sec++;  //秒計(jì)數(shù)自加1

            //以下代碼將 sec 按十進(jìn)制位從低到高依次提取并轉(zhuǎn)為數(shù)碼管顯示字符
            LedBuff[0] = LedChar[sec%10];
            LedBuff[1] = LedChar[sec/10%10];
            LedBuff[2] = LedChar[sec/100%10];
            LedBuff[3] = LedChar[sec/1000%10];
            LedBuff[4] = LedChar[sec/10000%10];
            LedBuff[5] = LedChar[sec/100000%10];
        }
    }
}
/* 定時器0中斷服務(wù)函數(shù) */
void InterruptTimer0() interrupt 1{
    TH0 = 0xFC;  //重新加載初值
    TL0 = 0x67;
    cnt++;  //中斷次數(shù)計(jì)數(shù)值加1

    //以下代碼完成數(shù)碼管動態(tài)掃描刷新
    P0 = 0xFF;
    //顯示消隱
    switch (i){
        case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
        case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
        case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
        case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
        case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
        case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
        default: break;
    }
}

大家可以先把程序抄下來,編譯下載到單片機(jī)里運(yùn)行,看看實(shí)際效果。是否可以看到,近乎完美的顯示效果經(jīng)過我們的努力終于做成功了。下面我們還要再來解析一下這個程序。

在這個程序中,有兩個函數(shù),一個是主函數(shù),一個是中斷服務(wù)函數(shù)。主函數(shù) main()我們就不用說了,重點(diǎn)強(qiáng)調(diào)一下中斷服務(wù)函數(shù),它的書寫格式是固定的,首先中斷函數(shù)前邊 void 表示函數(shù)返回空,即中斷函數(shù)不返回任何值,函數(shù)名是 InterruptTimer0(),這個函數(shù)名在符合函數(shù)命名規(guī)則的前提下可以隨便取,我們?nèi)∵@個名字是為了方便區(qū)分和記憶,而后是 interrupt 這個關(guān)鍵字,一定不能錯,這是中斷特有的關(guān)鍵字,另外后邊還有個數(shù)字1,這個數(shù)字1怎么來的呢?我們先來看表6-3。

表6-3 中斷查詢序列

中斷函數(shù)編號 中斷名稱 中斷標(biāo)志位 中斷使能位 中斷向量地址 默認(rèn)優(yōu)先級
0 外部中斷0 IE0 EX0 0x0003 1(最高)
1 T0 中斷 TF0 ET0 0x000B 2
2 外部中斷1 IE1 EX1 0x0013 3
3 T1 中斷 TF1 ET1 0x001B 4
4 UART 中斷 TI/RI ES 0x0023 5
5 T2 中斷 TF2/EXF2 ET2 0x002B 6

這個表格同樣不需要大家記住,需要的時候過來查就可以了。我們現(xiàn)在看第二行的 T0 中斷,要使能這個中斷那么就要把它的中斷使能位 ET0 置1,當(dāng)它的中斷標(biāo)志位 TF0 變?yōu)?時,就會觸發(fā) T0 中斷了,那么這時就應(yīng)該來執(zhí)行中斷函數(shù)了,單片機(jī)又怎樣找到這個中斷函數(shù)呢?靠的就是中斷向量地址,所以 interrupt 后面中斷函數(shù)編號的數(shù)字 x 就是根據(jù)中斷向量得出的,它的計(jì)算方法是 x*8+3=向量地址。當(dāng)然表中都已經(jīng)給算好放在第一欄了,我們可以直接查出來用就行了。到此為止,中斷函數(shù)的命名規(guī)則我們就都搞清楚了。

中斷函數(shù)寫好后,每當(dāng)滿足中斷條件而觸發(fā)中斷后,系統(tǒng)就會自動來調(diào)用中斷函數(shù)。比如我們上面這個程序,平時一直在主程序 while(1)的循環(huán)中執(zhí)行,假如程序有100行,當(dāng)執(zhí)行到50行時,定時器溢出了,那么單片機(jī)就會立刻跑到中斷函數(shù)中執(zhí)行中斷程序,中斷程序執(zhí)行完畢后再自動返回到剛才的第50行處繼續(xù)執(zhí)行下面的程序,這樣就保證了動態(tài)顯示間隔是固定的 1 ms,不會因?yàn)槌绦驁?zhí)行時間不一致的原因?qū)е聰?shù)碼管顯示的抖動了。