前面我們學習了定時器的簡單用法,并且設(shè)計了幾個有趣的小項目
今天我們學習進階操作——中斷
1 認識中斷
一個生活的小場景……
你拿著手機,屏幕上顯示著Zi Jin Code的最新文章,一旁的藍牙音響播放著悠揚的古風音樂……目不轉(zhuǎn)睛之刻,朋友給你打來一個電話……你調(diào)小了藍牙音箱的音量,接起了電話,你聊得正歡時,門外傳來敲門聲,班主任來了……
相信這樣的場景,你應該也遇到不少,我們常常正在做一件事情,突然另一件事冒出來打斷我們,讓我們不得不趕快去處理這件事
事實上,單片機的世界里,這樣的事件比比皆是。舉個最簡單的例子,在一個秒表電路中,單片機一直輸出數(shù)碼管掃描顯示,這時候定時器溢出了,單片機不得不先放下手中的活,去處理時器溢出的操作,處理完后又返回到剛剛停下來的位置繼續(xù)來執(zhí)行輸出顯示的操作。這個過程中,單片機放下輸出顯示的任務(wù),先跑去執(zhí)行定時器溢出的一些操作,就是一次中斷的過程,用圖表示如下
單片機正常執(zhí)行程序的過程中,一個特殊的事件讓單片機停下現(xiàn)在正在執(zhí)行的任務(wù),跳轉(zhuǎn)去處理這個事件的任務(wù),處理完之后再返回到剛剛停下來的地方,繼續(xù)執(zhí)行最初放下來的任務(wù),這個過程就叫中斷,觸發(fā)的這個事件叫做中斷源
到此,相信我們已經(jīng)初步了解中斷的過程,思考以下,那之前我們寫的代碼(如下)是使用了定時器中斷嗎
#include< reg52.h >
sbit LED = P1^0;//LED接在P1.0
unsigned char Counter = 0;//溢出次數(shù)記錄
void main()
{
TH0 = 0x00;
TL0 = 0x00;
TR0 = 1;//Timer0初始值設(shè)置,使能定時器
LED = 1;//初始化LED引腳電平值
while(1)
{
if(TF0 == 1)
{
Counter++;//定時器溢出次數(shù)++
TH0 = 0x00;//溢出以后要對定時器進行簡單的重新裝載初始值
TL0 = 0x00;
TF0 = 0;//重新設(shè)置定時器溢出位的數(shù)值
}
if(counter == 256)//定時器累計溢出256次
{
Counter = 0;//復位
LED = !LED;//LED電平取反
}
}
}
從程序運行流程分析,這并不是一個中斷
我們的程序運行到while(1)的時候就進入我們設(shè)計的死循環(huán),在這個死循環(huán)中有兩個任務(wù),第一個是判斷Timer0有沒有溢出以及如果溢出后的操作,第二個是判斷定時器溢出次數(shù)是否累計達到我們的需求。
這個代碼中,只有執(zhí)行到if(TF0==0)的時候才會執(zhí)行判斷定時器是否溢出,倘若我們的程序執(zhí)行到if(Counter==256)的代碼的時候定時器已經(jīng)溢出,那么機器也會繼續(xù)執(zhí)行完這個if的語句,直到再次執(zhí)行到if(TF0==0)才會對timer溢出做出處理。顯然不是定時器中斷
這種使用定時器的優(yōu)點是代碼簡單,而且應對簡單的定時器任務(wù)的時候可以簡潔的代碼完成任務(wù)(比如說一秒點亮一個led燈),但是應對稍微復雜的任務(wù)(例如做一個單片機電子時鐘,只有執(zhí)行到判斷Timer溢出的代碼才做出判斷,這樣就可能發(fā)生定時不準確的問題了)
至此,希望你跟我一樣,也明白了定時器和中斷不是一回事,也希望大家不要把定時器和中斷搞混
2 中斷使用第一步, 中斷的分類,認識中斷源
C51中的中斷,可以這樣分類
中斷符號 | 中斷名稱 | 作用 |
---|---|---|
IE0 | 外部中斷0 | 對應外部中斷INT0 |
IE1 | 外部中斷1 | 對應外部中斷INT1 |
T0 | Timer0中斷 | 定時器溢出后觸發(fā)的中斷 |
T1 | Timer1中斷 | |
T2 | Timer2中斷 | |
ES | uart串口中斷 | 串口中斷 |
不難發(fā)現(xiàn),按照功能分,一共有三類:
Timer中斷,當定時器溢出的時候觸發(fā)這個中斷
外部中斷,C51提供兩組外部中斷,當外部中斷引腳的電平變化的時候觸發(fā)中斷
分別是外部中斷0,對應INT0,在P3.2引腳
分別是外部中斷0,對應INT1,在P3.3引腳
串口中斷
串口中斷等到我們使用串口的時候再介紹,這里先放一放。
在單片機中,凡是能觸發(fā)中斷信號就叫中斷源,以上的定時器,外部中斷,串口都是中斷源。
換句話說,就是這些信號觸發(fā)了單片機的中斷。
3 中斷使用第二步,打開中斷
中斷使能寄存器,地址0xA8,可位尋址
位 | 符號 | 復位值 | 功能說明 | 操作說明 |
---|---|---|---|---|
7 | EA | 0 | 總中斷使能開關(guān) | EA = 1使能中斷總開關(guān) |
6 | - | 0 | - | - |
5 | ET2 | 0 | Timer2中斷使能開關(guān) | 寫1的時候啟用中斷,寫0的時候不使用中斷 |
4 | ES | 0 | 串口中斷使能 | |
3 | ET1 | 0 | Timer1中斷使能 | |
2 | EX1 | 0 | 外部中斷INT1使能 | |
1 | ET0 | 0 | Timer0中斷使能 | |
0 | EX0 | 0 | 外部中斷INT0使能 |
下面說明一下,假設(shè)我們要使用Timer0中斷,需要這樣操作:
EA = 1;//中斷總使能開關(guān)
//現(xiàn)在先對中斷使能,確保我們能使用中斷
ET0 = 1;//使能timer0中斷
4 中斷使用第三步,認識外部中斷
前面我們知道了,C51的外部中斷INT0,INT1和串口中斷ES分別在P3.2,P3.3,P3.1。這里我們先不介紹串口中斷,我們介紹兩個外部中斷INT0和INT1,串口中斷我們學串口的時候再研究。
還記得我們之前學習定時器的時候提到的TCON寄存器嗎
TCON寄存器
位 | 符號 | 功能描述 | 操作 | 復位值 |
---|---|---|---|---|
7 | TF1 | Timer1溢出檢測 | 定時器中斷標志 | 0 |
6 | TR1 | Timer1使能 | TR1=1開啟定時器,寫0關(guān)閉定時器 | 0 |
5 | TF0 | Timer0溢出檢測 | 定時器中斷標志 | 0 |
4 | TR0 | Timer0使能 | TR0=1開啟定時器,寫0關(guān)閉定時器 | 0 |
3 | IE1 | INT1外部中斷請求標志 | 請求標識,值為1的時候表明中斷向機器請求中斷。機器響應后,自動寫0 | 0 |
2 | IT1 | INT1中斷觸發(fā)設(shè)置 | 外部中斷觸發(fā)模式設(shè)置 | 0 |
1 | IE0 | INT0外部中斷請求標志 | 請求標識,值為1的時候表明中斷向機器請求中斷。機器響應后,自動寫0 | 0 |
0 | IT0 | INT0中斷觸發(fā)設(shè)置 | 外部中斷觸發(fā)模式設(shè)置 | 0 |
上次我們沒有介紹低四位的功能,這回我們詳細的介紹一下:
首先,IE1,IT1屬于外部中斷1,也就是外部中斷INT1。IE0,IT0屬于外部中斷0,也就是外部中斷INT0。
相信這點不難理解,接下來介紹IE,IT的功能
IE是外部中斷請求標識,當外部中斷被觸發(fā)的時候,IE會變成1,表示外部中斷在向CPU請求中斷。當CPU響應這個外部中斷的時候,IE會自動變成0。所以,IE就是用于中斷請求的標識,一般使用的時候我們不用操作這一位寄存器
IT是中斷觸發(fā)方式,一共有兩種觸發(fā)方式
觸發(fā)方式 | 說明 | IT設(shè)置值 |
---|---|---|
低電平觸發(fā) | 當外部中斷引腳電平為低電平的時候觸發(fā) | 0 |
負跳變觸發(fā) | 當引腳電平由高電平轉(zhuǎn)換到低電平的這個過程中觸發(fā)(保持在高電平或者低電平都不會觸發(fā)) | 1 |
這里講一下什么叫負跳變,假設(shè)現(xiàn)在中斷引腳電平為高電平,外部電路變化讓外部中斷觸發(fā)引腳從高電平變化成低電平,這個時候外部中斷引腳的電平變化是1—>0,這就是個負跳變的變化過程。就在這個過程中,觸發(fā)了中斷。1—>0的負跳變過程后,單片機外部中斷IO但凡是保持在1或者保持在0都不會觸發(fā)中斷,只有再次發(fā)生電平1—>0的變化才會再次觸發(fā)外部中斷
而低電平觸發(fā),只要中斷引腳的電平為低電平就能直接觸發(fā)中斷。
靈活使用外部中斷IT模式的設(shè)置,配合我們的程序,玩出新花樣!?。?/p>
這里不得不聲明一下,之前我們學習定時器的時候,我們總是會寫這樣一行代碼
這個代碼之前我們說是判斷定時器是否溢出的標志,溢出后需要我們手動清零
事實上,當我們使用定時器的時候,就已經(jīng)不再需要我們手動清零了,如果我們使能定時器的中斷,這里的TF就相當于IE一樣的中斷請求信號,當CPU接受請求(也就是CPU處理中斷函數(shù)之后,這個TF就自動清零了)
所以我們使用定時器中斷的時候就不必要使用
來進行手動清零了。
5 中斷使用第四步,中斷索引和中斷函數(shù)的使用
前面我們學習了中斷源和中斷觸發(fā)的配置。
中斷觸發(fā)了,我們總要讓單片機做一點事情。單片機依靠中斷索引知道是哪個中斷觸發(fā)了,并且找到中斷函數(shù),執(zhí)行中斷函數(shù)的內(nèi)容。
先來看中斷函數(shù)的格式,這個是一個典型的中斷函數(shù)
void Timer1Interrupt() interrupt 1
{
counter++;
}
void 【中斷函數(shù)名字】() interrupt 【中斷索引編號】
{
counter++;
}
【中斷函數(shù)名字】可以是任何符合函數(shù)名規(guī)則的函數(shù)名
但是括號后的關(guān)鍵字“interrupt”一定不能寫錯,這個是中斷函數(shù)的關(guān)鍵字,關(guān)鍵字后面的數(shù)字是中斷索引
中斷索引,單片機靠這個找到中斷
索引 | 中斷名稱 | 符號 | 功能 |
---|---|---|---|
0 | 外部中斷0 | IE0 | 外部中斷引腳INT0電平變化符合我們設(shè)置觸發(fā)的類型的時候觸發(fā)外部中斷 |
1 | Timer0中斷 | T0 | Timer0溢出的時候觸發(fā)中斷 |
2 | 外部中斷1 | IE1 | 外部中斷引腳INT1電平變化符合我們設(shè)置觸發(fā)的類型的時候觸發(fā)外部中斷 |
3 | Timer1中斷 | T1 | Timer1溢出的時候觸發(fā)中斷 |
4 | UART | ES | 串口中斷 |
5 | Timer2 | T2 | Timer2溢出的時候觸發(fā)中斷 |
單片機靠這個索引,知道是哪個中斷被觸發(fā)了,并且找到這個中斷觸發(fā)要執(zhí)行的對應函數(shù)上
小總結(jié)(一)中斷的初始化
到這里,我們來簡單總結(jié),對中斷進行初始化,中斷初始化在main函數(shù)中完成,值得一提,必須放在main函數(shù)的死循環(huán)前面
中斷使能寄存器,0xA8,可位尋址
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符 | EA | - | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
功能 | 中斷總開關(guān) | - | Timer2中斷使能開關(guān) | UART串口中斷使能開關(guān) | Timer1中斷使能開關(guān) | 外部中斷1中斷使能開關(guān) | Timer0中斷使能開關(guān) | 外部中斷0中斷使能開關(guān) |
操作 | 1:打開 | - | 1:打開,0:關(guān)閉 |
中斷控制寄存器,0x88,可位尋址
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符 | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE 0 | IT0 |
功能 | 檢測/復位定時1器溢出 | 使能定時器1 | 檢測/復位定時器0溢出 | 使能定時器0 | 外部中斷1請求信號 | 外部中斷1觸發(fā)信號設(shè)置 | 外部中斷0請求信號 | 外部中斷觸0發(fā)信號設(shè)置 |
操作 | 定時器中斷信號 | TR0 = 1使能定時器 | 定時器中斷信號 | TR1 = 1使能定時器 | 自動處理,無需干預 | 設(shè)置觸發(fā)模式 | 自動處理,無需干預 | 設(shè)置觸發(fā)模式 |
這里還是再次說明一次
IE是中斷請求觸發(fā)器,當中斷觸發(fā)的時候自動寫1,CPU請求后自動寫0
IT設(shè)置中斷觸發(fā)模式
IT=1 | 負跳變模式,電平由1—>0觸發(fā) |
---|---|
IT=0 | 低電平觸發(fā)模式,低電平的時候自動觸發(fā) |
我們使用了timer的中斷,這時候TF相當于Timer的中斷請求位,cpu響應中斷后就自動清零,所以我們就不需要在中斷函數(shù)里面對TF進行干預
小總結(jié)(二)中斷索引與中斷函數(shù)
索引 | 中斷符號 | 說明 |
---|---|---|
0 | IE0 | 外部中斷引腳INT0電平變化符合我們設(shè)置觸發(fā)的類型的時候觸發(fā)外部中斷 |
1 | T0 | Timer0溢出的時候觸發(fā)中斷 |
2 | IE1 | 外部中斷引腳INT1電平變化符合我們設(shè)置觸發(fā)的類型的時候觸發(fā)外部中斷 |
3 | T1 | Timer1溢出的時候觸發(fā)中斷 |
4 | ES | 串口中斷 |
5 | T2 | Timer2溢出的時候觸發(fā)中斷 |
中斷函數(shù)格式
void [中斷函數(shù)名] () interrupt [中斷索引編號]
{
}
這里再次說明,中斷函數(shù)寫在main函數(shù)后面
[中斷函數(shù)名]可以是任何一個符合函數(shù)名規(guī)則的函數(shù)名
interrupt是中斷函數(shù)的關(guān)鍵字,必須寫對
[中斷索引編號參考以上的編號]
實踐(一)timer中斷
下面我們就來實踐一下吧
這個程序里面對定時器Timer0初始化(設(shè)置一個基本的16位定時器),并且設(shè)置定時器中斷
#include< reg51.h >
void main ()
{
EA = 1;//【第一步】總中斷開關(guān)使能
TMOD = 0x01;//【第二步】設(shè)置定時器T0的模式為標準的16位定時器
TH0 = 0x00;//【第三步】設(shè)置定時器初始值
TL0 = 0xEE;
ET0 = 1;//【第四步】定時器Timer0中斷使能
TR0 = 1;//【第五步】激活定時器
while(1)
{
}
}
void Tmr0Int () interrupt 1
{
TH0 = 0x00;
TL0 = 0xFF;//只需要重新設(shè)置初始值,不需要復位TF
}
分析一下代碼,我們很容易的發(fā)現(xiàn),新的代碼里面多了兩個對中斷的操作:
EA = 1;//【第一步】總中斷開關(guān)使能
ET0 = 1;//【第四步】定時器Timer0中斷使能
EA=1使能總中斷開關(guān)
ET0=1打開Timer0中斷開關(guān)
還有一些注意事項:
定時器中斷設(shè)置的順序
EA=1 > TMOD設(shè)置 > 定時器初始值設(shè)置 > ET=1使能定時器中斷 > TR=1使能定時器
初始化在main函數(shù)的死循環(huán)前
中斷函數(shù)一般寫在main函數(shù)后面
我們使用了timer的中斷,這時候TF相當于Timer的中斷請求位,cpu響應中斷后就自動清零,所以我們就不需要在中斷函數(shù)里面對TF進行干預
使用16位定時器中斷,模式1,定時器溢出中斷后我們?nèi)匀恍枰獙Χ〞r器初始值進行干預
實踐(二)timer中斷 ****
下面我們就來實踐一下吧
這里我們要用上外部中斷0,設(shè)置外部中斷觸發(fā)的方式是低電平觸發(fā)
以下是代碼
#include< reg51.h >
void main ()
{
EA = 1;//【第一步】總中斷開關(guān)使能
IT0 = 0;//【第二步】設(shè)置外部中斷模式,低電平觸發(fā)
EX0 = 1;//【第三步】打開外部中斷0的使能開關(guān)
while(1)
{
}
}
void ExInit0 () interrupt 0
{
}
不難發(fā)現(xiàn),這段代碼比設(shè)置定時器中斷的代碼更簡單,不過仍然有兩個地方需要注意:
第一個當然是初始化操作的順序
第二個是注意中斷函數(shù)的索引編號絕對不能錯
好了,介紹就到這里了,希望能幫助你更好的了解單片機,如果您覺得還不錯的話歡迎關(guān)注我
-
單片機
+關(guān)注
關(guān)注
6037文章
44561瀏覽量
635637 -
中斷
+關(guān)注
關(guān)注
5文章
898瀏覽量
41515 -
定時器
+關(guān)注
關(guān)注
23文章
3250瀏覽量
114878 -
C51單片機
+關(guān)注
關(guān)注
12文章
164瀏覽量
34783
發(fā)布評論請先 登錄
相關(guān)推薦
評論