一般來說,要取得 delay_1ms 的函數有幾種方法。
#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();
}
}
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);
}
}
#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();
}
}
- 利用 timer 中斷來取得。
- 利用 while loop 來取得,但是要算出 while loop 的數值,通常使用示波器來輔助求得。
計算過程
- 首先 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 數量的多寡要經由測試才能得知,有興趣的人自己試看看吧。
感謝大大的demo code, 今天突然想到這樣簡單的delay function到底該怎寫才是最有效率, 時間誤差最小的寫法.上網搜尋到大大的文章.
我是用 ATMEL ATTiny85 + WinAVR compiler,測試大大說得while loop, 以及NOP 的有無, 另外最後再測試for loop迴圈做1ms這幾種寫法.
1. 基本上都是先測試調整到各種寫法能夠delay 1ms 時,誤差小於10us.
2. 測試100ms, 以及1000ms 時的誤差.
3. Code Size比較
NOTE: 測試時, compiler不做optimize.
結果:
1. 當使用while loop時, delay 1ms 都可以很準確, for loop也是. (所謂的準確定義在誤差小於10us, 因使用內部RC震盪很難在更精準)
2. 當delay 100ms(msec=100)時, 使用while loop平均都會只有92ms左右, 但是for loop約為94ms
3. 當delay 1000ms(msec=1000)時, 使用while loop平均都會只有920ms左右, 但是for loop約為940ms
4. code size 使用while loop 做跟for loop一樣的事情會多約26byte(有無NOP)
結論:
不知道是否我的測試方式有問題, 或是compiler不同造成的差異, 感覺for 的指令速度更快, 誤差率更小. 甚至code size也是. 不過看來以WinAVR來講還是用for loop 比較適用的感覺. 不過還是謝謝大大的文章, 讓我受益很多.
code:
#if 1
void Delay_1ms(int msec)
{
int i;
while(msec--) {
_NOP(); //nop有無對誤差相差不大
i = 32;
while(i--);
}
}
#else
void Delay_1ms(int msec)
{
int i;
char j;
for(i = 0; i < msec; i++)
for(j = 0; j < 82; j++)
_NOP();
}
#endif
compiler 的差異其實蠻大的,我用 KeilC 及 SDCC 在同一個板子上所得出的 CNT 數值就有很大的差異。所以要用那種方法,只有自己實驗出來最適合囉。
>> 由此證明 keil 在 char 變數的四則運算有點問題喔。
沒有問題,
1. 請改用 unsign char 運算才是正確的.
2. 就像要印出 long 變數要 %ld 一樣, printf 時請改用 %bd 來印出 char 變數