定時器(二)
在上一篇文章已經介紹定時器的用法,而且簡單介紹工作方式1。工作方式1使用場景較豐富,在大部分用到定是的場景皆可使用。其余剩下的3種工作方式也有很多使用場景,,下面來一一介紹。下文以定時器0為準,晶振以11.0592為準。這裏貼一張圖,來再回顧定時器的4種工作方式。
4種工作方式
工作方式0
工作方式0,13位定時器/計數器。下面以定時器0為例講解。
定時器方式0邏輯結構
上圖可以知道定時器工作方式0的邏輯結構,與工作方式1相差不遠,只是方式1是兩個8位計數器,而方式0是一個8位,一個低5位組成的13位計數器。編程控制兩個非常像。方式0的計數器最大值為2的13次方=8192,計數一次時間最大值為8192*1.09=8929us(以晶振為11.0592MHZ為例)。下面示範講解使用,比如說設置一個10ms的間距,哎哦,最大間距為9ms,設置不了,那就以5ms為例
示例
N=5*1000/1.09=4587,
TL0(低五位計數器)最大值為2的5次方=32,所以TH0(高八位)=4587/32=143,十六進制為0X8F。
TL0=4587%32=11,十六進制為0X0B。
所以 TH0=0X8F; //註意高低位,H=high, 高
TL0=0X0B; //L=low, 低
下面寫一個簡單的完整程序:`
#include<reg52.h> //頭文件
sbit led=P1^1; //位定義
unsigned char count; //定義一個無符號型變量count,記錄中斷次數
main () //主函數
{
TMOD=0X00; //定時器0,工作方式0
TH0=0X8F; //賦初值,定時5ms
TL0=0X0B;
EA=1; //打開總中斷
ET0=1; //打開定時器0中斷
TR0=1; // 啟動定時器0
}
void timer_T0() interrupt 1 //中斷函數,1表示中斷源為定時器0
{
TMOD=0X00; //重新賦值
TH0=0X8F;
count++; //中斷一次加1
if(count==20) //count==20時表示100ms到
{
count=0; //清0
led=~led; //狀態取反
}
}
方式0的使用至此已經講解完畢,方式0與方式1的使用差不多,可以對照來看,兩者使用場景也差不多,能使用方式1的地方也可以使用方式0.
工作方式2
工作方式2,8位初值自動重載的8位定時器/計數器
定時器方式2邏輯結構
在這裏說明一下,定時器是比簡單的延時函數實用,精確。但定時器也是有誤差的,在賦值時,采用的是約等數。人工重載時時間上也會造成誤差。種種原因,時間一久,誤差累積就會很大,在一些要求精確度功能時,比方說串口通信設置波特率,就會出錯。所以就出現了方式2,計數器自動重載。
來看一下方式2的邏輯圖,右邊部分的圖不用說了,重點來看左邊。TH0被作為常數緩沖器,當TL0計數溢出,在溢出標誌TF0置1的同時,自動將TH0的常數重新裝入TL0中,時TL0從初值開始重新計數。
方式2,8位計數器,最大值為2的8次方=256,計數時間最大值為2561.09=279us,以100ms為間距時, N=100
範例
#include<reg52.h> //頭文件
sbit led=P1^1; //位定義
unsigned int count; //定義一個無符號型變量count,記錄中斷次數
main () //主函數
{
TMOD=0X02; //定時器0,工作方式2
TH0=0X00; //賦初值,定時279us
TL0=0X00;
EA=1; //打開總中斷
ET0=1; //打開定時器0中斷
TR0=1; // 啟動定時器0
}
void timer_T0() interrupt 1 //中斷函數,1表示中斷源為定時器0
{
//不用賦初值,計數器自動重載
count++; //中斷一次加1
if(count==358) //count==358時表示100ms到
{
count=0; //清0
led=~led; //狀態取反
}
}
方式2的講解告一段落,方式2特別適合用於做較精確的脈沖信號發射器。
工作方式3
工作方式3,僅使用於T0,分成兩個8位計數器,T1停止計數。
工作方式3邏輯結構
上圖結構簡單,就不多講解。就是定時器0的計數器分成兩個,TH0作用於TF1,TR1.
TL0作用於TF0,TR0.那就是說使用工作方式3時,不使用T1定時器,或使用時不使用中斷,這個情況就是定時器1使用工作方式2,不然會出錯,因為TF1,TR1已經讓定時器0占用。計算已經大同小異,把式子就算了。TH0定時5ms,TL0定時10ms。
N=51000/(2561.09)=18 //TH0
N=101000/(2561.09)= 36 //THL0
示例
#include<reg52.h> //頭文件
sbit led_1=P1^1; //位定義
sbit led_2=P1^2;
unsigned char count; //定義一個無符號型變量count,記錄TH0中斷次數
unsigned char num; //定義一個無符號型變量num,記錄TL0中斷次數
main () //主函數
{
TMOD=0X03; //定時器0,工作方式2
TH0=0X00; //賦初值,定時279us
TL0=0X00; //賦初值,定時279us
EA=1; //打開總中斷
ET0=1; //打開定時器0中斷
ET1=1; //打開定時器1中斷
TR0=1; // 啟動定時器0的低八位計數器
TR1=1; // 啟動定時器0的高八位計數器
}
void timer_TH0() interrupt 1 //中斷函數,1表示中斷源為定時器0
{
TH0=0X00; //重新賦值
count++; //中斷一次加1
if(count==18) //count==18時表示5ms到
{
count=0; //清0
led_1=~led_1; //狀態取反
}
}
void timer_THL0() interrupt 1 //中斷函數,1表示中斷源為定時器0
{
TH0=0X00; //重新賦值
num++; //中斷一次加1
if(num==36) //num==36時表示10ms到
{
num=0; //清0
led_2=~led_2; //狀態取反
}
}
方式3的使用也講完了。下面介紹兩個定時器嵌套使用。
定時器綜合
兩個定時器一起使用也沒有什麽困難,很簡單的,只要一個的會用,兩個也不成問題,小小意思。這和方式3有點像。看看下面例程就回了。
#include<reg52.h> //頭文件
sbit led_1=P1^1; //位定義
sbit led_2=P1^2;
unsigned char count; //定義一個無符號型變量count,記錄TH0中斷次數
unsigned char num; //定義一個無符號型變量num,記錄TL0中斷次數
main () //主函數
{
TMOD=0X11; // 0001 0001 定時器0,工作方式1 定時器1, 工作方式1
TH0=(65535-50000)/256; //賦初值,定時50ms
TL0=(65535-50000)%256;
TH1=(65535-10000)/256; //賦初值,定時10ms
TL1=(65535-10000)%256;
EA=1; //打開總中斷
ET0=1; //打開定時器0中斷
ET1=1; //打開定時器1中斷
TR0=1; // 啟動定時器0
TR1=1; // 啟動定時器1
}
void timer_T0() interrupt 1 //中斷函數,1表示中斷源為定時器0
{
TH0=(65535-50000)/256; //重新賦值
TL0=(65535-50000)%256;
count++; //中斷一次加1
if(count==20) //count==20時表示1s到
{
count=0; //清0
led_1=~led_1; //狀態取反
}
}
void timer_T1() interrupt 3 //中斷函數,3表示中斷源為定時器1
{
TH1=(65535-10000)/256; //重新賦值
TL1=(65535-10000)%256;
num++; //中斷一次加1
if(num==10) //num==10時表示100ms到
{
num=0; //清0
led_2=~led_2; //狀態取反
}
}
結束語
定時器的講解已經結束,大家對定時器的認知肯定也提升不少。定時器非常重要,在大部分的程序中都會運用到,這個要求會用。上面的講解已經非常直白了,不懂的多看幾遍,每次都會有新的體悟。定時器就一計數器,到點了就中斷,卡準中斷時間,不就可知道時間,定時器就這樣子,不難。
定時器(二)