有網友問到如何精確定時,實現定時的目的。在這我想談談自己的經驗和看法。最近較忙所以會分開幾次完成,如有錯誤之處也請高手們指證。 先來假設一個硬件項目,有一個硬件項目要求在P1.1上輸出0.5HZ的方波,要求精確到1us,首先可以得知高低電平各占1秒的時間。最容易想到的一個思路也許會想到用循環去做,但精度要求高,這個方法實現不了。那只有用定時器了。1us的精度那么選用12M的晶體,確定使用定時器0的模式1,這個模式下定時器0是16位定時器,也就是最大定時值為FFFFH,12M晶體的每個定時周期為1us,也就是最多可以定時FFFFH*1us=65635us,顯然用最大的值也無法一次定時為1秒,那么我們可以把一秒分幾十次,再用一變量去計數,這里分20ms,50次定時中斷后就剛好一秒。也許你很快就可以算出20ms定時中斷的定時值,FFFFH-20ms/1機器周期的時間=FFFFH-20ms/1us=B1DFH。這時可以寫下如下的程序。 //調試信息:晶體12M 編譯優化級別8級 #include <at89x51.h> unsigned int TimeCom=0; void main(void) { EA = 1; //允許CPU中斷 ET0 = 1; //定時器0中斷打開 TMOD = 0x1; //設定時器0為模式1,16位模式 TH0=0xB1; TL0=0xDF; //設定時值為20000us TR0 = 1; //開始定時
while(1); } void KeyAndDis_Time0(void) interrupt 1 { TH0=0xB1; TL0=0xDF; //設定時值 TimeCom++; if (TimeCom>49) { P1=~P1; TimeCom=0; } }
首先我們先調試每20ms中斷時的精度 設定調試晶振為12M,編譯優化為8級后,先在中斷中設定一個斷點,再運行 這時可以計下每次中斷時的時間
第一次為0.02047100 第二次為0.04048300 第三次為0.06049500 .............
可以看出每中斷一次會比定時值長了12us,那為什么會這樣呢?查看中斷程序的匯編源碼就可以得知了,原來出入堆棧要花掉一些時間。
那么我們是不是只要在定時值中減去12us就行了?原本20000這時要減去12,得出新的值為B1EBH,這時我們可以把程序改成如下的 //調試信息:晶體12M 編譯優化級別8級 #include <at89x51.h> unsigned int TimeCom=0; void main(void) { EA = 1; //允許CPU中斷 ET0 = 1; //定時器0中斷打開 TMOD = 0x1; //設定時器0為模式1,16位模式 TH0=0xB1; TL0=0xDF; //設定時值為20000us TR0 = 1; //開始定時
while(1); } void KeyAndDis_Time0(void) interrupt 1 { TH0=0xB1; TL0=0xEB; //調整定時值 TimeCom++; if (TimeCom>49) { P1=~P1; TimeCom=0; } }
再次調試后得運行時間如下: 第一次為 0.02047100 第二次為 0.04047100 第三次為 0.06047100 .................. 可以看出從第一次后每次可以保證20ms中斷一次了(這里沒有考慮第一次的時間) 現在我們可以來看看每秒時的情況。設斷點,看每次到達秒計數的時間
第一次為1.00048300 第二次為2.00048300 .................. 第359次為359.00048300 ..................
可以看出這個程序已符合設計的要求。當然這里的討論只是在軟環境下的理想晶振頻率下實現的,在現實中會因晶振偏差等因素而造成誤差。同時這樣的方法在短小簡單的程序中是比較適用,應用在多中斷或復雜的程序中同樣也會造成一定的偏差。 后記:我這種簡單的方法在實際中可能不會太實用,只是想討論一種編程的思路,也望大家指證不對之處。 明浩 2004、6、27 午
|