利用89C51設計一個(ge) 簡易日曆時鍾係統,時鍾係統硬件主要由單片機控製的計時電路、複位等輔助電路、按鍵電路、數碼管顯示電路、電源係統等組成。日曆時鍾可以顯示年、月、時、分、秒;可以設置年、月、時、分 其中計時控製電路由AT89C51單片機控製;按鍵電路包含時間設置;時間顯示屏電路由7個(ge) 數碼管組成;電源係統由小功率整流濾波穩壓電路組成,輸出直流電壓5 V,向主電路及顯示電路供電。係統框圖如圖1所示。
圖1 日曆時鍾係統框圖
在計時過程中,係統利用89C51自身的計時器T0作為(wei) 時鍾基準,計時器中斷的準確度直接關(guan) 係到整個(ge) 係統的精度,因此獲取精確的定時時鍾信號成為(wei) 該係統的關(guan) 鍵。MCS-51單片機內(nei) 有2個(ge) 可編程的16位定時器/計數器,在本係統設計中采用AT89C51的定時器T0,並工作在方式1下,晶振頻率為(wei) 12 MHz。
1 T0定時中斷
定時器/計數器T0工作方式1的電路邏輯結構如圖2所示。T0定時特性功能寄存器由TL0(低8位)和TH0(高8位)構成。特殊功能寄存器TMOD控製定時寄存器的工作方式;TCON則用於(yu) 控製定時器T0和T1的啟動和停止計數,同時管理定時器T0和T1的溢出標誌等。程序開始時需對TL0和TH0進行初始化編程,以定義(yi) 它們(men) 的工作方式,並控製T0和T1的計數。在係統的設計中,計時單位以s為(wei) 基準,並要求日誤差≤10 s,如果用循環去做,無法滿足精度要求。選用12 MHz的晶體(ti) 可得到1 s的精度,經分析確定使用定時器0的方式1。這個(ge) 方式下,定時器0是16位定時器,也就是最大定時值為(wei) FFFFH,12 MHz晶體(ti) 的每個(ge) 定時周期為(wei) 1 s,最多可以定時FFFFH×1 s=65635 us,即使使用最大值也無法一次定時1 s,設計中使用1次定時20 ms,50次定時中斷得到1 s。20 ms定時中斷的定時值為(wei) :FFFFH-20 ms/1 s= B1DFH。
圖2 定時器/計數器工作方式1邏輯結構
2 程序測試與(yu) 調整
在Keil uVision3平台下利用C語言實現如下代碼:
#include<reg52.h>
#define uchar unsigned char
uchar data MScond= 0;//ms
uchar data Scond= 0; //s
uehar data M inute= 0;//min
uchar data Hour= 0;//h
void main(void){
EA =1; //允許CPU中斷
ET0 = 1; //定時器0中斷打開
TMOD =0x1; //設定時器0為(wei) 方式1
TH0= 0xB1;
TL0= 0xDF; //設定時值為(wei) 20 000 us(20 ms)
TR0 = 1; //開始定時
while(1);
}
void Time0(void) interrupt 1 using 1
{
TH0=0xB1; //20 ms斷點 (1)
TL0=0xDF; //設定時值
MScond= MScond+ 1;
if(MScond == 50)
{MScond=0;
Scond= Scond+ 1;
if(Scond == 60)
{Scond=0;
Minute=Minute+1; //分斷點 (2)
if(Minute == 60)
{Minute=0;
Hour=Hour+1; //d,時斷點 (3)
if(Hour == 24)
{ Hour=0; }}}}
首先調試每20 ms中斷時的精度,在選項中設定調試晶振為(wei) 12 MHz,在(1)處設置一個(ge) 斷點再運行,這時記錄下每次中斷時的時間,如圖3所示。在初始化中費時為(wei) 551 s,每一次中斷時間應該考慮該項的影響。在實際處理中可以利用兩(liang) 次中斷時間的差來作為(wei) 定時器的中斷時
間間隔。
通過測試,得到第一次為(wei) 0.020 568 00 s,第二次為(wei) 0.040 580 00 s,第三次為(wei) 0.060 592 00 s。可以看出,每中斷一次會(hui) 比定時值長了12 s。如果將斷點設定在(2)處,並通過Logic Analyzer tool,得到分鍾第一次中斷的時間為(wei) 60.036 57 S,第二次中斷的時間為(wei) 120.072 57 s,則每分鍾的實際時間為(wei) 60.036 S。再將斷點設定在(3)處,得到小時第一次中斷的時間為(wei) 3 602.160 576 S,第二次中斷的時間為(wei) 7204.320 576 S,可以得到小時的實際時間為(wei) 3602.16 S,如圖4所示。
為(wei) 什麽(me) 會(hui) 產(chan) 生這些誤差呢?通過對中斷程序的匯編源碼進行分析,實際上中斷程序入堆棧時使用了兩(liang) 條語句:PUSH ACC和PUSH PSW。執行人棧指令花費了4個(ge) 機器周期,加上重新對TH0和TL0的加載又用去2個(ge) 機器周期,計數值加1花費了2個(ge) 機器周期,中斷返回約4個(ge) 機器周期共約12個(ge) 機器周期。為(wei) 了消除這些因素的影響,需要在對T0設置計數值時減去12個(ge) 機器周期,將計算得到的初始值B1DFH加上12(0CH)得到:B1DFH+12=B1EBH作為(wei) 新的定時器初值,修改後的程序為(wei) :
#include<reg52.h>
#define uchar unsigned char
uchar data MScond=0; //ms
uchar data Scond=0; //s
uchar data Minute=0; //min
uchar data Hour=0; //h
void main(void){
EA = 1; //允許CPU 中斷
ET0= 1; //定時器0中斷打開
TMOD = 0x1; //設定時器0為(wei) 方式1
TH0= 0xB1;
TL0=0xEB; //設定時值為(wei) 20 000 (20 ms)減去12
TR0= 1; //開始定時
while(1);
}
void Time0(void) interrupt 1 using 1
{TH0=0xB1; //20 ms斷點 (1)
TL0=0xDF; //設定時值
MScond= MScond+ 1;
if(MScond == 50)
{MScond=0;
Scond= Scond+ 1;
if rScond == 60)
{Scond=0;
Minute=Minute+1; //分斷點 (2)
if(Minute == 60)
{Minute=0;
Hour=Hour+1; //時斷點 (3)
if(Hour == 24)
{ Hour=0;}}}}
重新調試程序,仍然在選項中設定調試晶振為(wei) 12 MHz,重新測試20 ms定時器的實際時間,在(1)處設置一個(ge) 斷點後運行,重新記錄下每次中斷時的時間,如圖5所示。初始化時間為(wei) 556 s,為(wei) 消除其影響,使用兩(liang) 次中斷時間間隔來作為(wei) 定時器實際獲得的基準時鍾。得到一次中斷時的時間為(wei) 0.020 556 00 S,第二次為(wei) 0.040 556 000,第二次為(wei) 0.060 556 00 s,可以看出每次中斷間隔剛好20 ms。如果將斷點設定在(2)處.並通過Logic Analyzer tool,得到第一次中斷時時間為(wei) 60.000 57 s,第二次為(wei) 120.000 57 s,間隔剛好60 s。將斷點設定在(3)處,得到第一次中斷的時間為(wei) 3600.000 578 s,第二次中斷時間為(wei) 7200.000 578 s,時間間隔為(wei) 3 600 s,測試結果如圖6所示,完全可以滿足係統設計的需要。
3 總結
通過對定時器的誤差分析和校正.可以提高係統的精確度。當然.上麵的分析是在軟環境下理想晶振頻率下實現的,在現實中會(hui) 因晶振偏差等因素而造成誤差。在該測試中,主程序沒有進行其他處理,而在日曆設計中還要涉及到計時器T1的中斷來完成對掃描顯示電路的處理,還包括外部中斷對時鍾進行了調整,加上一些鬧鍾功能,這必然會(hui) 對T0的定時精確性產(chan) 生影響。另外.當中斷程序中語句越多,占用的機器周期也越多,因此在設計中應充分利用Keil uVision3的分析工具,通過多次調整計數初值以獲取精確的時鍾信號,這對於(yu) 要求精確時鍾信號的應用具有重要的意義(yi) 。