單片機(jī)定時(shí)器中斷是我們經(jīng)常都需要用的,下面將以51單片機(jī)為例子來(lái)說(shuō)明單片機(jī)定時(shí)器中斷原理。
80C51的定時(shí)/計(jì)數(shù)器的結(jié)構(gòu)
定時(shí)/計(jì)數(shù)器的實(shí)質(zhì)是加1計(jì)數(shù)器(16位),由高8位和低8位兩個(gè)寄存器組成。TMOD是定時(shí)/計(jì)數(shù)器的工作方式寄存器,確定工作方式和功能;TCON是控制寄存器,控制T0、T1的啟動(dòng)和停止及設(shè)置溢出標(biāo)志。
中斷系統(tǒng)介紹
中斷系統(tǒng)是一套硬件電路,它可以在每個(gè)機(jī)器周期對(duì)所有的外設(shè)的標(biāo)志位作查詢。相比于前面的軟件查詢(if(xx==1)),中斷系統(tǒng)也可以叫做硬件查詢。51的中斷系統(tǒng)可查詢以下6個(gè)標(biāo)志位。
IE0(TCON.1),外部中斷0中斷請(qǐng)求標(biāo)志位。
IT1(TCON.2),外部中斷1觸發(fā)方式控制位。
IE1(TCON.3),外部中斷1中斷請(qǐng)求標(biāo)志位。
TF0(TCON.5),定時(shí)/計(jì)數(shù)器T0溢出中斷請(qǐng)求標(biāo)志位。
TF1(TCON.7),定時(shí)/計(jì)數(shù)器T1溢出中斷請(qǐng)求標(biāo)志位。
RI(SCON.0)或TI(SCON.1),串行口中斷請(qǐng)求標(biāo)志。當(dāng)串行口接收完一幀串行數(shù)據(jù)時(shí)置位RI或當(dāng)串行口發(fā)送完一幀串行數(shù)據(jù)時(shí)置位TI,向CPU申請(qǐng)中斷。
當(dāng)中斷系統(tǒng)查詢到外設(shè)的標(biāo)志位變?yōu)?時(shí),中斷系統(tǒng)可暫停當(dāng)前的主循環(huán),并且將程序跳轉(zhuǎn)到用戶預(yù)先指定的函數(shù)中執(zhí)行。要啟動(dòng)中斷系統(tǒng),必須先進(jìn)行中斷初始化,其流程如下:
a、是否要查詢外設(shè)標(biāo)志(EA=0或EA=1,EA 也叫 CPU中斷允許(總允許)位)
b、查詢到標(biāo)志1,是否要跳程序
c、跳轉(zhuǎn)的目標(biāo)函數(shù),即中斷服務(wù)子函數(shù)
所以在使用定時(shí)器中斷時(shí),我們只需要首先初始化中斷系統(tǒng),開(kāi)啟總中斷(相當(dāng)于總開(kāi)關(guān)),開(kāi)啟定時(shí)器對(duì)應(yīng)的控制位(相當(dāng)于支路開(kāi)關(guān)),再初始化定時(shí)器即可。中斷系統(tǒng)作為單片機(jī)的外設(shè),只有在某個(gè)中斷產(chǎn)生時(shí)才會(huì)打斷主循環(huán),并由相應(yīng)的中斷號(hào)引入到相應(yīng)的中斷服務(wù)子函數(shù)。下圖是6個(gè)中斷標(biāo)志位的信息。
? ?80C51單片機(jī)定時(shí)器中斷原理
這里將涉及到單片機(jī)中斷的應(yīng)用,在cpu的一步步按照指令運(yùn)行的過(guò)程中(主程序),可能會(huì)有其它的更緊急的需要做的事情(中斷服務(wù)程序),需要cpu暫時(shí)停止當(dāng)前的程序(主程序),做完了(中斷服務(wù)程序)之后,又可以繼續(xù)去運(yùn)行先前的程序(主程序)。就像你正在吃飯,一邊又在給水桶里放水,吃著吃著,水滿了,你就得趕快去把水龍頭關(guān)掉或者換一個(gè)空的水桶,再回來(lái)吃飯。
單片機(jī)的定時(shí)器就像是一個(gè)水桶,你讓它啟動(dòng)了,也就是水龍頭打開(kāi)了;開(kāi)始裝水了;定時(shí)在每個(gè)機(jī)器周期不斷自動(dòng)加1,最后溢出了;水桶的水不斷增加,最也就滿出來(lái)了;定時(shí)器溢出時(shí),你就要去做處理了;水桶的水滿了,你也應(yīng)該處理一下了;處理完后,單片機(jī)又可以回到剛剛開(kāi)停止的地方繼續(xù)運(yùn)行;水桶處理了,先前你在做什么也可以繼續(xù)去做什么了。
單片機(jī)的主程序是從0x0000開(kāi)始運(yùn)行的,單片機(jī)服務(wù)程序從哪里開(kāi)始運(yùn)行呢?在51里,有多個(gè)中斷服務(wù)程序入口,0號(hào)入口是外中斷0,地址在0x0003;1號(hào)入口是定時(shí)器0,在 0x000B;2號(hào)入口是外中斷1;地址在0x0013,3號(hào)入口是定時(shí)器2;地址在0x001B,等等。當(dāng)中斷發(fā)生時(shí),程序就記下當(dāng)前運(yùn)行的位置,跳到對(duì)應(yīng)的中斷入口去運(yùn)行中斷服務(wù)程序,運(yùn)行完之后,又跳回到原來(lái)的位置繼續(xù)運(yùn)行。
在C51中,你不用理會(huì)中斷服務(wù)程序放在哪里,會(huì)怎么跳轉(zhuǎn)。你只要把某個(gè)函數(shù)標(biāo)識(shí)為幾號(hào)中斷服務(wù)函數(shù)就可以了。在發(fā)生了對(duì)應(yīng)的中斷時(shí),就會(huì)自動(dòng)的運(yùn)行這個(gè)函數(shù)。
定時(shí)/計(jì)數(shù)器的工作原理
加1計(jì)數(shù)器輸入的計(jì)數(shù)脈沖有兩個(gè)來(lái)源,一個(gè)是由系統(tǒng)的時(shí)鐘振蕩器輸出脈沖經(jīng)12分頻后送來(lái);一個(gè)是T0或T1引腳輸入的外部脈沖源。每來(lái)一個(gè)脈沖計(jì)數(shù)器加1,當(dāng)加到計(jì)數(shù)器為全1時(shí),再輸入一個(gè)脈沖就使計(jì)數(shù)器回零,且計(jì)數(shù)器的溢出使TCON中TF0或TF1置1,向CPU發(fā)出中斷請(qǐng)求(定時(shí)/計(jì)數(shù)器中斷允許時(shí))。如果定時(shí)/計(jì)數(shù)器工作于定時(shí)模式,則表示定時(shí)時(shí)間已到;如果工作于計(jì)數(shù)模式,則表示計(jì)數(shù)值已滿。
可見(jiàn),由溢出時(shí)計(jì)數(shù)器的值減去計(jì)數(shù)初值才是加1計(jì)數(shù)器的計(jì)數(shù)值。
設(shè)置為定時(shí)器模式時(shí),加1計(jì)數(shù)器是對(duì)內(nèi)部機(jī)器周期計(jì)數(shù)(1個(gè)機(jī)器周期等于12個(gè)振蕩周期,即計(jì)數(shù)頻率為晶振頻率的1/12)。計(jì)數(shù)值N乘以機(jī)器周期Tcy就是定時(shí)時(shí)間t 。
設(shè)置為計(jì)數(shù)器模式時(shí),外部事件計(jì)數(shù)脈沖由T0或T1引腳輸入到計(jì)數(shù)器。在每個(gè)機(jī)器周期的S5P2期間采樣T0、T1引腳電平。當(dāng)某周期采樣到一高電平輸入,而下一周期又采樣到一低電平時(shí),則計(jì)數(shù)器加1,更新的計(jì)數(shù)值在下一個(gè)機(jī)器周期的S3P1期間裝入計(jì)數(shù)器。由于檢測(cè)一個(gè)從1到0的下降沿需要2個(gè)機(jī)器周期,因此要求被采樣的電平至少要維持一個(gè)機(jī)器周期。當(dāng)晶振頻率為12MHz時(shí),最高計(jì)數(shù)頻率不超過(guò)1/2MHz,即計(jì)數(shù)脈沖的周期要大于2 ?Us。
#define _1231_C_
#include “reg51.h”
//sbit OE=P2^3;
unsigned int SystemTime;
void timer0(void) interrupt 1 using 3 //中斷部分代碼,見(jiàn)下文的釋疑
{
TH0 = 0xdb;
TL0 = 0xff;
// TF0 = 0;
SystemTime++;
}
void main()
{
TMOD &= 0xF0;
TMOD |= 0x01; //TMOD的值表示定時(shí)器工作方式選擇
TH0 = 0xdb; //寫(xiě)入初始值,初始值可以決定定時(shí)多久
TL0 = 0xff;
//根據(jù)上文的木桶比喻的話,如果TH0 = 0x00;TL0 = 0x00;則表示從桶底開(kāi)始裝水。
//TH0 = 0xdb;TL0 = 0xff;可以這樣子理解相當(dāng)于木桶里已經(jīng)有部分液鉛在里面,
//TH0和TL0這個(gè)兩個(gè)值表示木桶里液鉛的高度,即此時(shí)桶里只能從液鉛的高度以上開(kāi)始裝水,
//TH0 = 0xff;TL0 = 0xff;即表示桶的最高位置。
TF0 = 0; //計(jì)數(shù)到時(shí)TF0為1,即當(dāng)TH0 = 0xff;TL0 = 0xff;再運(yùn)行一步TF0 = 1;
TR0 = 1; //開(kāi)始計(jì)數(shù),從這時(shí)起,每運(yùn)行一步TH0和TL0都會(huì)增加,直到TH0 = 0xff;TL0 = 0xff;
//相當(dāng)于開(kāi)水龍頭,如TR0=0則TH0和TL0不變
ET0 = 1; //允許定時(shí)器0中斷
EA=1; //開(kāi)總中斷
//下面是個(gè)死循環(huán),程序里每運(yùn)行一步TH0和TL0都會(huì)增加,當(dāng)增加到TH0 = 0xff;TL0 = 0xff;
//單片機(jī)會(huì)從死循環(huán)里退出,去執(zhí)行中斷部分的代碼,即開(kāi)始運(yùn)行void timer0(void) interrupt 1 using 3{}
//運(yùn)行完中斷部分的代碼后,接著繼續(xù)執(zhí)行死循環(huán)里的代碼。
//注意:當(dāng)TH0 = 0xff;TL0 = 0xff;再運(yùn)行,TF0并沒(méi)有從0變?yōu)?,個(gè)人猜測(cè)TF0=1;時(shí)觸發(fā)了中斷,并重新被置零。
//如把ET0 = 1;和EA=1;注釋掉,當(dāng)TH0 = 0xff;TL0 = 0xff;再運(yùn)行,TF0會(huì)變?yōu)?,此時(shí)不會(huì)再執(zhí)行中斷部分代碼。
while(1)
{
if ((SystemTime%100)《50) //SystemTime除以100,余數(shù)小于50為真
{
…………;
}
else
{
…………;
}
};
}
釋疑:void Timer0() interrupt 1 using 1
Timer0 是函數(shù)名,隨便取的
interrupt xx using y
跟在interrupt 后面的xx 值得是中斷號(hào),就是說(shuō)這個(gè)函數(shù)對(duì)應(yīng)第幾個(gè)中斷端口,一般在51中
0 外部中斷0
1 定時(shí)器0
2 外部中斷1
3 定時(shí)器1
4 串行中斷
實(shí)際上編譯的時(shí)候就是把你這個(gè)函數(shù)的入口地址方到這個(gè)對(duì)應(yīng)中斷的跳轉(zhuǎn)地址
using y 這個(gè)y是說(shuō)這個(gè)中斷函數(shù)使用的那個(gè)寄存器組,51里面一般有4組 r0 -- r7寄存器,一共有32個(gè),如果你的終端函數(shù)和別的程序用的不是同一個(gè)寄存器組則進(jìn)入中斷的時(shí)候就不會(huì)將寄存器組壓入堆棧返回時(shí)也不會(huì)談出來(lái)節(jié)省代碼和時(shí)間
初始值算法:定時(shí)器是當(dāng)總數(shù)達(dá)到FFFFH后產(chǎn)生中斷吧!那你要讓它計(jì)數(shù)10000,是不是用FFFF(16進(jìn)制)減去10000(十進(jìn)制)的數(shù)當(dāng)計(jì)數(shù)初值 啊?TH0=-(10000/256); TL0=-(10000%256)跟FFFF(16進(jìn)制)減去10000(十進(jìn)制)的數(shù)是一樣的。從TH0=-(10000/256); TL0=-(10000%256)開(kāi)始計(jì)數(shù),計(jì)數(shù)到10000剛好滿。跟用FFFF(16進(jìn)制)減去10000(十進(jìn)制)的數(shù)一樣!?。?xiě)起來(lái)更簡(jiǎn)單,不 用算?。。?/p>
看看原碼、補(bǔ)碼就知道。正數(shù)的補(bǔ)碼是對(duì)應(yīng)的二進(jìn)制數(shù),符號(hào)位為零,負(fù)數(shù)的補(bǔ)碼是它的絕對(duì)值對(duì)應(yīng)的二進(jìn)制數(shù)按位取反再加一,符號(hào)位為一。無(wú)符號(hào)數(shù)不考慮符號(hào),那么這個(gè)結(jié)果就跟用FFFF減去它的絕對(duì)值一樣。
評(píng)論
查看更多