一般來說,要取得 delay_1ms 的函數有幾種方法。
- 利用 timer 中斷來取得。
- 利用 while loop 來取得,但是要算出 while loop 的數值,通常使用示波器來輔助求得。
使用第一項方式會浪費掉一個 timer,使用第二個方法又很麻煩(因為不會用示波器)。所以我就想利用 printf 加上 timer 來算出 while loop 的數值。程式如下:
#define CNT 100
void delay_1ms(int msec)
{
int i;
while(msec--) {
i = CNT;
while(i--);
}
}
void main(void)
{
long time, cnt;
uart_init();
while (1) {
TMOD = 0x01;
TH0 = 0;
TL0 = 0;
TR0 = 1;
delay_1ms(1);
TR0 = 0;
time = (TH0*256L + TL0) * 12L / 40L;
cnt = 1000l * (long)CNT / time;
printf("\n\rtime0 = %d,%d,%ld,%ld\n\r", TH0, TL0, time, cnt);
getchar();
}
}
計算過程
- 首先 delay_1ms 中的 i 值先給 100,燒進去執行。
- 取得 TH0, TL0,利用 timer 公式 (TH0*256+TL0)*12/Fosc,計算出 100 次要花多少 t 時間。
- 再利用公式 t/i0 = 1ms/i1,i1=1ms*i0/t,取得下一次要填入的 i1 值。
- 再次重覆計算,直到 i 值不變或 t>1ms 為止。
範例:W79L632A@40MHz
- TH0=2, TL0=75, t=176.1 us, i1=567.8591 取 568。
- TH0=12, TL0=168, t=972 us, i1=584.3621 取 584。
- TH0=13, TL0=3, t=999.3 us, i1=584.4090 取 585。
- TH0=13, TL0=8, t=1000.8 us, i 值設定成 585。
- 同樣的硬體,同樣的 source code,使用 keil C51,計算出來的值卻是 711,由此可見 keil C51 跑得比較快??
- 最近又從別人的 code 發現,其實在 i=CNT 前加入 nop 指令可以用來增加準確度到 1us 喔,當然 nop 數量的多寡要經由測試才能得知,有興趣的人自己試看看吧。
測試誤差
void delay_1ms(int msec)
{
int i;
while(msec--) {
i = 585;
while(i--);
}
}
void main(void)
{
long time;
int cnt=20;
uart_init();
while (cnt--) {
TMOD = 0x01;
TH0 = 0;
TL0 = 0;
TR0 = 1;
delay_1ms(cnt);
TR0 = 0;
time = (TH0*256L + TL0) * 12L / 40L;
printf("\n\rtime0 = %d,%d,%ld,%d", TH0, TL0, time, cnt);
}
}
Keil-C 怪怪的問題
最近玩 mstar 的晶片,改用 Keil C 來測這個程式,結果發現一個怪異現象,就是每次 printf 出來的 TH0 都是 16bit 的數值,而 TL0 卻總是 0,time,cnt 算出的值也不對,真是莫明奇怪。後來發現把 TH0, TL0 放入 int 變數就正常了,如果放入 char 變數也是不對。
由此證明 keil 在 char 變數的四則運算有點問題喔。
#define CNT 100
void delay_1ms(int msec)
{
int i;
while(msec--) {
i = CNT;
while(i--);
}
}
void main(void)
{
long time, cnt;
int a, b;
uart_init();
while (1) {
TMOD = 0x01;
TH0 = 0;
TL0 = 0;
TR0 = 1;
delay_1ms(1);
TR0 = 0;
a=TH0; b=TL0;
time = (a*256L + b) * 12L / 40L;
cnt = 1000l * (long)CNT / time;
printf("\n\rtime0 = %d,%d,%ld,%ld\n\r", a, b, time, cnt);
getchar();
}
}