51单片机之定时器/计数器应用实例(方式0、1、2、3)
- 硬件:STC89C52RC
- 開發工具:Keil uVision4
???? 對于剛接觸單片機的同學來說可能會對定時器/計數器的應用很蒙圈,特別是初值的計算和各種定時方式的選擇。下面希望能給你帶來一個清晰的思路。
定時器:一般用于軟件計時,給定時器設置一個時間,時間到了系統停止當前的工作跳轉到事先定義好的定時器中斷函數里,函數里可以做一些周期性的事情。
計數器:一般用于檢測外來脈沖信號,給計數器設置一個次數,次數到了系統停止當前的工作跳轉到事先定義好的計數器中斷函數里,函數里做相應的事情。
先說一下相關的寄存器,也可以直接跳過,看后面的實例分析。
配置定時器或者計數器就是對相應的寄存器進行賦值,下面是相關的寄存器描述:?
第一部分寄存器:
對照著上面這一字節的每一位,進一步解析:
| 位(符號) | 功能 |
| TMOD.7 (GATE) | 置1時,只有在腳為高、TR1=1時才可打開定時器/計數器1 置0時,TR1=1即可打開定時器/計數器1 |
| TMOD.3 (GATE) | 置1時,只有在腳為高、TR1=1時才可打開定時器/計數器0 置0時,TR1=1即可打開定時器/計數器0。 |
| TMOD.6 ?(?/) | 置1時,用作計數器1(從T1/P3.5腳輸入) 置0時,用作定時器1 |
| TMOD.2 ?(/ ) | 置1時,用作計數器0(從T0/P3.4腳輸入) 置0時,用作定時器0 |
| TMOD.5/TMOD.4 ?(M1、M0) 定時器/計數器1 選擇工作方式 | 方式0:M1=0,M0=0 ,13位定時器/計數器 |
| 方式1:M1=0,M0=1 ,16位定時器/計數器 | |
| 方式2:M1=1,M0=0 ,8位自動重載定時器 | |
| 方式3:M1=1,M0=1 ,定時器/計數器1 此時無效 | |
| TMOD.1/TMOD.0 ?(M1、M0) 定時器/計數器0 選擇工作方式 | 方式0:M1=0,M0=0 ,13位定時器/計數器 |
| 方式1:M1=0,M0=1 ,16位定時器/計數器 | |
| 方式2:M1=1,M0=0 ,8位自動重載定時器 | |
| 方式3:M1=0,M0=0 ,雙8位定時器/計數器 |
用或運算是為了在給相應位賦值時不會影響無關位。可以試著解讀TMOD=0xDA
第二部分寄存器:
主要看T開頭的,I開頭是外部中斷,先不管。
| 位(符號) | 功能 |
| TCON.7 (TF1) | 定時器/計數器1溢出標志位。當 T1 被允許計數后T1從初值開始加1計數,最高位產生溢出時,置“1 ”TF1 ,并向 CPU請求中斷,當CPU響應時,由硬件清“0 ”TF1 ,TF1也可以由程序查詢或清“0 ”。 |
| TCON.5?(TR1) | 定時器 T1 的運行控制位。該位由軟件置位和清零。當 GATE(TMOD.7)=0,TR1=1 時就允許T1開始計數,TR1=0 時禁止 T1 計數。當 GATE(TMOD.7)=1,TR1=1 且 INT1 輸入高電平時,才允許 T1 計數。 |
| TCON.4?(TF0) | 定時器/計數器 0 溢出標志位。當T0被允許計數后T0 從初值開始加 1 計數,最高位產生溢出時,置“1”TF0,并向CPU請求中斷,當 CPU 響應時,由硬件清“0”TF0,TF0也可以由程序查詢或清“0”。 ? |
| TCON.3?(TR0) | 定時器 T0 的運行控制位。該位由軟件置位和清零。當 GATE(TMOD.3)=0,TR0=1 時就允許T0開始計數,TR1=0 時禁止 T0 計數。當 GATE(TMOD.3)=1,TR0=1 且 INT0 輸入高電平時,才允許 T0 計數 |
除了TCON、TMOD還有TL0、TH0和TL1、TH1,它們分別是定時器0的Timer寄存器和定時器1的Timer寄存器。這個參數沒有單位,不是毫秒或是其他,所以設置定時器的時間要通過一定的計算得來,也就是后面要說的重點部分。
定時器的應用:
編寫單片機定時器程序的步驟:
下面以定時器0為例,闡述不同的方式的編程過程。
方式0:
#include<reg52.h>#define uchar unsigned char #define uint unsigned intsbit led1=P1^0; uchar num;void TIM0init(void) {TMOD=0x00; //設置定時器0為工作方式0TH0=(8192-5000)/32; //裝入初值,怎么計算,下面分析TL0=(8192-5000)%32; EA=1; //開總中斷ET0=1; //開定時器中斷TR0=1; //啟動定時器0 } /* interrupt 0 指明是外部中斷0; interrupt 1 指明是定時器中斷0; interrupt 2 指明是外部中斷1; interrupt 3 指明是定時器中斷1; interrupt 4 指明是串行口中斷;函數名字可以隨便起,但定時器0的中斷號是固定為1的 */ void T0_time() interrupt 1 {TH0=(8192-5000)/32; //重裝初值,如果不重裝,中斷只觸發一次TL0=(8192-5000)%32;num++; }void main() {TIM0init(); while(1){if(num==200) //如果到了200,說明一秒時間到{num=0;led1=~led1; //讓發光管狀態取反}} }假設單片機用的晶振是12MHz,上面的中斷函數每過5ms會被調用一次,也就是發光管每一秒狀態取反一次。那么怎么計算初值以確定TL0和TH0的值呢?
定時器方式0是指13位定時器,=8192;也就是說,當設置好初值后,系統會在這個初值的隔一個機器周期就會自增1,當累加到8192的時候溢出,然后觸發中斷。所以(8192-初值)*機器周期=定時器產生一次中斷的時間。
如果我們要設定的定時器產生一次中斷的時間為5ms,那么:
???????????????????????????????????????????????? 機器周期=12*(1/12MHz)=1μs
???????????????????????????????????????????????? 初值=(8192-5ms/1μs)=3192
13位定時器中,TH0整個 8 位全用,TL0只用低 5 位參與分頻。
| TH0 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
| TL0 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
因:3192=“110001111000”
所以TH0=“1100011”,TL0=“11000”
即TH0=(8192-5000)/32,TL0=(8192-5000)%32
如果用的是11.0592MHz的晶振,機器周期就不是整數了,12*(1/11059200)≈1.0851μs.
關于機器周期:
方式0跟方式1差不多的,不同的是方式1中TH0、TL0所有位全用。兩個字節,=65536.
方式2:
?? 在定時器的方式0和方式1中,當計數溢出后,計數器變為0,因此在循環定時或循環計數時必須用軟件反復設置計數初值,這必然會影響到定時的精度,同時也給程序設計帶來很多麻煩。
???? 方式2被稱為8位初值自動重裝的8位定時器/計數器,TL(0/1)從初值開始計數,當溢出時,在溢出標志TF(0/1)置1的同時,自動將TH(0/1)中的常數重新裝入TL(0/1)中,使TL(0/1)從初值開始重新計數,這樣避免了認為軟件重新裝初值所帶來的時間誤差,從而提高了定時的精度。
#include<reg52.h>#define uchar unsigned char #define uint unsigned intsbit led1=P1^0; uint num;void TIM0init(void) {TMOD=0x02; //設置定時器0為工作方式2TH0=6; //裝入初值TL0=6; EA=1; //開總中斷ET0=1; //開定時器中斷TR0=1; //啟動定時器0 }void T0_time() interrupt 1 {//相比上面的方式0,這里不需要認為加入重裝初值的代碼num++; }void main() {TIM0init(); while(1){if(num==4000) //如果到了4000,說明1秒時間到{num=0;led1=~led1; //讓發光管狀態取反}} }??? 這個也是基于12MHz的振蕩頻率,TL0跟TL1必然是相同的,計算初值的方法跟上面一樣。方式2為8位定時器/計數器,最多能裝載=256個,相對方式0的13位和方式1的16位的少。方式2經歷256個機器周期該計數器就會溢出。
??? 還有一個值得注意的是num變量的類型變了,因為4000已經超出了uchar的方位,所以改為uint。
方式3:
?????當選擇方式3時,定時器T0就會被分成兩個獨立的計數器或者定時器。此時,TL0為8位計數器,計數溢出好置位TF0,并向CPU申請中斷,之后需要軟件重裝初值;?TH0也被固定為8位計數器,不過TL0已經占用了TF0和TR0,因此TH0將占T1的中斷請求標志TF1和定時器啟動控制為TR1。
???? 為了防止中斷沖突,定時器T0在方式3時,T1不能產生中斷,但可以正常工作在方式0、1、2下。通常這種情況下T1將用作串行口的波特率發生器。
下面的例子是利用定時器方式3,TL0計數器對應的8位定時器實現第一個發光管以1s亮滅閃爍,用TH0計數器對應的8位定時器實現第二個發光管以0.5s亮滅閃爍。
#include<reg52.h>#define uchar unsigned char #define uint unsigned intsbit led1=P1^0; sbit led2=P1^1; uint num1,num2;void TIMEinit(void) {TMOD=0x03; //設置定時器0為工作方式3 TH0=6; //裝初值TL0=6;EA=1; //開總中斷ET0=1; //開定時器0中斷ET1=1; //開定時器1中斷TR0=1; //啟動定時器0TR1=1; //啟動定時器0的高8位計數器 }void TL0_time() interrupt 1 {TL0=6; //重裝初值num1++; }void TH0_time() interrupt 3 //占用T1定時器的中斷號 {TH0=6; //重裝初值num2++; }void main() {TIMEinit();while(1){if(num1>=4000) //12*(1/12MHz)*(256-6)*4000=1s{ num1=0;led1=~led1;}if(num2>=2000) //12*(1/12MHz)*(256-6)*2000=0.5s{num2=0;led2=~led2 ;} } }這里的num1>=4000而不是num1==4000,是為了穩妥起見,萬一定時器計數超過了4000,而主循環還沒來得及判斷,則會錯過4000.那led1就不能實現取反了。
?
僅供參考,錯誤之處以及不足之處還望多多指教。
總結
以上是生活随笔為你收集整理的51单片机之定时器/计数器应用实例(方式0、1、2、3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt程序怎么在别人的电脑上运行
- 下一篇: Qt小游戏《2048》源码(含大量注释)