簡介
C51中的定時器和計數(shù)器是同一個硬件電路支持的,通過寄存器配置不同,就可以將他當(dāng)做定時器或者計數(shù)器使用。 確切的說,定時器和計數(shù)器區(qū)別是致使他們背后的計數(shù)存儲器加1的信號不同。當(dāng)配置為定時器使用時,每經(jīng)過1個機(jī)器周期,計數(shù)存儲器的值就加1。而當(dāng)配置為計數(shù)器時,每來一個負(fù)跳變信號(信號從P3.4 或者P3.5引腳輸入),就加1,以此達(dá)到計數(shù)的目的。
標(biāo)準(zhǔn)C51有2個定時器/計數(shù)器:T0和T1。他們的使用方法一致。C52相比C51多了一個T2。
時鐘周期與機(jī)器周期
定時器的本質(zhì)原理就是:每經(jīng)過1個機(jī)器周期,計數(shù)存儲器的值就加1。因此當(dāng)使用定時器時,就必須掌握時鐘周期和機(jī)器周期的關(guān)系。 時鐘周期 :晶振頻率的倒數(shù)。如果使用的是11.0592M的晶振,那么就是 1 / (11.0592x10^6) 秒 注:1MHz = 10^6Hz 機(jī)器周期 :標(biāo)準(zhǔn)51下,機(jī)器周期 =12倍的時鐘周期。即:12 / (11.0592x10^6) 秒 。有的增強(qiáng)51單片機(jī),1個機(jī)器周期等于4倍的時鐘周期,還有的更短。 計數(shù)存儲寄存器THx&TLx 定時器和計數(shù)器工作,都依賴于 計數(shù)。計數(shù)則是由計數(shù)存儲器THx和TLx這2個8位寄存器完成的。 對于計數(shù)器,每來一個負(fù)跳變信號(信號從P3.4 或者P3.5引腳輸入),就加1,以此達(dá)到計數(shù)的目的。 對于定時器,每隔1個機(jī)器周期 加 1,假如(只是假如)一個機(jī)器周期為 1ms , 當(dāng)加到1000次時,我們就認(rèn)為經(jīng)過了1s,這就是定時器定時依據(jù)。
T0和T1都擁有一對8bit計數(shù)存儲寄存器。他們的復(fù)位值都是0。 T0 對應(yīng):TH0 ,TL0 T1 對應(yīng) : TH1 , TL1
sfr TL0 = 0x8A; // TL中的L是LOW的意思,代表低位,同理H代表HIGH高位。2個8位組合起來就形成了一個16位的計數(shù)器。當(dāng)然也可以配置為僅僅當(dāng)做8bit計數(shù)存儲器用。 sfr TL1 = 0x8B; sfr TH0 = 0x8C; sfr TH1 = 0x8D;
當(dāng)計數(shù)器加滿后,再加1,就溢出了,溢出后自動歸0。且溢出時,溢出標(biāo)識位TFx 就會自動變?yōu)?(T0的溢出標(biāo)志位TF0,T1的溢出標(biāo)志位TF1)。如果啟用了對應(yīng)的中斷,單片機(jī)會調(diào)用中斷處理函數(shù)。
若TH0 和 TL0 以 16位 模式工作,那它的計數(shù)范圍為 [0 , 65535 ] , 也就是累加 65536次發(fā)生溢出。 每累加一次是 12 / (11.0592x10^6) 秒。 那么從 0 累加到溢出 歷時 ≈ 0.071s = 71ms 。 可以延時 10的整數(shù)倍ms,這樣就避免了誤差,以便用倍數(shù)控制更長的延時時間。所以,我么要給 TH0 和 TL0賦一個初始值,使他們的溢出周期(TH0,TL0從初始值到溢出所用的時間)減少到 10ms。 就像一個瓶子,開始裝了2/3,再來就只能裝1/3就溢出了。通過比例式計算: 12 / (11.0592x10^6) s ----- 1 次 10x 10-3 s ------ x 次 (求出 x = 9216次 ,計數(shù)9216次后溢出) 65536 - 9216 = 56320 = 二進(jìn)制( 11011100 00000000) 也就是 TH0 = 11011100 , TL0 = 00000000 工作模式寄存器TMOD 通過TMOD來配置T0和T1的工作模式。 注意,TMOD寄存器不可位尋址(例如sbit led = P0^0 就是P0寄存器位尋址的例子),因此對它的配置需要對這個8bit寄存器整體賦值。 注:51中有些特殊功能寄存器不支持位尋址。只有寄存器的地址值能夠被8整除的(即以數(shù)字0或者8結(jié)尾的地址,如0xA8, 0xD0),才能支持位尋址。不支持位尋址的,只能整體賦值。
小技巧:在對寄存器整體賦值時,要注意只修改我們想修改的位而不影響其它無關(guān)位的值,避免影響了之前對這個寄存器的配置。 TMOD |= 0x01; //僅僅修改TMOD的最低位,其他位保持不變。
C/T:計數(shù)器,定時器功能選擇位。 1為計數(shù)器模式, 0 為定時器模式。 M0和M1:
M1
| | | 0
| | THx和TLx 組成一個16位計數(shù)存儲器 | 1
| | 8位重裝模式。THx的值不變,負(fù)責(zé)在每次溢出后初始化TLx,僅僅由TLx計數(shù) | 1
| | 禁用定時器 1,定時器 0 變成 2 個 8 位定時器。很少使用。 | 0
| | 兼容 8048 單片機(jī)的 13 位定時器,THn的 8 位和 TLn 的 5 位組 成一個 13 位定時器。很少使用。 |
GATE:門控位。
解釋說明: ②處 C/T = 0 表示為定時器模式,觸發(fā)信號為①處的單片機(jī)內(nèi)部時鐘信號。(若②處CT = 1,則觸發(fā)信號為Tn腳) ③處表明,信號能觸發(fā)使加法計數(shù)器加1,還得受④處控制。不然時鐘信號是不能讓加法計數(shù)器累加的。 ④處這個是與門,所以TRx(TR0和TR1)必須為1,表明我們要開啟定時器。同時當(dāng),GATE為0,通過非門后為1,再通過或門,也是1,那么就讓③處控制起來了。若GATE為1,那么,定時器的啟動停止受 TRx和 INTx 共同控制。 INTx腳為1且TRx為1才能啟動定時器/計數(shù)器。 于是在一般情況下使用定時器,我們需要如下配置: TRx 為 1 GATE 為 0 INTx 任意 控制寄存器TCON 控制寄存器就是用來控制定時器/計數(shù)器 啟動和停止的,以及溢出標(biāo)志位的查詢和修改。TFx是計數(shù)存儲器溢出標(biāo)志位,只要一溢出,就馬上置為1。
TF1:定時器/計數(shù)器1的溢出標(biāo)志位。1表示計數(shù)存儲器溢出,0表示計數(shù)存儲器正常計數(shù)。 清0方式:①通過代碼修改TF1為0 ②當(dāng)通過中斷機(jī)制來使用定時器/計數(shù)器1時,進(jìn)入中斷處理函數(shù)后自動歸0 TR1:定時器/計數(shù)器1的啟動和停止位。1表示啟動,0表示停止。
TF0:定時器/計數(shù)器0的溢出標(biāo)志位。1表示計數(shù)存儲器溢出,0表示計數(shù)存儲器正常計數(shù)。 清0方式:①通過代碼修改TF0為0 ②當(dāng)通過中斷機(jī)制來使用定時器/計數(shù)器0時,進(jìn)入中斷處理函數(shù)后自動歸0 TR0:定時器/計數(shù)器0的啟動和停止位。1表示啟動,0表示停止。
低4位與外部中斷INT0和INT1有關(guān),與定時器/計數(shù)器無關(guān)。這里不做介紹。 查詢法使用T0作為定時器 程序1:P0_0連接驅(qū)動的LED小燈,用T0作為16位定時器,完成間隔為1s 的 blink程序。 #include<REGX51.H> #include"binary.h" #include"int51.h" /******************************/ void timer0_init(void); void timer0_delay(uint16_t dly); // P0_0驅(qū)動LED小燈 #define LEDpin P0_0 void main(void) { LEDpin = 0; timer0_init(); TR0 = 1; for(;;) { LEDpin = 1; timer0_delay(1000); //延時1000ms LEDpin = 0; timer0_delay(1000); //延時1000ms } } /************************* T0作為定時器的初始化 **************************/ void timer0_init(void) { TMOD |= B0000_0001; //定時器0,16位存儲計數(shù)器模式
TH0 = B1101_1100; //TH0 TL0 形成數(shù)是 56320 。這樣,一次溢出代表經(jīng)過10ms TL0 = B0000_0000; } /********************** 參數(shù): dly,延時的毫秒數(shù),只能是10的整數(shù)倍 ***********************/ void timer0_delay(uint16_t dly) { while(dly) { if(TF0){ TF0 = 0; TH0 = B1101_1100; TL0 = B0000_0000; dly -= 10; //溢出一次代表10ms } } }
程序2:通過T0定時器的8位重裝模式,使得P0_0輸出PWM信號,LED為呼吸燈效果。 #include<REGX51.H> #include"binary.h" #include"int51.h" /******************************/ void timer0_init(void); void timer0_delay(uint16_t dly); void pwm_duty(uint16_t d); // P0_0驅(qū)動LED小燈 #define LEDpin P0_0 void main(void) { uint16_t i; LEDpin = 0; timer0_init(); TR0 = 1; for(;;) { for(i=0;i<=500;++i) pwm_duty(i); for(i=500;i>0;--i) pwm_duty(i); } } /************************* T0作為定時器的初始化 **************************/ void timer0_init(void) { TMOD |= B0000_0010; //定時器0,8位重裝模式 TH0 = 250; //一次溢出代表經(jīng)過6.51us TL0 = 250; } /********************** 一次溢出代表經(jīng)過6.51us 參數(shù) c乘以6.51us 就是這個函數(shù)延時的時間 ***********************/ void timer0_delay(uint16_t c) { while(c) { if(TF0){ TF0 = 0; //因為是自動重裝,因此不用給計數(shù)存儲器賦值。 --c; } } } /************* 參數(shù)d 的范圍是[0,500]。d / 500 即為 pwm輸出的占空比,控制LED燈的暗亮程度 **************/ void pwm_duty(uint16_t d) { LEDpin = 1; timer0_delay(d); LEDpin = 0; timer0_delay(500-d); }
注:如果需要精確的延時,使用8位自動重裝模式最好,因為硬件裝值(賦值給TH0)比軟件裝值快。但8位自動重裝模式不宜做單次長時間延遲。畢竟溢出周期短。長時間延遲需要多個溢出周期,也挺消耗資源的。
盡量讓溢出周期 越長越好。溢出周期為10ms 的優(yōu)于 1ms 的。因為,在同樣的延時時間下,如100ms,溢出周期為10ms 的 只需要溢出10次,為TH0 和 TL0重新賦值10次,而溢出周期為1ms的要溢出100次,為TH0 和 TL0重新賦值100次。減少溢出次數(shù)和賦值次數(shù),可以減輕單片機(jī)的負(fù)擔(dān),提高定時的準(zhǔn)確性。
|