單片機定時器中斷是我們經常都需要用的,下面將以51單片機為例子來說明單片機定時器中斷原理。
80C51的定時/計數器的結構
定時/計數器的實質是加1計數器(16位),由高8位和低8位兩個寄存器組成。TMOD是定時/計數器的工作方式寄存器,確定工作方式和功能;TCON是控制寄存器,控制T0、T1的啟動和停止及設置溢出標志。
中斷系統介紹
中斷系統是一套硬件電路,它可以在每個機器周期對所有的外設的標志位作查詢。相比于前面的軟件查詢(if(xx==1)),中斷系統也可以叫做硬件查詢。51的中斷系統可查詢以下6個標志位。
IE0(TCON.1),外部中斷0中斷請求標志位。
IT1(TCON.2),外部中斷1觸發方式控制位。
IE1(TCON.3),外部中斷1中斷請求標志位。
TF0(TCON.5),定時/計數器T0溢出中斷請求標志位。
TF1(TCON.7),定時/計數器T1溢出中斷請求標志位。
RI(SCON.0)或TI(SCON.1),串行口中斷請求標志。當串行口接收完一幀串行數據時置位RI或當串行口發送完一幀串行數據時置位TI,向CPU申請中斷。
當中斷系統查詢到外設的標志位變為1時,中斷系統可暫停當前的主循環,并且將程序跳轉到用戶預先指定的函數中執行。要啟動中斷系統,必須先進行中斷初始化,其流程如下:
a、是否要查詢外設標志(EA=0或EA=1,EA 也叫 CPU中斷允許(總允許)位)
b、查詢到標志1,是否要跳程序
c、跳轉的目標函數,即中斷服務子函數
所以在使用定時器中斷時,我們只需要首先初始化中斷系統,開啟總中斷(相當于總開關),開啟定時器對應的控制位(相當于支路開關),再初始化定時器即可。中斷系統作為單片機的外設,只有在某個中斷產生時才會打斷主循環,并由相應的中斷號引入到相應的中斷服務子函數。下圖是6個中斷標志位的信息。
? ?80C51單片機定時器中斷原理
這里將涉及到單片機中斷的應用,在cpu的一步步按照指令運行的過程中(主程序),可能會有其它的更緊急的需要做的事情(中斷服務程序),需要cpu暫時停止當前的程序(主程序),做完了(中斷服務程序)之后,又可以繼續去運行先前的程序(主程序)。就像你正在吃飯,一邊又在給水桶里放水,吃著吃著,水滿了,你就得趕快去把水龍頭關掉或者換一個空的水桶,再回來吃飯。
單片機的定時器就像是一個水桶,你讓它啟動了,也就是水龍頭打開了;開始裝水了;定時在每個機器周期不斷自動加1,最后溢出了;水桶的水不斷增加,最也就滿出來了;定時器溢出時,你就要去做處理了;水桶的水滿了,你也應該處理一下了;處理完后,單片機又可以回到剛剛開停止的地方繼續運行;水桶處理了,先前你在做什么也可以繼續去做什么了。
單片機的主程序是從0x0000開始運行的,單片機服務程序從哪里開始運行呢?在51里,有多個中斷服務程序入口,0號入口是外中斷0,地址在0x0003;1號入口是定時器0,在 0x000B;2號入口是外中斷1;地址在0x0013,3號入口是定時器2;地址在0x001B,等等。當中斷發生時,程序就記下當前運行的位置,跳到對應的中斷入口去運行中斷服務程序,運行完之后,又跳回到原來的位置繼續運行。
在C51中,你不用理會中斷服務程序放在哪里,會怎么跳轉。你只要把某個函數標識為幾號中斷服務函數就可以了。在發生了對應的中斷時,就會自動的運行這個函數。
定時/計數器的工作原理
加1計數器輸入的計數脈沖有兩個來源,一個是由系統的時鐘振蕩器輸出脈沖經12分頻后送來;一個是T0或T1引腳輸入的外部脈沖源。每來一個脈沖計數器加1,當加到計數器為全1時,再輸入一個脈沖就使計數器回零,且計數器的溢出使TCON中TF0或TF1置1,向CPU發出中斷請求(定時/計數器中斷允許時)。如果定時/計數器工作于定時模式,則表示定時時間已到;如果工作于計數模式,則表示計數值已滿。
可見,由溢出時計數器的值減去計數初值才是加1計數器的計數值。
設置為定時器模式時,加1計數器是對內部機器周期計數(1個機器周期等于12個振蕩周期,即計數頻率為晶振頻率的1/12)。計數值N乘以機器周期Tcy就是定時時間t 。
設置為計數器模式時,外部事件計數脈沖由T0或T1引腳輸入到計數器。在每個機器周期的S5P2期間采樣T0、T1引腳電平。當某周期采樣到一高電平輸入,而下一周期又采樣到一低電平時,則計數器加1,更新的計數值在下一個機器周期的S3P1期間裝入計數器。由于檢測一個從1到0的下降沿需要2個機器周期,因此要求被采樣的電平至少要維持一個機器周期。當晶振頻率為12MHz時,最高計數頻率不超過1/2MHz,即計數脈沖的周期要大于2 ?Us。

