

6.5單片機(jī)中斷系統(tǒng)
6.5.1中斷的產(chǎn)生背景
請設(shè)想這樣一個場景:此刻我正在廚房用煤氣燒一壺水,而燒開一壺水剛好需要10分鐘,我是一個主體,燒水是一個目的,而且我只能時時刻刻在這里燒水,因為一旦水開了溢出來澆滅煤氣的話,有可能引發(fā)一場災(zāi)難。但就在這個時候呢,我又聽到了電視里傳來《天龍八部》的主題歌,馬上就要開演了,我真想奪門而出,去看我最喜歡的電視劇。然而,聽到這個水壺發(fā)出的“咕嘟”的聲音,我清楚:除非等水燒開了,否則我是無法享受我喜歡的電視劇的。
這里邊主體只有一個我,而我要做的有兩件事情,一個是看電視,一個是燒水,而電視和燒水是兩個獨立的客體,它們是同時進(jìn)行的。其中燒水需要10分鐘,但不需要了解燒水的過程,只需要得到水燒開的這樣一個結(jié)果就行了,提下水壺和關(guān)閉煤氣只需要幾秒的時間而已。所以采取的辦法就是:燒水的時候,定上一個鬧鐘,定時10分鐘,然后我就可以安心看電視了。當(dāng)10分鐘時間到了,鬧鐘響了,此刻水也燒開了,我就過去把煤氣滅掉,然后繼續(xù)回來看電視就可以了。
這個場景和單片機(jī)有什么關(guān)系呢?
在單片機(jī)的程序處理過程中也有很多類似的場景,當(dāng)單片機(jī)正在專心致志的做一件事情(看電視)的時候,總會有一件或者多件緊迫或者不緊迫的事情發(fā)生,需要去關(guān)注,有一些需要停下手頭的工作去馬上去處理(比如水開了),只有處理完了,才能回頭繼續(xù)完成剛才的工作(看電視)。這種情況下單片機(jī)的中斷系統(tǒng)就該發(fā)揮它的強(qiáng)大作用了。合理巧妙的利用中斷,不僅可以使單片機(jī)獲得處理突發(fā)狀況的能力,而且可以讓它能夠“同時”完成多項任務(wù)。
6.5.2定時器中斷的應(yīng)用
在第5章學(xué)過了定時器,實際應(yīng)用中定時器一般用法都是采取中斷方式來做的,在第5章采用的是查詢法,使用if(TF0==1)語句的目的是明確告訴讀者,定時器和中斷不是一回事,定時器是單片機(jī)模塊的一個資源,確確實實存在的一個模塊,而中斷是單片機(jī)的一種運行機(jī)制。尤其是初學(xué)者,很多人會誤以為定時器和中斷是一個東西,只有定時器才會觸發(fā)中斷,但實際上很多事件都會觸發(fā)中斷,除了“燒水”,還有“有人按門鈴”,“來電話了”等。
標(biāo)準(zhǔn)51單片機(jī)控制中斷的寄存器有兩個,一個是中斷使能寄存器,另一個是中斷優(yōu)先級寄存器,這里先介紹中斷使能寄存器,如表6-1和表6-2所示。隨著一些增強(qiáng)型51單片機(jī)的問世,可能會有增加的寄存器,大家理解了這里所講的,其它的通過自己研讀數(shù)據(jù)手冊就可以理解明白并且用起來了。
表6-1 IE——中斷使能寄存器的位分配(地址0xA8、可位尋址)

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

