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