#define _1231_C_
#include “reg51.h”
//sbit OE=P2^3;
unsigned int SystemTime;
void timer0(void) interrupt 1 using 3 //中斷部分代碼,見下文的釋疑
{
TH0 = 0xdb;
TL0 = 0xff;
// TF0 = 0;
SystemTime++;
}
void main()
{
TMOD &= 0xF0;
TMOD |= 0x01; //TMOD的值表示定時器工作方式選擇
TH0 = 0xdb; //寫入初始值,初始值可以決定定時多久
TL0 = 0xff;
//根據上文的木桶比喻的話,如果TH0 = 0x00;TL0 = 0x00;則表示從桶底開始裝水。
//TH0 = 0xdb;TL0 = 0xff;可以這樣子理解相當于木桶里已經有部分液鉛在里面,
//TH0和TL0這個兩個值表示木桶里液鉛的高度,即此時桶里只能從液鉛的高度以上開始裝水,
//TH0 = 0xff;TL0 = 0xff;即表示桶的最高位置。
TF0 = 0; //計數到時TF0為1,即當TH0 = 0xff;TL0 = 0xff;再運行一步TF0 = 1;
TR0 = 1; //開始計數,從這時起,每運行一步TH0和TL0都會增加,直到TH0 = 0xff;TL0 = 0xff;
//相當于開水龍頭,如TR0=0則TH0和TL0不變
ET0 = 1; //允許定時器0中斷
EA=1; //開總中斷
//下面是個死循環,程序里每運行一步TH0和TL0都會增加,當增加到TH0 = 0xff;TL0 = 0xff;
//單片機會從死循環里退出,去執行中斷部分的代碼,即開始運行void timer0(void) interrupt 1 using 3{}
//運行完中斷部分的代碼后,接著繼續執行死循環里的代碼。
//注意:當TH0 = 0xff;TL0 = 0xff;再運行,TF0并沒有從0變為1,個人猜測TF0=1;時觸發了中斷,并重新被置零。
//如把ET0 = 1;和EA=1;注釋掉,當TH0 = 0xff;TL0 = 0xff;再運行,TF0會變為1,此時不會再執行中斷部分代碼。
while(1)
{
if ((SystemTime%100)《50) //SystemTime除以100,余數小于50為真
{
…………;
}
else
{
…………;
}
};
}
釋疑:void Timer0() interrupt 1 using 1
Timer0 是函數名,隨便取的
interrupt xx using y
跟在interrupt 后面的xx 值得是中斷號,就是說這個函數對應第幾個中斷端口,一般在51中
0 外部中斷0
1 定時器0
2 外部中斷1
3 定時器1
4 串行中斷
實際上編譯的時候就是把你這個函數的入口地址方到這個對應中斷的跳轉地址
using y 這個y是說這個中斷函數使用的那個寄存器組,51里面一般有4組 r0 -- r7寄存器,一共有32個,如果你的終端函數和別的程序用的不是同一個寄存器組則進入中斷的時候就不會將寄存器組壓入堆棧返回時也不會談出來節省代碼和時間
初始值算法:定時器是當總數達到FFFFH后產生中斷吧!那你要讓它計數10000,是不是用FFFF(16進制)減去10000(十進制)的數當計數初值 啊?TH0=-(10000/256); TL0=-(10000%256)跟FFFF(16進制)減去10000(十進制)的數是一樣的。從TH0=-(10000/256); TL0=-(10000%256)開始計數,計數到10000剛好滿。跟用FFFF(16進制)減去10000(十進制)的數一樣!!!寫起來更簡單,不 用算!!!
看看原碼、補碼就知道。正數的補碼是對應的二進制數,符號位為零,負數的補碼是它的絕對值對應的二進制數按位取反再加一,符號位為一。無符號數不考慮符號,那么這個結果就跟用FFFF減去它的絕對值一樣。
評論