51单片机开发入门(5)-定时器/计数器
定時器/計數(shù)器闡述
AT89S51單片機(jī)有**兩個16位**內(nèi)部定時/計數(shù)器,記作T0、T1。
(AT89S52有3個定時/計數(shù)器,比AT89S51多了個T2。)
本質(zhì)上,定時/計數(shù)器就是一個可以通過編程控制計數(shù)脈沖源、計數(shù)初值,并具有溢出標(biāo)志和中斷響應(yīng)機(jī)制的**硬件加法計數(shù)器**。
定時/計數(shù)器在計數(shù)值**溢出時置位一個標(biāo)志位TF**,可以供中斷或者查詢使用。
定時/計數(shù)器的計數(shù)脈沖源可以通過編程進(jìn)行設(shè)置,從而起到**定時或者計數(shù)**的作用。
定時/計數(shù)器的結(jié)構(gòu)
定時/計數(shù)器為16位硬件加法計數(shù)器,由高8位TH和低8位TL兩個計數(shù)寄存器組成。
定時/計數(shù)器具有可選的兩個計數(shù)脈沖源:
1、外部輸入脈沖
2、內(nèi)部機(jī)器周期脈沖
- 定時器模式
加法計數(shù)器對**內(nèi)部機(jī)器周期**進(jìn)行計數(shù)。
機(jī)器周期**T**已知
通過設(shè)置計數(shù)值**N確定定時時間t**。
t=NxT
- 計數(shù)器模式
加法計數(shù)器對**外部輸入脈沖**進(jìn)行計數(shù),實現(xiàn)計數(shù)或頻率測量功能。
外部計數(shù)脈沖由T0(P3.4)或T1(P3.5)引腳輸入到計數(shù)器。計數(shù)器在檢測到**外部輸入脈沖下降沿時計數(shù)值加1,由于檢測一個從1到0的下降沿需要2個機(jī)器周期,因此可以計數(shù)的外部脈沖最高頻率為fosc/24**。
例:晶振頻率12MHz時,最高外部計數(shù)頻率500KHz。
12MHz / 24 = 500KHz
- 定時/計數(shù)器具有兩個設(shè)置寄存器:
TMOD**,確定工作方式和功能;
**TCON,控制T0、T1的啟停,且包含溢出標(biāo)志。
定時/計數(shù)器有關(guān)的SFR
定時/計數(shù)器0計數(shù)值寄存器(16位):
共16位,分為高8位和低8位進(jìn)行存儲;
根據(jù)工作方式的不同可以使用其中的13位、8位、16位;
計數(shù)值范圍根據(jù)使用位數(shù)而發(fā)生變化;
只能加法計數(shù);
用戶在編程時可以向TH和TL寫入定時/計數(shù)器的計數(shù)初始值,在啟動定時/計數(shù)器后,定時/計數(shù)器將從設(shè)置的初始值開始加法計數(shù)。
📌當(dāng)計數(shù)到最大值時,產(chǎn)生溢出標(biāo)志。
- TMOD(T/C方式控制寄存器) SFR地址:0x89
GATE:門控信號
1 – T/C啟動要求TR和INT同時為‘1’;
0 – T/C啟動僅受TR控制
C/T:計數(shù)器/定時器選擇位
0 – 定時器模式;內(nèi)部時鐘
1 – 計數(shù)器模式;外部脈沖
M1和M0:T/C工作方式選擇
可以選擇工作方式0、1、2、3。
- TCON(T/C控制寄存器) SFR地址:0x88
TR0:T/C0啟動控制位;
TF0:T/C0溢出標(biāo)志位,用于中斷響應(yīng);
TR1:T/C1啟動控制位;
TF1:T/C1溢出標(biāo)志位,用于中斷響應(yīng);
TR:1--啟動計數(shù);0--停止計數(shù);
TF:計數(shù)值溢出時由硬件置1;CPU轉(zhuǎn)入中斷響應(yīng)程
序時由硬件自動清零,也可以軟件清零。
定時/計數(shù)器的工作方式
8051單片機(jī)的定時/計數(shù)器具有**四種**工作方式,可以使用TMOD中的M1和M0進(jìn)行選擇:
| 0 | 0 | 0 | 13位定時器/計數(shù)器 |
| 0 | 1 | 1 | 16位定時器/計數(shù)器 |
| 1 | 0 | 2 | 自動重載的8位定時器/計數(shù)器 |
| 1 | 1 | 3 | 僅適用于T0,兩個8位定時器/計數(shù)器 |
工作方式0(定時/計數(shù)器0和1均可)
方式0為**13位**計數(shù),由TL0的低5位(高3位未用)和TH0的8位組成。TL0的低5位溢出時向TH0進(jìn)位,TH0溢出時,置位TCON中的TF0標(biāo)志,向CPU發(fā)出中斷請求。
工作方式1 (定時/計數(shù)器0和1均可)
方式1的計數(shù)位數(shù)是**16位**,由TL0作為低8位、TH0作為高8位,組成了16位加1計數(shù)器 。
工作方式2 (定時/計數(shù)器0和1均可)
方式2為自動重裝初值的8位計數(shù)方式。
🎯**工作方式2特別適合于用作較精確的脈沖信號發(fā)生器。 **
定時器時間的設(shè)置(定時期初值的計算)
在內(nèi)部定時方式下, T0、T1對內(nèi)部機(jī)器周期計數(shù),若fosc=6MHz,一個機(jī)器周期為12/fosc=2us,所以
? 方式0 13位定時器最大定時間隔=2^13×2us=16.384ms
? 方式1 16位定時器最大定時間隔=2^16×2us=131.072ms
? 方式2 8位定時器最大定時間隔 =2^8 ×2us=512us
例:若使T0工作在方式0,要求定時1ms,求計數(shù)初值。設(shè)計數(shù)初值為x,則有:
(2^13-x)×2us= 1000us
x = 2^13-500 = 0x1E0C
因此,TH,TL可置 8192-500
TH0 = 0xF0;TL0 = 0x0C;
工作方式3(僅適用于定時/計數(shù)器0)
方式3只適用于T/C0。當(dāng)方式3時,TH0和TL0成為兩個獨(dú)立的計數(shù)器。 此時,TL0可用作定時/計數(shù)器,占用TR0和TF0;TH0只能用作定時器,占用TR1和TF1;T/C1仍可用于方式0、1、2,但不能使用中斷。
📡只有在T/C1被串口占用時,T/C0才使用方式3
定時/計數(shù)器的使用
定時器/計數(shù)器的初始化及其步驟
使用8051的T0、T1前,應(yīng)對它進(jìn)行編程初始化,主要是對TCON和TMOD編程;計算和裝載計數(shù)初值(TH、TL)。
一般完成以下幾個步驟:
○確定定時/計數(shù)器的工作方式(編程TMOD寄存器);
○計算計數(shù)初始值,并賦予TH、TL;
○如果在中斷方式工作,需使能中斷;
○啟動定時器/計數(shù)器(編程TR0或TR1位)。
數(shù)碼管顯示實驗
- “實現(xiàn)數(shù)值0~65535的變化顯示”
問題:如何使數(shù)值變化的速度減慢?(如每隔1秒數(shù)值加1)
一般方法(延時函數(shù))
void main(void) { unsigned char i,j; unsigned int uiTemp = 0;while(1) { convert(uiTemp); for(i=0;i<6;i++) { P2=LED_seg[i]; //送段碼 P0=LED_bit[i]; //送位碼 delay1ms(5); //5ms延遲 } j ++;if ( j == 33 ) //約1秒{ uiTemp++;j = 0;}} }- 定時/計數(shù)器方法
實現(xiàn)數(shù)值0~65535的變化顯示,每隔50ms數(shù)值加1。( 設(shè)晶振頻率為12MHz)
#include < reg51.h>unsigned int gluiTemp ; void Timer0( ) interrupt 1 using 1 {TH0 = (65536 – 50000) / 256;TL0 = (65536 – 50000) % 256;gluiTemp ++ ; } void main(void) { unsigned char i,j; gluiTemp = 0;TMOD = 0x01;TH0 = (65536 – 50000) / 256;TL0 = (65536 – 50000) % 256;ET0 = 1;EA = 1;TR0 = 1;while(1) { convert(gluiTemp); for(i=0;i<6;i++) { P2=LED_seg[i]; //送段碼 P0=LED_bit[i]; //送位碼 delay1ms(5); //5ms延遲 P0=0;} } }實現(xiàn)數(shù)值0~65535的變化顯示,每隔1s數(shù)值加1。( 設(shè)晶振頻率為12MHz)
#include < reg51.h> unsigned char glucCounter ;//定義一個全局變量,實現(xiàn)計數(shù)功能 unsigned int gluiTemp ; void Timer0( ) interrupt 1 using 1 {TH0 = (65536 – 50000) / 256;TL0 = (65536 – 50000) % 256;glucCounter ++ ;if ( glucCounter == 20 ){gluiTemp ++ ;glucCounter = 0;} } void main(void) { unsigned char i,j; gluiTemp = 0;glucCounter = 0;TMOD = 0x01;TH = (65536 – 50000) / 256;TL = (65536 – 50000) % 256;ET0 = 1;EA = 1;TR0 = 1;while(1) { convert(gluiTemp); for(i=0;i<6;i++) { P2=LED_seg[i]; //送段碼 P0=LED_bit[i]; //送位碼 delay1ms(5); //5ms延遲 P0=0;} } }單片機(jī)輸出(1)
例1:設(shè)晶振頻率fosc=6MHz,要求在P2.0腳上輸出周期為2ms的方波。
解:采用定時器T0的方式1進(jìn)行編程
思路:采用定時間隔1ms,每次時間到P2.0取反并且啟動下一次定時,從而實現(xiàn)2ms周期的方波。
定時所需計數(shù)次數(shù)n=1000us/2us=500
由于計數(shù)器遞增計數(shù),為得到500個計數(shù)之后的定時器溢出,必須給定時器置初值65536-500。
中斷方式
#include <reg51.h> sbit P2_0=P2^0; void T0_ISR() interrupt 1 using 1 //T0中斷服務(wù)程序入口 { TH0= (65536-500)/ 256; //計數(shù)初值重載TL0=(65536-500)% 256; P2_0=!P2_0; //P2.0取反 } void main(void) { TMOD=0x01; //T0工作在定時器方式lTH0=(65536-500)/256; //計數(shù)初值TL0=(65536-500)%256; ET0=1; EA=1; //中斷使能TR0=1; //啟動T0while(1); }但是這里精度還有待考究
中斷方式
#include <reg51.h> sbit P2_0=P2^0; void main(void) { TMOD=0x01; //T0工作在定時器方式lTH0= (65536-500)/256; //計數(shù)初值TL0=(65536-500)%256; TR0=1; //啟動T0while(1){TH0= (65536-500)/256; //計數(shù)初值TL0=(65536-500)%256; /*查詢TF0是否為‘1’,如果為‘1’則說明溢出(定時時間到)*/while (!TF0);P2_0 = ! P2_0;TF0 = 0;} }單片機(jī)輸出(2)
例2:設(shè)單片機(jī)fosc=6MHz,要求在P2.0引腳上輸出周期為2ms,占空比為75%的矩形波。在例2基礎(chǔ)上應(yīng)作何修改?
解:采用定時器T0的方式1進(jìn)行編程
思路:采用定時間隔0.5ms,并設(shè)置一個全局變量i對定時器的中斷次數(shù)進(jìn)行計數(shù),小于等于3時輸出高電平,等于4時輸出低電平,以此循環(huán)。
#include "reg51.h" sbit P1_0=P1^0; unsigned char i; void TIMER0_ISR (void) interrupt 1 { TH0=(65536-250)/256;TL0=(65536- 250)%256;i++;if(i = = 3) { P1_0=0; }if(I = = 4) { P1_0=1; i=0; } } void main (void) { TMOD=0x01;TH0=(65536- 250)/256;TL0=(65536- 250)%256;ET0=1; EA=1;TR0=1;i = 0;P1_0 = 1;while (1) { ; } }小思考
如果周期2ms占空比要求是35%,可以采用采用定時間隔0.5ms嗎?
思路:高電平持續(xù)時間35%,低電平持續(xù)時間65%,應(yīng)考慮取其公約數(shù)。一般取最大公約數(shù)。
所以可取時間間隔為0.1ms。
高電平持續(xù)7個定時間隔,低電平持續(xù)13個時間間隔。
單片機(jī)輸出(3)
例3:設(shè)單片機(jī)fosc=6MHz,要求在P2.0引腳上輸出周期為2ms,占空比為65%的矩形波。在例2基礎(chǔ)上應(yīng)作何修改?
#include "reg51.h" sbit P1_0=P1^0; unsigned char i; void TIMER0_ISR (void) interrupt 1 { TH0=(65536-50)/256;TL0=(65536- 50)%256;i++;if(i = = 7) { P1_0=0; }if(I = = 20) { P1_0=1; i=0; } } void main (void) { TMOD=0x01;TH0=(65536- 50)/256;TL0=(65536- 50)%256;ET0=1; EA=1;TR0=1;i = 0;P1_0 = 1;while (1) { ; } }單片機(jī)輸出(4)
例4:設(shè)晶振頻率fosc=6MHz,要求在P2.0腳上輸出周期為2s的方波。
長定時的實現(xiàn)
思路:定時間隔為1秒,使用T0無法直接得到1秒的定時。因此,需要使用多次定時復(fù)合的方法來得到較長時間的定時。
方法一 使用兩個定時/計數(shù)器實現(xiàn)1秒定時:
T0工作在定時方式1,定時100ms,從而控制P1.0輸出周期為200ms的方波;將這個方波輸入到T1,T1工作在計數(shù)方式2,計數(shù)5次后溢出,控制P2.0反向,從而實現(xiàn)周期為2秒的方波輸出。
#include <reg51.h> sbit P1_0 = P1^0; sbit P1_7 = P1^7; void Timer0( ) interrupt 1 using 1 {TH0 = (65536 – 50000) / 256;TL0 = (65536 – 50000) % 256;P1_0 = ! P1_0; } void Timer1( ) interrupt 3 using 2 {P1_7 = ! P1_7; } void main( ) {P1_7 = 0;P1_0 = 1;TMOD = 0x61;TH0 = (65536 – 50000) / 256;TL0 = (65536 – 50000) % 256;TH1 = 256 – 5 ;TL1 = 256 – 5;IP = 0x08;EA = 1;ET0 = 1;ET1 = 1;TR0 = 1;TR1 = 1;while(1) { ; } }方法二 僅使用一個定時/計數(shù)器實現(xiàn)
使用方法一時需要使用兩個定時器和兩個IO引腳,資源消耗比較多。如何使用較少的資源實現(xiàn)同樣的定時功能
#include < reg51.h> sbit WAVE = P1^7; unsigned char glucCounter; void Timer0( ) interrupt 1 using 1 {TH0 = (65536 – 50000) / 256;//設(shè)定初值,100ms定時TL0 = (65536 – 50000) % 256;glucCounter ++ ;//定義一個全局變量,實現(xiàn)計數(shù)功能if ( glucCounter == 10 ){WAVE = ! WAVE;glucCounter = 0;} } void main( ) {WAVE = 0;TMOD = 0x01;TH0 = (65536 – 50000) / 256;TL0 = (65536 – 50000) % 256;EA = 1;ET0 = 1;TR0 = 1;glucCounter = 0;while(1) { ; } }8051 定時計數(shù)器工程應(yīng)用
設(shè)計要求
航標(biāo)燈控制(工程設(shè)計)
2位撥碼開關(guān):燈質(zhì)設(shè)定(頻率與占空比)
光敏電阻:實現(xiàn)光照采集(光照強(qiáng),電阻小;光照弱,電阻大)
燈驅(qū)動:發(fā)光二極管采用12V供電,要實驗單片機(jī)驅(qū)動電路設(shè)計,三極管驅(qū)動
電路仿真圖:
軟件程序
這里把相關(guān)函數(shù)先封裝起來了,具體程序代碼如下所示:
main.c
#include <reg51.h> #include "timer.h"/*******變量類型宏定義*******/ #define uchar unsigned char #define uint unsigned int/*******引用結(jié)構(gòu)變量*******/ extern struct IALA IALA4[4]; extern struct Turn Counter50ms;/*******IO口定義*******/ sbit D=P0^0; //燈輸出控制,高電平有效 sbit OPCON=P1^2; //光照檢測,高電平(亮)/*******定時器0初始化*******/ void Timer0Init() {TMOD=(TMOD&0xf0)|0x01;//定時器0 方式1TR0=1; //開啟定時器T0TH0=(65535-COUNT_50MS)/256;//設(shè)定定時器初始值(高8位)TL0=(65535-COUNT_50MS)%256;//設(shè)定定時器初始值(低8位)TF0=0; //中斷標(biāo)志位清零ET0=1; //允許定時器0中斷EA=1; //打開總中斷PT0=0; //中斷優(yōu)先級設(shè)置 }/*******主函數(shù)*******/ void main(void) {uchar key,pre_key=0,i;uint sum;Timer0Init(); //定時器0初始化 D=0; //LED控制端初始化while(1){key=key_scan(); //獲取撥碼開關(guān)對應(yīng)狀態(tài)if(key!=pre_key)//撥碼開關(guān)狀態(tài)改變{sum=0; //變量初始化for(i=0;i<6;i++){sum=sum+IALA4[key].gcd*IALA4[key].state[i];//結(jié)構(gòu)體變量賦值且運(yùn)算賦值給sumCounter50ms.state[i]=sum; //確定定時時間}}pre_key=key;} } void T0_ISR(void) interrupt 1 //中斷服務(wù)函數(shù) {static uint i=0; //定義靜態(tài)變量iuchar j;TH0=(65535-COUNT_50MS)/256; //重新賦初值TL0=(65535-COUNT_50MS)%256;if(OPCON==0) //如果是黑夜,對應(yīng)燈質(zhì)狀態(tài){for(j=0; j<2; j++) //循環(huán)閃爍{if(i==Counter50ms.state[2*j]){D=D_CLOSE;}if(i==Counter50ms.state[2*j+1]){D=D_OPEN;}}if(i==Counter50ms.state[4]) {D=D_CLOSE;}if(i>=Counter50ms.state[5]){D=D_OPEN;i=0;}i++;}else{//白天熄燈D=D_CLOSE;i=0;} }timer.c
#include <reg51.h> #include "timer.h"sbit D=P0^0; //燈輸出控制,高電平有效 sbit OPCON=P1^2; //光照檢測,高電平(亮)//結(jié)構(gòu)體全局變量定義,const:常數(shù) const struct IALA IALA4[4]={ //NUM gcd ON1 OFF1 ON2 OFF2 ON3 OFF3{0,0,0,0,0,0,0,0},//全亮{1,10,1,3,0,0,0,0},{2,10,1,1,1,7,0,0},{3,10,1,2,1,2,1,5}};//結(jié)構(gòu)體全局變量定義 struct Turn Counter50ms; unsigned char key_scan(void) //撥碼開關(guān)狀態(tài)檢測 {unsigned char key;key=0;if(P1&0x01)key=key|0x01;if(P1&0x02)key=key|0x02;return(key); }timer.h
#ifndef _TIMER_H_ #define _TIMER_H_ #define COUNT_50MS 500 //50ms gcd:50ms的倍數(shù) #define D_OPEN 1 //開燈 #define D_CLOSE 0 //關(guān)燈//結(jié)構(gòu)體類型聲明1 struct IALA {unsigned char num; //撥碼開關(guān)狀態(tài)unsigned char gcd; //各時間間隔最大公約數(shù)unsigned char state[6];//無符號字符型時間常數(shù) }; //結(jié)構(gòu)體類型聲明2 struct Turn {unsigned int state[6]; }; unsigned char key_scan(void); //函數(shù)聲明#endif總結(jié)
以上是生活随笔為你收集整理的51单片机开发入门(5)-定时器/计数器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Go实战基础】程序里面数据是如何显示到
- 下一篇: 什么是全连接神经网络?