中斷使能寄存器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)顯示的程序改用中斷再實現(xiàn)出來,同時數(shù)碼管顯示抖動和“鬼影”也一并處理掉了。程序運行的流程跟圖6-1所示的流程圖是基本一致的,但因為加入了中斷,所以整個流程被分成了兩部分,轉(zhuǎn)換為數(shù)碼管顯示字符的部分還留在主循環(huán)內(nèi),而實現(xiàn)1秒定時和動態(tài)掃描部分則移到了中斷函數(shù)內(nèi),并加入了消隱的處理。下面來看程序:
#include
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ù)
unsigned char flag1s = 0; //1秒定時標(biāo)志
void main()
{
unsigned long sec = 0; //記錄經(jīng)過的秒數(shù)
EA = 1; //使能總中斷
ENLED = 0; //使能U3,選擇控制數(shù)碼管
ADDR3 = 1; //因為需要動態(tài)改變ADDR0-2的值,所以不需要再初始化了
TMOD = 0x01; //設(shè)置T0為模式1
TH0 = 0xFC; //為T0賦初值0xFC67,定時1ms
TL0 = 0x67;
ET0 = 1; //使能T0中斷
TR0 = 1; //啟動T0
while (1)
{
if (flag1s == 1) //判斷1秒定時標(biāo)志
{
flag1s = 0; //1秒定時標(biāo)志清零
sec++; //秒計數(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ù)計數(shù)值加1
if (cnt >= 1000) //中斷1000次即1秒
{
cnt = 0; //清零計數(shù)值以重新開始下1秒計時
flag1s = 1; //設(shè)置1秒定時標(biāo)志為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ī)里運行,看看實際效果。是否可以看到,近乎完美的顯示效果經(jīng)過努力終于做成功了,下面來解析一下這個程序。
在這個程序中,有兩個函數(shù),一個是主函數(shù),一個是中斷服務(wù)函數(shù)。主函數(shù)main()就不用說了,重點強(qiáng)調(diào)一下中斷服務(wù)函數(shù),它的書寫格式是固定的,首先中斷函數(shù)前邊void表示函數(shù)返回空,即中斷函數(shù)不返回任何值,函數(shù)名是InterruptTimer0(),這個函數(shù)名在符合函數(shù)命名規(guī)則的前提下可以隨便取,取這個名字是為了方便區(qū)分和記憶,而后是interrupt這個關(guān)鍵字,一定不能錯,這是中斷特有的關(guān)鍵字,另外后邊還有個數(shù)字1,這個數(shù)字1怎么來的呢?來看表6-3。
表6-3 中斷查詢序列

這個表格同樣不需要記憶,有需要的時候過來查。第二行的T0中斷,要使能這個中斷那么就要把它的中斷使能位ET0置1,當(dāng)它的中斷標(biāo)志位TF0變?yōu)?時,就會觸發(fā)T0中斷了,那么這時就應(yīng)該來執(zhí)行中斷函數(shù)了,單片機(jī)又怎樣找到這個中斷函數(shù)呢?靠的就是中斷向量地址,所以interrupt后面中斷函數(shù)編號的數(shù)字x就是根據(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)顯示間隔是固定的1ms,不會因為程序執(zhí)行時間不一致的原因?qū)е聰?shù)碼管顯示的抖動了。
6.5.3中斷的優(yōu)先級
中斷優(yōu)先級的內(nèi)容,本小節(jié)先做簡單介紹,后邊實際應(yīng)用的時候再詳細(xì)介紹。
在講中斷產(chǎn)生背景的時候,僅僅講了看電視和燒水的例子,但是實際生活當(dāng)中還有更復(fù)雜的,比如我正在看電視,這個時候來電話了,我要進(jìn)入接電話的“中斷”程序當(dāng)中去,就在接電話的同時,聽到了水開的聲音,水開的“中斷”也發(fā)生了,我就必須要放下手上的電話,先把煤氣關(guān)掉,然后再回來聽電話,最后聽完了電話再看電視,這里就產(chǎn)生了一個優(yōu)先級的問題。
還有一種情況,我在看電視的時候,這個時候聽到水開的聲音,水開的“中斷”發(fā)生了,我要進(jìn)入關(guān)煤氣的“中斷”程序當(dāng)中,而在關(guān)煤氣的同時,電話聲音響了,而這個時候的處理方式是先把煤氣關(guān)閉,再去接聽電話,最后再看電視。
從這兩個過程中,可以得到一個結(jié)論,就是最最緊急的事情,一旦發(fā)生后,不管當(dāng)時處在哪個“程序”當(dāng)中,必須先去處理最最緊急的事情,處理完畢后再去解決其它事情。在單片機(jī)程序當(dāng)中有時候也是這樣的,有一般緊急的中斷,有特別緊急的中斷,這取決于具體的系統(tǒng)設(shè)計,這就涉及到中斷優(yōu)先級和中斷嵌套的概念,在本章節(jié)先簡單介紹一下相關(guān)寄存器,不做例程說明。
中斷優(yōu)先級有兩種,搶占優(yōu)先級和固有優(yōu)先級。先介紹搶占優(yōu)先級,如表6-4和表6-5。
表6-4 IP——中斷優(yōu)先級寄存器的位分配(地址0xB8、可位尋址)

表6-5 IP——中斷優(yōu)先級寄存器的位描述

IP這個寄存器的每一位,表示對應(yīng)中斷的搶占優(yōu)先級,每一位的復(fù)位值都是0,當(dāng)把某一位設(shè)置為1的時候,這一位的優(yōu)先級就比其它位的優(yōu)先級高了。比如設(shè)置了PT0位為1后,當(dāng)單片機(jī)在主循環(huán)或者任何其它中斷程序中執(zhí)行時,一旦定時器T0發(fā)生中斷,作為更高的優(yōu)先級,程序馬上就會跑到T0的中斷程序中來執(zhí)行。反過來,當(dāng)單片機(jī)正在T0中斷程序中執(zhí)行時,如果有其它中斷發(fā)生了,還是會繼續(xù)執(zhí)行T0中斷程序,直到把T0中的中斷程序執(zhí)行完畢以后,才會去執(zhí)行其它中斷程序。
當(dāng)進(jìn)入低優(yōu)先級中斷中執(zhí)行時,如又發(fā)生了高優(yōu)先級的中斷,則立刻進(jìn)入高優(yōu)先級中斷執(zhí)行,處理完高優(yōu)先級級中斷后,再返回處理低優(yōu)先級中斷,這個過程就叫做中斷嵌套,也稱為搶占。所以搶占優(yōu)先級的概念就是,優(yōu)先級高的中斷可以打斷優(yōu)先級低的中斷的執(zhí)行,從而形成嵌套。當(dāng)然反過來,優(yōu)先級低的中斷是不能打斷優(yōu)先級高的中斷的。
那么既然有搶占優(yōu)先級,自然就也有非搶占優(yōu)先級了,也稱為固有優(yōu)先級。在表6-3中的最后一列給出的就是固有優(yōu)先級,請注意,在中斷優(yōu)先級的編號中,一般都是數(shù)字越小優(yōu)先級越高。從表中可以看到一共有1~6共6級的優(yōu)先級,這里的優(yōu)先級與搶占優(yōu)先級的不同點就是,它不具有搶占的特性,也就是說即使在低優(yōu)先級中斷執(zhí)行過程中又發(fā)生了高優(yōu)先級的中斷,那么這個高優(yōu)先級的中斷也只能等到低優(yōu)先級中斷執(zhí)行完后才能得到響應(yīng)。既然不能搶占,那么這個優(yōu)先級有什么用呢?
答案是多個中斷同時存在時的仲裁。比如說有多個中斷同時發(fā)生了,當(dāng)然實際上發(fā)生這種情況的概率很低,但另外一種情況就常見的多了,那就是出于某種原因暫時關(guān)閉了總中斷,即EA=0,執(zhí)行完一段代碼后又重新使能了總中斷,即EA=1,那么在這段時間里就很可能有多個中斷都發(fā)生了,但因為總中斷是關(guān)閉的,所以它們當(dāng)時都得不到響應(yīng),而當(dāng)總中斷再次使能后,它們就會同時請求響應(yīng),很明顯,這時也必需有個先后順序才行,這就是非搶占優(yōu)先級的作用——如表6-3中,誰優(yōu)先級最高先響應(yīng)誰,然后按編號排隊,依次得到響應(yīng)。
搶占優(yōu)先級和非搶占優(yōu)先級的協(xié)同,可以使單片機(jī)中斷系統(tǒng)有條不紊的工作,既不會無休止的嵌套,又可以保證必要時緊急任務(wù)得到優(yōu)先處理。在后續(xù)的學(xué)習(xí)過程中,中斷系統(tǒng)與讀者如影隨形,處處都有它的身影,隨著學(xué)習(xí)的深入,相信會對它的理解也會更加的深入。
6.6練習(xí)題
1、掌握C語言數(shù)組的概念、定義和應(yīng)用。
2、掌握if語句和switch語句的用法及區(qū)別,編程的時候能夠正確選擇使用哪個語句。
3、徹底理解中斷的原理和應(yīng)用方法,關(guān)閉教程自己獨立把本章節(jié)程序編寫完畢并且下載到實驗板上實踐。
4、嘗試修改程序,讓數(shù)碼管只顯示有效位,也就是高位的0不顯示。
5、嘗試寫一個從999999開始倒計時的程序,并且改用定時器T1的中斷來完成,通過寫這個程序,熟練掌握定時器和中斷的應(yīng)用。
審核編輯 黃宇
-
數(shù)碼管
+關(guān)注
關(guān)注
32文章
1894瀏覽量
94145
發(fā)布評論請先 登錄
數(shù)碼管 選型手冊2025年最新版
【EASY EAI Nano-TB(RV1126B)開發(fā)板試用】+串行數(shù)碼管的顯示驅(qū)動
原廠 FZH853 8×4顯示掃描模式單線通訊LED恒流驅(qū)動專用電路
第7章 變量進(jìn)階與點陣LED(7.3 7.4)
第6章 中斷與數(shù)碼管動態(tài)顯示(6.1 6.2)
第5章 定時器與數(shù)碼管基礎(chǔ)(5.3)
【瑞薩RA6E2】ADC數(shù)據(jù)采集與數(shù)碼管顯示
原廠 FZH114C 一款LED(發(fā)光二極管、數(shù)碼管、點陣屏)驅(qū)動控制專用芯片
FZH114 LED(發(fā)光二極管、數(shù)碼管、點陣屏)驅(qū)動控制專用芯片,集成數(shù)字通訊電路、 解碼電路、數(shù)據(jù)鎖存器、震蕩器
【「高速數(shù)字設(shè)計(基礎(chǔ)篇)」閱讀體驗】+第6章閱讀體驗
級聯(lián)動態(tài)掃描顯示數(shù)碼管問題
抗噪數(shù)顯驅(qū)動數(shù)碼管顯示IC芯片VK1616
【應(yīng)用】工業(yè)現(xiàn)場的“智能顯示管家”:億佰特EID051-1xx系列 Modbus數(shù)碼管解析
第6章 中斷與數(shù)碼管動態(tài)顯示(6.5 6.6)
評論