定時(shí)/計(jì)數(shù)器從電路上來(lái)講是一個(gè)脈沖計(jì)數(shù)器,當(dāng)計(jì)數(shù)脈沖來(lái)自于單片機(jī)內(nèi)部機(jī)器周期時(shí),我們習(xí)慣上稱(chēng)其為定時(shí)器,而當(dāng)計(jì)數(shù)脈沖來(lái)自于單片機(jī)外部的輸入信號(hào)時(shí),則稱(chēng)其為計(jì)數(shù)器。8051系列單片機(jī)在片內(nèi)集成了兩個(gè)可編程的定時(shí)/計(jì)數(shù)器,分別稱(chēng)其為定時(shí)/計(jì)數(shù)器0(T0)和定時(shí)/計(jì)數(shù)器1(T1),二者都具有定時(shí)和計(jì)數(shù)的功能。
這兩個(gè)定時(shí)計(jì)數(shù)器可以獨(dú)立配置為定時(shí)器或計(jì)數(shù)器。當(dāng)被配置為定時(shí)器時(shí),將按照預(yù)先設(shè)置好的長(zhǎng)度運(yùn)行一段時(shí)間后產(chǎn)生一個(gè)溢出中斷;當(dāng)被配置為計(jì)數(shù)器時(shí),如果單片機(jī)的外部中斷引腳上檢測(cè)到一個(gè)脈沖信號(hào),該計(jì)數(shù)器加1,當(dāng)達(dá)到預(yù)先設(shè)置好事件數(shù)目時(shí),產(chǎn)生一個(gè)中斷事件。
51單片機(jī)的定時(shí)計(jì)數(shù)器由內(nèi)部寄存器和外部引腳組成,T0(P3.4)引腳和T1(P3.5)引腳用于接收外部的脈沖信號(hào)。
51單片機(jī)的52子系列還有一個(gè)和這兩個(gè)計(jì)數(shù)器功能差別較大的16位定時(shí)計(jì)數(shù)器T2,T2的最常用功能是用作串行口的波特率發(fā)生器,這部分內(nèi)容我們之后再進(jìn)行講解。
51單片機(jī)通過(guò)對(duì)相應(yīng)寄存器的操作來(lái)實(shí)現(xiàn)對(duì)定時(shí)計(jì)數(shù)器的控制,這些寄存器包括工作方式控制寄存器TMOD(Timer MODe register)和控制寄存器TCON(Timer CONtrol register),此外T0和T1還分別擁有兩個(gè)8位數(shù)據(jù)寄存器TH0、TL0和TH1、TL1。
TMOD是定時(shí)計(jì)數(shù)器的工作方式控制寄存器,通過(guò)對(duì)該寄存器的操作可以改變T0和T1的工作方式。該寄存器的內(nèi)部結(jié)構(gòu)和說(shuō)明如下圖所示,該寄存器不支持位尋址,單片機(jī)復(fù)位后被清零。
TCON是定時(shí)計(jì)數(shù)器控制寄存器,上一節(jié)講解外部中斷時(shí)中我們已經(jīng)介紹過(guò)了,其內(nèi)部結(jié)構(gòu)如表下圖所示,在51單片機(jī)復(fù)位后初始化值為所有位都清零。
TH0、TL0/TH1、TL1分別是T0/T1的數(shù)據(jù)高位/低位寄存器,均為8位。當(dāng)定時(shí)計(jì)數(shù)器收到一個(gè)驅(qū)動(dòng)事件(定時(shí)、計(jì)數(shù))后,對(duì)應(yīng)的數(shù)據(jù)寄存器的內(nèi)容加1,當(dāng)數(shù)據(jù)寄存器的值到達(dá)最大時(shí),將產(chǎn)生一個(gè)溢出中斷,在單片機(jī)復(fù)位后所有寄存器的值都被初始化為0x00,這些寄存器都不能位尋址。
51單片機(jī)定時(shí)器工作原理
定時(shí)功能:定時(shí)功能是通過(guò)計(jì)數(shù)器的計(jì)數(shù)來(lái)實(shí)現(xiàn)的。計(jì)數(shù)脈沖來(lái)自單片機(jī)內(nèi)部,每個(gè)機(jī)器周期產(chǎn)生1個(gè)計(jì)數(shù)脈沖,即每個(gè)機(jī)器周期使計(jì)數(shù)器加1。由于1個(gè)機(jī)器周期等于12個(gè)振蕩脈沖周期,所以計(jì)數(shù)器的計(jì)數(shù)頻率為振蕩器頻率的1/12。假如晶振的頻率fosc =12MHz,則計(jì)數(shù)器的計(jì)數(shù)頻率 fcont =fosc ×1/12 為1MHz,即每微秒計(jì)數(shù)器加1。這樣,單片機(jī)的定時(shí)功能就是對(duì)單片機(jī)的機(jī)器周期數(shù)進(jìn)行計(jì)數(shù)。由此可知,計(jì)數(shù)器的計(jì)數(shù)脈沖周期為:
T=1/fcont =1/(fosc ×1/12)=12/fosc
式中,fosc 為單片機(jī)振蕩器的頻率;fcont 為計(jì)數(shù)脈沖的頻率,fcont =fosc /12。在實(shí)際應(yīng)用中,可以根據(jù)計(jì)數(shù)值計(jì)算出定時(shí)時(shí)間,也可以反過(guò)來(lái)按定時(shí)時(shí)間的要求計(jì)算出計(jì)數(shù)器的初值。
單片機(jī)的定時(shí)器用于定時(shí),其定時(shí)的時(shí)間由計(jì)數(shù)初值和選擇的計(jì)數(shù)器的長(zhǎng)度(如8位、13位或16位)來(lái)確定。
計(jì)數(shù)功能:計(jì)數(shù)功能就是對(duì)外部事件進(jìn)行計(jì)數(shù)。外部事件的發(fā)生以輸入脈沖表示,因此計(jì)數(shù)功能實(shí)質(zhì)上就是對(duì)外部輸入脈沖進(jìn)行計(jì)數(shù)。STC89 系列單片機(jī)的T0(P3.4)、T1(P3.5)或T2(P1.0)信號(hào)引腳作為計(jì)數(shù)器的外部計(jì)數(shù)輸入端,當(dāng)外部輸入脈沖信號(hào)產(chǎn)生由1至0的負(fù)跳變時(shí),計(jì)數(shù)器的值加1。
在計(jì)數(shù)方式下,計(jì)數(shù)器在每個(gè)機(jī)器周期的S5P2期間,對(duì)外部脈沖輸入進(jìn)行1次采樣。如果在第1個(gè)機(jī)器周期中采樣到高電平“1”,而在第2個(gè)機(jī)器周期中采樣到1個(gè)有效負(fù)跳變脈沖,即低電平“0”,則在第3個(gè)機(jī)器周期的S3P1期間計(jì)數(shù)器加1。由此可見(jiàn),采樣1次由“1”至“0”的負(fù)跳變計(jì)數(shù)脈沖需要花費(fèi)2個(gè)機(jī)器周期,即24個(gè)振蕩器周期,故計(jì)數(shù)器的最高計(jì)數(shù)頻率為fcont =fosc ×1/24。例如,單片機(jī)的工作頻率fosc 為12MHz,則最高的采樣頻率為0.5MHz。
對(duì)外部脈沖的占空比并沒(méi)有什么限制,但外部計(jì)數(shù)脈沖的高電平和低電平保持時(shí)間均必須在1個(gè)機(jī)器周期以上,方可確保某一給定的電平在變化前至少采樣1次。
51單片機(jī)定時(shí)器模式設(shè)置
51單片機(jī)T0和T1定時(shí)器都有4種模式,由TMOD寄存器中間的M1、M0這兩位的設(shè)置來(lái)控制。寄存器配置工作模式如下圖所示:
接下來(lái)我們一起看看這些模式的設(shè)置與特點(diǎn)。
工作方式0:當(dāng)M1、M0設(shè)定為“00”時(shí),定時(shí)器工作于工作方式0,此時(shí)定時(shí)計(jì)數(shù)器的內(nèi)部結(jié)構(gòu)如圖所示。
在工作方式0下,定時(shí)器內(nèi)部計(jì)數(shù)器計(jì)數(shù)值為13位,由TH0/TH1的8位和TL0/TL1的低5位組成;當(dāng)TL0/TL1溢出時(shí)將向TH0/TH1進(jìn)位,當(dāng)TH0/TH1溢出后則產(chǎn)生相應(yīng)的溢出中斷。工作方式下的驅(qū)動(dòng)事件來(lái)源則由GATE位、C/T#位來(lái)控制。
工作方式1:當(dāng)M1、M0設(shè)定為“01”時(shí),定時(shí)器工作于工作方式1,此時(shí)定時(shí)計(jì)數(shù)器的內(nèi)部結(jié)構(gòu)如圖所示。
和工作方式0相比,工作方式1的唯一區(qū)別在于此時(shí)的內(nèi)部計(jì)數(shù)器寬度為16位,分別由TH0/TH1的8位和TL0/TL1的8位組成,其溢出方式和驅(qū)動(dòng)事件的來(lái)源和工作方式相同。51系列單片機(jī)的定時(shí)計(jì)數(shù)器采用加1計(jì)數(shù)的方式,即當(dāng)接收到一個(gè)驅(qū)動(dòng)事件時(shí)計(jì)數(shù)器加1,當(dāng)計(jì)數(shù)器溢出時(shí)則產(chǎn)生相應(yīng)的中斷請(qǐng)求,第一個(gè)驅(qū)動(dòng)事件到來(lái)時(shí)刻和中斷請(qǐng)求產(chǎn)生。
51單片機(jī)在接收到一個(gè)驅(qū)動(dòng)事件之后計(jì)數(shù)器加1,當(dāng)計(jì)數(shù)器溢出時(shí)則產(chǎn)生相應(yīng)的中斷請(qǐng)求。在定時(shí)的模式下,定時(shí)計(jì)數(shù)器的驅(qū)動(dòng)事件為單片機(jī)的機(jī)器周期,也就是外部時(shí)鐘頻率的1/12,可以根據(jù)定時(shí)器的工作原理計(jì)算出工作方式0和工作方式1下的最長(zhǎng)定時(shí)長(zhǎng)度T為:
通過(guò)對(duì)定時(shí)計(jì)數(shù)器的數(shù)據(jù)寄存器賦一個(gè)初始化值的方法可以讓定時(shí)計(jì)數(shù)器得到0到最大定時(shí)長(zhǎng)度中任意選擇的定時(shí)長(zhǎng)度,初始化值N的計(jì)算公式如下:
注意:定時(shí)計(jì)數(shù)器的工作方式0和工作方式1,不具備自動(dòng)重新裝入初始化值的功能,所以如果要想循環(huán)得到確定的定時(shí)長(zhǎng)度就必須在每次啟動(dòng)定時(shí)器之前重新初始化數(shù)據(jù)寄存器,通常是在中斷服務(wù)程序里完成這樣的工作。
工作方式2:當(dāng)M1、M0設(shè)定為“10”時(shí),定時(shí)器工作于工作方式2,此時(shí)定時(shí)計(jì)數(shù)器的內(nèi)部結(jié)構(gòu)如圖所示。
定時(shí)計(jì)數(shù)器的工作方式2和前兩種工作方式有很大的不同,工作方式2下的8位計(jì)數(shù)器的初始化數(shù)值可以被自動(dòng)重新裝入。在工作方式2下,TL0/TL1為一個(gè)獨(dú)立的8位計(jì)數(shù)器,而TH0/TH1用于存放時(shí)間常數(shù),當(dāng)T0/T1產(chǎn)生溢出中斷時(shí),TH0/TH1中的初始化數(shù)值被自動(dòng)裝入TL0/TL1中。這種方式可以大大減少程序的工作量,但是其定時(shí)長(zhǎng)度也大大減少,應(yīng)用較多的場(chǎng)合是較短的重復(fù)定時(shí)或用作串行口的波特率發(fā)生器。
工作方式3:當(dāng)M1、M0設(shè)定為“11”時(shí),定時(shí)器工作于工作方式3,此時(shí)定時(shí)計(jì)數(shù)器的內(nèi)部結(jié)構(gòu)如圖所示。
在這種工作方式下T0被拆分成了兩個(gè)獨(dú)立的8位計(jì)數(shù)器TH0和TL0,TL0使用T0本身的控制和中斷資源,而TH0則占用了T1的TR1和TF1作為啟動(dòng)控制位和溢出標(biāo)志。在這種情況下,T1將停止運(yùn)行并且其數(shù)據(jù)寄存器將保持當(dāng)前數(shù)值,所以設(shè)置T0為工作方式3也可以代替復(fù)位TR1來(lái)關(guān)閉T1定時(shí)計(jì)數(shù)器。
接下來(lái)
51單片機(jī)定時(shí)器中斷
51系列單片機(jī)內(nèi)部集成了兩個(gè)定時(shí)器/計(jì)數(shù)器,分別提供了兩個(gè)定時(shí)中斷源:TF0和TF1。
TF0中斷,定時(shí)器/計(jì)數(shù)器0中斷請(qǐng)求,其中斷請(qǐng)求號(hào)為1。
TF1中斷,定時(shí)器/計(jì)數(shù)器1中斷請(qǐng)求,其中斷請(qǐng)求號(hào)為3。
51單片機(jī)的斷控制寄存器IE中的EA位和ET0/ET1都被置“1”時(shí),定時(shí)計(jì)數(shù)器0/1的中斷被使能,在這種狀態(tài)下,如果定時(shí)計(jì)數(shù)器0/1出現(xiàn)一個(gè)計(jì)數(shù)溢出事件,則會(huì)觸發(fā)定時(shí)計(jì)數(shù)器中斷事件。可以通過(guò)修改中斷優(yōu)先級(jí)寄存器IP中的PT0/PT1位來(lái)提高定時(shí)計(jì)數(shù)器的中斷優(yōu)先級(jí),MCS-51單片機(jī)的定時(shí)計(jì)數(shù)器的中斷處理函數(shù)的結(jié)構(gòu)如下:
void 函數(shù)名(void) interrupt 中斷向量號(hào) (using 工作組寄存器)
{
//中斷代碼
}
51單片機(jī)定時(shí)器計(jì)數(shù)功能應(yīng)用
現(xiàn)在我們?cè)O(shè)計(jì)一個(gè)簡(jiǎn)單的電路來(lái)實(shí)踐一下定時(shí)器的使用方法。
如圖所示的電路中我們將T0對(duì)應(yīng)的外部脈沖引腳連接一個(gè)按鍵K5,用來(lái)測(cè)試定時(shí)器的計(jì)數(shù)功能。
普通計(jì)數(shù)器:P3.4電平變化后計(jì)數(shù)器累加,如果將計(jì)數(shù)初值設(shè)置為0xff,則每按鍵一次后計(jì)數(shù)器都會(huì)溢出,標(biāo)志位就會(huì)置位,程序中通過(guò)掃描計(jì)數(shù)器標(biāo)志位狀態(tài)來(lái)切換數(shù)字。
/*
*這是一個(gè)定時(shí)器計(jì)數(shù)應(yīng)用程序
*目的是運(yùn)用定時(shí)器計(jì)數(shù)功能進(jìn)行捕獲計(jì)數(shù)
*/
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
u8 data_L,data_H;
u8 num = 0;
u8 code num_codelist[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void delay(u8 ms);
void count_func(void);
void data_init(void);
void T0_init(void);
void display(void);
void main(void)
{
T0_init();
while(1)
{
count_func();
data_init();
display();
}
}
void delay(u8 ms)
{
u8 i,j;
for(i=0; i
}
void data_init(void)
{
data_L = num%10;
data_H = num/10;
}
void display(void)
{
P2 = 0xfe;
P0 = num_codelist[data_H];
delay(1);
P2 = 0xfd;
P0 = num_codelist[data_L];
delay(1);
}
void count_func(void)
{
//查詢(xún)標(biāo)志位
if(1 == TF0)
{
TH0 = 0xff;
TL0 = 0xff;
if(20 == num)
{
num = 0;
}
else
{
num++;
}
TF0 = 0;
}
}
void T0_init(void)
{
//設(shè)置定時(shí)器0為模式2
TMOD = 0x05;
//設(shè)置計(jì)數(shù)初值(模式2具備自動(dòng)重裝)
TH0 = 0xff;
TL0 = 0xff;
//中斷使用設(shè)置,這里只啟用定時(shí)器不使用中斷功能
ET0=0;
TR0 = 1;
EA = 0;
}
中斷計(jì)數(shù)器:這里的操作原理與以上普通程序相似,只是啟用了中斷,就不用進(jìn)行循環(huán)掃描操作了,定時(shí)器中斷進(jìn)行計(jì)數(shù),捕獲P3.4電平變化后計(jì)數(shù)器加1,當(dāng)數(shù)據(jù)寄存器數(shù)值溢出后產(chǎn)生中斷,在中斷中處理程序功能即可。
/*
*這是一個(gè)定時(shí)器中斷計(jì)數(shù)應(yīng)用程序
*目的是運(yùn)用定時(shí)器中斷功能進(jìn)行計(jì)數(shù)
*/
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
u8 data_L,data_H;
u8 num = 0;
u8 code num_codelist[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void delay(u8 ms);
//void count_func(void);
void data_init(void);
void T0_init(void);
void display(void);
void main(void)
{
T0_init();
while(1)
{
data_init();
display();
}
}
void delay(u8 ms)
{
u8 i,j;
for(i=0; i
}
void data_init(void)
{
data_L = num%10;
data_H = num/10;
}
void display(void)
{
P2 = 0xfe;
P0 = num_codelist[data_H];
delay(1);
P2 = 0xfd;
P0 = num_codelist[data_L];
delay(1);
}
void T0_init(void)
{
//設(shè)置定時(shí)器0為模式2
TMOD = 0x05;
//設(shè)置計(jì)數(shù)初值
TH0 = 0xff;
TL0 = 0xff;
//中斷使用設(shè)置,啟用中斷
ET0 = 1;
TR0 = 1;
EA = 1;
}
//void count_func(void)
//{
// if(1 == TF0) //查詢(xún)是否溢出
// {
// TH0 = 0xff; //重新賦值
// TL0 = 0xff;
// if(99 == num)
// {
// num = 0;
// }
// else
// {
// num++;
// }
// TF0 = 0;
// }
//}
void count_func(void) interrupt 1
{
//重新賦值
TH0 = 0xff;
TL0 = 0xff;
// //中斷中此位會(huì)硬件清零,這句可以不用寫(xiě)
// TF0 = 0;
// //這里相當(dāng)于對(duì)按鍵進(jìn)行消抖,實(shí)際使用時(shí)酌情使用
// delay(15);
//查詢(xún)外部輸入脈沖變化
if(0 == T0)
{
if(20 == num)
{
num = 0;
}
else
{
num++;
}
}
}
這兩個(gè)程序很大一部分都是相同的,不同之處在于計(jì)數(shù)處理部分,第一個(gè)程序沒(méi)有使用中斷,我們定義了一個(gè)void count_func(void),第二個(gè)程序中我們使用了定時(shí)器中斷,中斷函數(shù)就是void count_func(void) interrupt 1。程序的主要區(qū)別就在于這兩個(gè)函數(shù)void count_func(void)是普通函數(shù)需要定義,說(shuō)明,調(diào)用,而void count_func(void) interrupt 1是中斷函數(shù)它只需要定義就行。另外兩函數(shù)功能差異就是我們剛所講到的,一個(gè)是在主函數(shù)循環(huán)中不斷掃描寄存器標(biāo)志位,一個(gè)是中斷中查詢(xún)引腳狀態(tài)。
其他程序段都是比較簡(jiǎn)單易懂的吧,這里簡(jiǎn)單說(shuō)明一下:void data_init(void)這個(gè)函數(shù)是對(duì)顯示數(shù)據(jù)進(jìn)行個(gè)位與十位處理的。void display(void)這個(gè)函數(shù)是數(shù)碼管顯示處理函數(shù),前面講數(shù)碼管時(shí)是介紹過(guò)了,這里只是把它打包成一個(gè)函數(shù)了。void T0_init(void)這個(gè)函數(shù)是定時(shí)器T0的初始化函數(shù),里面包括定時(shí)器的模式,計(jì)數(shù)初值,中斷等設(shè)置內(nèi)容。以上程序不理解的可以留言或參考資料分析一下。最后再來(lái)看一下電路仿真情況。
兩個(gè)程序?qū)崿F(xiàn)的功能都是一樣的,現(xiàn)在想想定時(shí)器使用計(jì)數(shù)功能時(shí)中斷作用通過(guò)一些特殊處理(比如我們這里將計(jì)數(shù)初值設(shè)置為最大值0xff)是不是和前面講的外部引腳P3.3和P3.4的中斷就很相似了,按鍵按一下就會(huì)產(chǎn)生中斷。所以如果某些程序需要多個(gè)外部中斷而單片機(jī)沒(méi)有那么多中斷引腳時(shí)不妨可以使用這種方式來(lái)增加單片機(jī)功能。當(dāng)然這都是題外話(huà),等你真正做開(kāi)發(fā)時(shí)很大概率是不會(huì)用這款單片機(jī)的,現(xiàn)在的單片機(jī)功能強(qiáng)大著呢!但這是一種開(kāi)發(fā)者應(yīng)該具有的思維,也不是說(shuō)你想到了某個(gè)法子就能派上用場(chǎng),但平時(shí)積累一些“奇巧淫技”是有必要的,萬(wàn)一哪天就用上了呢!或許你的一個(gè)軟件優(yōu)化就幫公司產(chǎn)品省去了一筆費(fèi)用,如果是一個(gè)月產(chǎn)n千或n萬(wàn),甚至更高產(chǎn)量的產(chǎn)品,那老板不給你加薪還給誰(shuí)加薪呢。對(duì)于有這樣思維的,像對(duì)待自己孩子一樣對(duì)待工作的人,無(wú)論什么崗位,我認(rèn)為如果遇到了都值得成為合伙人,可以直接給股份,共創(chuàng)未來(lái)。這就是你的個(gè)人,從某種意義上來(lái)說(shuō),工作能力是職場(chǎng)的信譽(yù)值,信譽(yù)是市場(chǎng)硬通貨,信譽(yù)也是真正的財(cái)富密碼。打工時(shí)拿到好的工資是因?yàn)槔习逭J(rèn)可你所做工作,產(chǎn)品找明星代言,當(dāng)我們選擇它時(shí)很大可能是因?yàn)樾湃文莻€(gè)明星。對(duì)于信譽(yù)值達(dá)到一定程度的產(chǎn)品,它本身就成了一種信譽(yù),就像在股市你之所以買(mǎi)某只股票,很大的原因就是因?yàn)槟阊芯窟^(guò)了這家公司后得出你的信任這家公司這個(gè)結(jié)論。以上為我個(gè)人分析,當(dāng)然并不一定所有人(老板)都會(huì)這么想,但我認(rèn)為若真正信任員工,那雇傭關(guān)系轉(zhuǎn)換為具有規(guī)則契約的合作關(guān)系能取得更好成就,這是一種互信機(jī)制。這都是本人回顧曾經(jīng)“滄?!钡漠a(chǎn)生的真實(shí)理念,但往事“不堪回首”,故事就不說(shuō)了,以后有機(jī)會(huì)再交流,5年或許10年后我還會(huì)再來(lái)看這段話(huà),或許到時(shí)我也早已不是“光桿司令”了。
51單片機(jī)定時(shí)器定時(shí)功能應(yīng)用
通過(guò)以上兩個(gè)例程各位對(duì)定時(shí)器計(jì)數(shù)功能應(yīng)該都能掌握了吧,想驗(yàn)證是否掌握最快的方法就是自己親自敲一遍代碼運(yùn)行一下,看結(jié)果是否相符,如果自己敲代碼后仿真運(yùn)行或在實(shí)驗(yàn)板上沒(méi)結(jié)果時(shí)記得對(duì)照程序好好檢查一下,看是哪里出了問(wèn)題。
接下來(lái)我們繼續(xù)介紹定時(shí)器的定時(shí)功能。我們想來(lái)看一段代碼,可以嘗試一下在不看后面解析的情況下試試自己能否讀出程序的結(jié)果。
/*
*這是一個(gè)定時(shí)器定時(shí)應(yīng)用程序
*目的是運(yùn)用定時(shí)器定時(shí)模式進(jìn)行控制數(shù)碼管和LED顯示
*/
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
u8 data_L,data_H;
u8 T0_cnt = 0;
u8 T0_s = 0;
u8 T1_cnt = 0;
u8 crol = 0xfe;
u8 code num_codelist[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void delay(u8 ms);
void data_init(void);
void Timer_init(void);
void display(void);
void main(void)
{
Timer_init();
P1 = crol;
while(1)
{
data_init();
display();
}
}
void delay(u8 ms)
{
u8 i,j;
for(i=0; i
}
void data_init(void)
{
data_L = T0_s%10;
data_H = T0_s/10;
}
void display(void)
{
P2 = 0xfe;
P0 = num_codelist[data_H];
delay(1);
P2 = 0xfd;
P0 = num_codelist[data_L];
delay(1);
}
void Timer_init(void)
{
//使能中斷總開(kāi)關(guān)
EA = 1;
// 使能定時(shí)器中斷
ET0 = 1;
ET1 = 1;
// 設(shè)置工作方式1
TMOD = 0x11;
// 設(shè)置定時(shí)器0定時(shí)時(shí)間50ms
TH0 = (65536-50000)/256;
TL0 = (65536-50000)%256;
// 設(shè)置定時(shí)器1定時(shí)時(shí)間50ms
TH1 = (65536-50000)/256;
TH1 = (65536-50000)%256;
// 設(shè)置控制寄存器:?jiǎn)?dòng)定時(shí)器
TR0 = 1;
TR1 = 1;
}
void T0_func(void) interrupt 1
{
//重新賦值
TH0 = (65536-50000)/256;
TL0 = (65536-50000)%256;
//計(jì)時(shí)1s
T0_cnt++;
if(20 == T0_cnt)
{
T0_cnt = 0;
T0_s++;
if(60 == T0_s)
{
T0_s = 0;
}
}
}
void T1_func(void) interrupt 3
{
//重新賦值
TH1 = (65536-50000)/256;
TL1 = (65536-50000)%256;
//計(jì)時(shí)0.5s
T1_cnt++;
if(5 == T1_cnt)
{
T1_cnt = 0;
//燈移位
crol = _crol_(crol,1);
P1 = crol;
}
}
這個(gè)程序的功能是使用定時(shí)T0定時(shí)控制數(shù)碼管每秒變化數(shù)字,利用定時(shí)器T1控制LED每秒移位點(diǎn)亮4個(gè)燈。
先看程序運(yùn)行的結(jié)果。
現(xiàn)在來(lái)分析一下程序,程序整體來(lái)說(shuō)是非?;A(chǔ)的,很多都是出現(xiàn)過(guò)多次的程序段了,我們主要介紹一下幾個(gè)新的函數(shù)。
void Timer_init(void)這個(gè)函數(shù)是定時(shí)器T0和T1的初始化函數(shù),里面包含中斷控制位配置,數(shù)據(jù)寄存器初始化賦值以及定時(shí)器開(kāi)關(guān)設(shè)置,這里將定時(shí)器T0和T1都設(shè)置為模式1,即16位定時(shí)器功能,這種模式下定時(shí)器數(shù)據(jù)寄存器沒(méi)有自動(dòng)重裝功能,所以在每次中斷之后要進(jìn)行賦初值操作,在定時(shí)器中斷中有這段代碼,計(jì)數(shù)通過(guò)前面內(nèi)容中的公式得出,這里是設(shè)置每50ms一次中斷。
計(jì)算初值這點(diǎn)肯定很多初學(xué)者不太理解,需要多看一下資料說(shuō)明,原理其實(shí)很簡(jiǎn)單,51單片機(jī)的計(jì)算器都是向上計(jì)數(shù)的,即從0~設(shè)置的定時(shí)器數(shù)據(jù)寄存器最大值,這里是16位所以就是0xffff(十進(jìn)制就是65535),當(dāng)計(jì)數(shù)到0xffff時(shí)再記一次這個(gè)數(shù)就溢出了,就產(chǎn)生了溢出中斷,這時(shí)在中斷函數(shù)中我們將數(shù)據(jù)寄存器程序重新賦值它又重頭開(kāi)始進(jìn)行計(jì)數(shù),所以最大定時(shí)時(shí)間就是0xffff個(gè)機(jī)器周期,51單片機(jī)是一個(gè)機(jī)器周期為12個(gè)時(shí)鐘周期,如果使用12M晶振則剛好一個(gè)機(jī)器周期對(duì)應(yīng)為1us,如果我們只需定時(shí)1us數(shù)據(jù)寄存器中就賦值0xffff,即65536-1,,經(jīng)過(guò)一個(gè)機(jī)器周期就會(huì)產(chǎn)生中斷了,如果定時(shí)2us,則設(shè)置為65536-2,其他的數(shù)值以此類(lèi)推。
void T0_func(void) interrupt 1這個(gè)函數(shù)是定時(shí)器0的中斷函數(shù),首先對(duì)定時(shí)器數(shù)據(jù)寄存器進(jìn)行賦初值然后設(shè)置一個(gè)1秒的累加技術(shù)操作,使得變量T0_s在每一秒計(jì)時(shí)之后加一,加到60s時(shí)進(jìn)行清零,之后再循環(huán)計(jì)數(shù),數(shù)碼管將顯示當(dāng)前秒數(shù)值。
void T1_func(void) interrupt 3這個(gè)函數(shù)是定時(shí)器T1中斷函數(shù),這里設(shè)置一個(gè)0.25s的時(shí)間進(jìn)行LED點(diǎn)亮移位操作。
評(píng)論