AVR 中 delay 函式的呼叫注意事項!delay_ns delay_ms
AVR編譯器眾多,可謂是百家齊鳴,本人獨尊WinAVR.
說明:編譯器版本WinAVR-20080610
先說winAVR的_Delay.h_肯定是在Include資料夾下了,進去一看果然有,可開啟一看,其曰:“This file has been moved to <util/delay.h>."
在util資料夾中找到delay標頭檔案如下:
--------------------------------------------------------------------------------------------------------------------------------------------
void
_delay_us(double __us)
{
uint8_t __ticks;
double __tmp = ((F_CPU) / 3e6) * __us; //3e6=3000000
if (__tmp < 1.0)
__ticks = 1;
else if (__tmp > 255)
{
_delay_ms(__us / 1000.0);
return;
}
else
__ticks = (uint8_t)__tmp;
_delay_loop_1(__ticks);
}
-----------------------------------------------------------------------------------------------------------------------------------------------
_delay_ms(double __ms)
{
uint16_t __ticks;
double __tmp = ((F_CPU) / 4e3) * __ms;
if (__tmp < 1.0)
__ticks = 1;
else if (__tmp > 65535)
{
// __ticks = requested delay in 1/10 ms
__ticks = (uint16_t) (__ms * 10.0);
while(__ticks)
{
// wait 1/10 ms
_delay_loop_2(((F_CPU) / 4e3) / 10);
__ticks --;
}
return;
}
else
__ticks = (uint16_t)__tmp;
_delay_loop_2(__ticks);
}
1、分析程式發現上面兩個子函式,分別using _delay_loop_1() and using_delay_loop2()
2、還有一點,用此標頭檔案時,必須設定主頻和優化項,否則會出現如下提示:
#ifndef F_CPU
/* prevent compiler error by supplying a default */
# warning "F_CPU not defined for <util/delay.h>"
# define F_CPU 1000000UL
#endif
#ifndef __OPTIMIZE__
# warning "Compiler optimizations disabled; functions from <util/delay.h> won't work as designed"
#endif
3、通過查詢發現_Delay_loop1()和_Delay_loop2()在檔案delay_basic.h中,如下:
/** \ingroup util_delay_basic
Delay loop using an 8-bit counter \c __count, so up to 256 iterations are possible. (The value 256 would have to be passedas 0.) The loop executes three CPU cycles per iteration, not including the overhead the compiler needs to setup the counter register.
Thus, at a CPU speed of 1 MHz, delays of up to 768 microseconds can be achieved.
*/
上面翻譯如下:
迴圈變數為8位,所以可達256(其值256和0等同),每次迴圈好執行3個CPU時鐘,不包括程式呼叫和退出該函式所花費的時間。
如此,當CPU為1MHZ時,最大延時為768us。( 3us*256)
void _delay_loop_1(uint8_t __count)
{
__asm__ volatile (
"1: dec %0" "\n\t"
"brne 1b" a a
: "=r" (__count)
: "0" (__count)
);
}
/** \ingroup util_delay_basic
Delay loop using a 16-bit counter \c __count, so up to 65536 iterations are possible. (The value 65536 would have to be passed as 0.) The loop executes four CPU cycles per iteration, not including the overhead the compiler requires to setup the counter register pair.
Thus, at a CPU speed of 1 MHz, delays of up to about 262.1 milliseconds can be achieved.
*/
上面翻譯如下:
迴圈變數為16位,所以可達65536(其值65536和0等同),每次迴圈好執行4個CPU時鐘,不包括程式呼叫和退出該函式所花費的時間。
如此,當CPU為1MHZ時,最大延時大約為262.1us。( 4us*65536)
void_delay_loop_2(uint16_t __count)
{
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__count)
: "0" (__count)
);
}
4、有了上面的基礎就不難得出
#include <util/delay_basic.h> // 標頭檔案
// _delay_loop_1(XX); // 8-bit count, 3 cycles/loop
// _delay_loop_2(XXXX); // 16-bit count, 4 cycles/loop
#include <util/delay.h> // 標頭檔案
_delay_loop_1(uint8_t __count)
1MHz時: MAX_DELAY_TIME = (1/1000000)*3*256 = 0.000768 S = 768 uS
8MHz時: MAX_DELAY_TIME = (1/8000000)*3*256 = 0.000096 S = 96 uS
............
F_CPU MAX_DELAY_TIME = (1/F_CPU)*3*256
依此類推。
_delay_loop_2(uint16_t __count)
1MHz時: MAX_DELAY_TIME = (1/1000000)*4*65535 = 0.26214 S = 262.1 mS
8MHz時: MAX_DELAY_TIME = (1/8000000)*4*65535 = 0.03277 S = 32.8 mS
............
F_CPU MAX_DELAY_TIME = (1/F_CPU)*4*65535
依此類推。
重要提示:_delay_loop_1(0)、_delay_loop_1(256)延時是一樣的!!
同理, _delay_loop_2(0)、_delay_loop_2(65536)延時也是一樣的!!這些函式的延時都是最長的延時。
重量級函數出場>>>>>>>>>>>>>_delay_us() and _delay_ms() !!!<<<<<<<<<<<<<<<<<
先說_delay_us(double __us),不要以為該函式的形參是double形就為所欲為,隨便付值都不會溢位了,其實這個函式的呼叫是有限制的,不然就會出現延時不對的情況。函 數的註釋裡說明如下:
The maximal possible delay is 768 us / F_CPU in MHz.
在1MHz時最大延時768us!!!!
也就是說double __us這個值在1M系統時鐘時最大隻能是768。如果大於768,比如這樣呼叫延時函式_delay_us(780)會怎麼樣呢??那就會和呼叫_delay_loop_1(0)一樣的效 果了!能延遲多少各位可以算出來。具體在各種系統時鐘之下這個值是多少可以通過一個公式算出來:
MAX_VALUE = 256*3000000/F_CPU
同理,分析程式,可以知道_delay_ms(double __ms)函式,在1MHz系統時鐘下其最大延時是262.14 ms!在這裡也給出該函式的形參的最大值,呼叫此函式時的實參都不要大於 這個值,大於這個限制值的話就和呼叫_delay_loop_2(0)同樣的延時效果!
MAX_VALUE = 65536*4000/F_CPU (1MHZ時,能輸入的最大值為262)
從上面可以看出來,當用延時函式時,若不加註意會出錯的(畢竟人們很難經常記住這兩個最大值),那還有什麼補償的辦法呢?
#include <util/delay_basic.h>
// _delay_loop_2(XXXX); // 16-bit count, 4 cycles/loop
// _delay_loop_1(XX); // 8-bit count, 3 cycles/loop
/*------------------------------------*/
void delay_1ms(void) //1ms延時函式 主頻為8MHz
{
_delay_loop_2(2000); // 16-bit count,4 cycles/loop
} // 2000*4/FREQ
//使用不同的晶振,可以自己來計算出()裡的值
/*-------------------------------------*/
void delay_nms(unsigned int n) //N ms延時函式
{
unsigned int i=0;
for (i=0;i<n;i++)
delay_1ms();
}
/*------------------------------------ -*/
個人的一些理解,歡迎拍磚:
(1)_delay_us(double __us)呼叫了子函式 void _delay_loop_1(uint8_t _count),uint8_t限定了_us的取值範圍不能超過255,而_us又決定了_delay_us()能延時多久的問題,具體能延時多久就根據時鐘頻率了,如上文所說(好像原文有誤 _delay_loop_1(uint8_t __count) 這個地方搞成了_delay_loop_2(uint16_t __count) ,本篇已改正):
_delay_loop_1(uint8_t __count)
1MHz時: MAX_DELAY_TIME = (1/1000000)*3*256 = 0.000768 S = 768 uS
8MHz時: MAX_DELAY_TIME = (1/8000000)*3*256 = 0.000096 S = 96 uS
............
F_CPU MAX_DELAY_TIME = (1/F_CPU)*3*256
同理,_delay_ms(double _ms)也一樣,呼叫了子函式 void _delay_loop_2(uint16_t _count),uint16_t限定了_ms的取值範圍不能超過65535,而_ms又決定了_delay_ms()能延時多久
_delay_loop_2(uint16_t __count)
1MHz時: MAX_DELAY_TIME = (1/1000000)*4*65535 = 0.26214 S = 262.1 mS
8MHz時: MAX_DELAY_TIME = (1/8000000)*4*65535 = 0.03277 S = 32.8 mS
............
F_CPU MAX_DELAY_TIME = (1/F_CPU)*4*65535
(2)AVR自帶delay函式延時1us、1ms跟晶振頻率沒有關係,晶振頻率只是決定了引數取值範圍,在計數延時時候F_CPU這個值抵消掉了。
(3)上文中的解決辦法是自己寫了個延時1ms的函式,其實跟自帶的是一樣,自帶的如下:
void _delay_ms(double __ms)
{
uint16_t __ticks;
double __tmp = ((F_CPU) / 4e3) * __ms;
if (__tmp < 1.0)
__ticks = 1;
else if (__tmp > 65535)
__ticks = 0;/* i.e. 65536 */
else
__ticks = (uint16_t)__tmp;
_delay_loop_2(__ticks);
}
綠色部分暫且不看,紅色部分即是_ticks的值,如果晶振是8M帶入(F_CPU) / 4e3既得2000,跟上文是一樣。就是自帶的函式裡面不用考慮時鐘頻率,因為它最終被約分。所以這個1ms延時不必自己寫了,我們只需要引用 _delay_ms(1);讓它延時1ms,然後再設迴圈延時nms,這樣就擺脫了 _delay_ms(double _ms)中_ms的範圍限制。
(4)另外關於延時函式正確使用的條件設定必須注意,見上篇文章: