前排提示
以下定時器的設置例程是以12MHz頻率運行為模板的
STC15F2K60S2單片機內部晶振頻率設置方法如下:
什么是中斷
首先,我們先來模擬一個情景:
在某天的晚上,你正在為今晚截止上交的作業絞盡腦汁,同時你還有一桶的衣服沒洗(嗯,假設是個懶癌,假設)**。但有一個好消息,洗衣機還有5分鐘就洗完了上一位同學的衣服,如果你足夠及時的話就能夠用上洗衣機。因此,你用手機設了一個5分鐘后的提醒,時間一到你就暫停做作業,并馬上去使用洗衣機洗衣服,完成這項任務后回去繼續寫作業。
分析一下上面的情景,我們可以得到以下信息:
1、你現在主要的任務是完成作業,而且現在就需要進行;
2、你需要洗你的衣服,但不是現在;
3、在洗衣機前一直等待的話,將阻礙你其他任務的進行;
4、你設置了一個定時器,會提醒你進行另一項任務。
得到這些信息,對于定時器中斷我們就很好理解了。
將上面的情景套到單片機中的話:
1、主函數(main)是你現在正在做的事(做作業);
2、中斷函數是你另外需要做的事(洗衣服)但不是現在需要做的;
3、主函數需要一直執行,而且無法與其他函數同時執行(只有一個線程);
4、定時器中斷可以暫停主函數的執行,并進入中斷函數,待中斷函數完成后就回到主函數,從暫停的位置繼續執行。
如果你理解了什么是中斷,那么請接著往下看。
怎樣使用中斷
使用定時器中斷分為兩部分:打開定時器中斷與編寫中斷函數,下面以定時器0為例,講解如何編寫和使用。
打開定時器中斷
打開定時器中斷分為以下三個步驟:
1、打開定時器;
2、設置定時器工作模式;
3、設置定時時長(設置初值)。
下面是設置定時50ms的例程:
void Timer0_Start() //中斷設置函數
{
EA = 1;
ET0 = 1;
TMOD |= 0x09;
TR0 = 1;
TH0 = (65536-50000)/256;
TL0 = (65536-50000)%256;
}
我們按上面所隔開的三部分,來逐句分析上面的程序:
- 第一步,打開定時器:
EA:中斷的總開關,置1使中斷可用;
ET0:定時器0的開關,置1使定時器0可用。
我們可以這樣理解,EA家里的總電閘,ET0是家里其中一盞電燈的開關,只有打開了EA,ET0才能用;只用ET0打開了,定時器0才能用。
- 第二步,設置定時器的工作模式:
TMOD:定時器共有4種工作模式(模式0~模式3),其中模式1較為常用,具體的設置方法以后更新,本文只講較常用的模式。
TR0:控制TR0的運行,置1時定時器開始運行(ET0是使定時器0能用,并不代表著定時器0已經開始工作)。
- 第三步,設置定時器初值:
TH0:相當于分,計滿(65536)時進入中斷;
TL0:相當于秒,計滿(65536)時分(TH0)加一。
如果不設置TH0和TL0,則默認從0開始計。
其中,50000為計時時長(50000微妙,即50毫秒)具體的為啥醬紫設置將在下篇細講。
編寫中斷函數
中斷函數的編寫與一般子函數的編寫差別不大:
void xxx() interrupt 1 //中斷函數
{
TH0 = (65536-50000)/256;
TL0 = (65536-50000)%256;
//balabalabala...
}
函數名同樣是可以自定義的,不過中斷函數并沒有返回值與參數。
函數名后面的interrupt關鍵字可以看成中斷函數的標志,帶這東西的就是中斷函數。
進入中斷后,首先要做的就是將TH0、TL0恢復初值(如果這個中斷只執行一次,那么可以忽略這個步驟)。
原來定時的時長已經完成了,如果不重新設定時間,那么就不會再繼續定時。
interrupt后面接的數字是中斷源的序號:
- 1 為定時器0;*
- 3 為定時器1;
- 有的單片機還有定時器2,定時器2的序號為5。*
中間其他數是其他中斷的序號。
要注意的是,中斷是有優先級的,序號越高則優先級越高。例如,定時器0與定時器1同時發生了中斷,則優先執行定時器0的中斷函數,執行完后再執行定時器1的中斷函數,最后回到主函數。
注意:
上面所使用的ET0、TR0、TH0、TL0中,最后的數字代表著定時器0,
如果使用定時器1,相應的就改成ET1、TR1、TH1、TL1。
寫個程序試試看
上面就是定時器中斷的設置與使用了,我們來試試用定時器做一個LED閃爍的小程序吧。
例程:
#include < reg52.h >
#define TIME_US 50000 //中斷間隔,50000即每隔50ms發生一次中斷
#define LED P1 //LED與單片機所連接的引腳
int count = 0; //用于記錄進入中斷次數的全局變量
void Timer0_Start() //中斷設置函數
{
EA = 1;
ET0 = 1;
TMOD |= 0x09;
TR0 = 1;
TH0 = (65536-TIME_US)/256;
TL0 = (65536-TIME_US)%256;
}
void main()
{
Timer0_Start(); //調用中斷設置函數,若不調用則與沒寫沒有區別
LED = 0x00; //設置LED的初值
while(1); //死循環,讓程序在此停止,否則將一直循環令LED為0x00
}
void Timer0() interrupt 1 //中斷函數
{
TH0 = (65536-TIME_US)/256;
TL0 = (65536-TIME_US)%256;
count++; //進入了一次中斷,變量count加1
if(count == 20) //如果進入了20次中斷,即過了1秒(50ms*20=1s),
{
count = 0; //將count歸0,下次中斷開始重新計數
LED = ~LED; //將LED進行取反,原來亮的就變滅,原來滅的就變亮
}
}
(程序下載效果,LED亮一秒滅一秒)
在使用中斷時,有幾個需要注意的地方:
1、中斷設置函數( Timer0_Start() )只需要進行一次,若循環進行則相當于一直給TH、TL賦值,TH、TL不能計滿溢出則無法進入中斷。
2、中斷里用來計數的變量(count)不能在中斷里進行定義(例如把int count寫在中斷函數里),因為每次退出中斷后里面的局部變量都會被清除,這個變量也就無法用于計數。
使用中斷的好處是,你可以在上面主函數的while循環里執行其他程序,且不影響LED閃爍的執行,可以完成多項任務(例如動態數碼管與LED閃爍同時工作)。
相比之下,使用軟件延時(delay)的話,要完成像上面這樣的多任務執行可能就要寫比較復雜的程序了,因為在軟件延時的期間單片機是無法執行其他程序的。
-
單片機
+關注
關注
6035文章
44554瀏覽量
634663 -
LED閃爍
+關注
關注
0文章
29瀏覽量
9822 -
定時器中斷
+關注
關注
0文章
49瀏覽量
11167 -
動態數碼管
+關注
關注
1文章
11瀏覽量
7473 -
中斷函數
+關注
關注
0文章
13瀏覽量
5282
發布評論請先 登錄
相關推薦
評論