新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > AVR 中 delay 函數(shù)的調用注意事項!delay_ns delay_ms

AVR 中 delay 函數(shù)的調用注意事項!delay_ns delay_ms

作者: 時間:2016-11-23 來源:網絡 收藏

早就知道AVR的編譯器有自帶的延時子函數(shù)(或者說是頭文件),但一直沒時間一探究竟,今天終于揭開了其內幕。

AVR編譯器眾多,可謂是百家齊鳴,本人獨尊WinAVR.
說明:編譯器版本WinAVR-20080610
先說winAVR的_Delay.h_肯定是在Include文件夾下了,進去一看果然有,可打開一看,其曰:“This file has been moved to 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);
}

本文引用地址:http://butianyuan.cn/article/201611/320455.htm

-----------------------------------------------------------------------------------------------------------------------------------------------
_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、分析程序發(fā)現(xiàn)上面兩個子函數(shù),分別using _delay_loop_1() and using_delay_loop2()
2、還有一點,用此頭文件時,必須設置主頻和優(yōu)化項,否則會出現(xiàn)如下提示:
#ifndef F_CPU
/* prevent compiler error by supplying a default */
# warning "F_CPU not defined for "
# define F_CPU 1000000UL
#endif

#ifndef __OPTIMIZE__
# warning "Compiler optimizations disabled; functions from wont work as designed"
#endif
3、通過查找發(fā)現(xiàn)_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.
*/
上面翻譯如下:
循環(huán)變量為8位,所以可達256(其值256和0等同),每次循環(huán)好執(zhí)行3個CPU時鐘,不包括程序調用和退出該函數(shù)所花費的時間。
如此,當CPU為1MHZ時,最大延時為768us。( 3us*256)
void _delay_loop_1(uint8_t __count)
{
__asm__ volatile (
"1: dec %0" ""
"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.
*/
上面翻譯如下:
循環(huán)變量為16位,所以可達65536(其值65536和0等同),每次循環(huán)好執(zhí)行4個CPU時鐘,不包括程序調用和退出該函數(shù)所花費的時間。
如此,當CPU為1MHZ時,最大延時大約為262.1us。( 4us*65536)
void_delay_loop_2(uint16_t __count)
{
__asm__ volatile (
"1: sbiw %0,1" ""
"brne 1b"
: "=w" (__count)
: "0" (__count)
);
}
4、有了上面的基礎就不難得出
#include // 頭文件

// _delay_loop_1(XX); // 8-bit count, 3 cycles/loop
// _delay_loop_2(XXXX); // 16-bit count, 4 cycles/loop

#include // 頭文件

_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)延時也是一樣的??!這些函數(shù)的延時都是最長的延時。

重量級函數(shù)出場>>>>>>>>>>>>>_delay_us() and _delay_ms() !??!<<<<<<<<<<<<<<<<<

先說_delay_us(double __us),不要以為該函數(shù)的形參是double形就為所欲為,隨便付值都不會溢出了,其實這個函數(shù)的調用是有限制的,不然就會出現(xiàn)延時不對的情況。函 數(shù)的注釋里說明如下:

The maximal possible delay is 768 us / F_CPU in MHz.
在1MHz時最大延時768us!?。。?/p>

也就是說double __us這個值在1M系統(tǒng)時鐘時最大只能是768。如果大于768,比如這樣調用延時函數(shù)_delay_us(780)會怎么樣呢??那就會和調用_delay_loop_1(0)一樣的效 果了!能延遲多少各位可以算出來。具體在各種系統(tǒng)時鐘之下這個值是多少可以通過一個公式算出來:

MAX_VALUE = 256*3000000/F_CPU

同理,分析程序,可以知道_delay_ms(double __ms)函數(shù),在1MHz系統(tǒng)時鐘下其最大延時是262.14 ms!在這里也給出該函數(shù)的形參的最大值,調用此函數(shù)時的實參都不要大于 這個值,大于這個限制值的話就和調用_delay_loop_2(0)同樣的延時效果!

MAX_VALUE = 65536*4000/F_CPU (1MHZ時,能輸入的最大值為262)

從上面可以看出來,當用延時函數(shù)時,若不加注意會出錯的(畢竟人們很難經常記住這兩個最大值),那還有什么補償?shù)霓k法呢?
#include

// _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延時函數(shù) 主頻為8MHz
{
_delay_loop_2(2000); // 16-bit count,4 cycles/loop

} // 2000*4/FREQ

//使用不同的晶振,可以自己來計算出()里的值

/*-------------------------------------*/

void delay_nms(unsigned int n) //N ms延時函數(shù)
{
unsigned int i=0;
for (i=0;idelay_1ms();
}
/*------------------------------------ -*/

原文見:http://hi.baidu.com/xtuyvzkkkllstue/item/9654ea2f29450bc7ef10f1e2

個人的一些理解,歡迎拍磚:

(1)_delay_us(double __us)調用了子函數(shù)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)也一樣,調用了子函數(shù)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函數(shù)延時1us、1ms跟晶振頻率沒有關系,晶振頻率只是決定了參數(shù)取值范圍,在計數(shù)延時時候F_CPU這個值抵消掉了。

(3)上文中的解決辦法是自己寫了個延時1ms的函數(shù),其實跟自帶的是一樣,自帶的如下:

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,跟上文是一樣。就是自帶的函數(shù)里面不用考慮時鐘頻率,因為它最終被約分。所以這個1ms延時不必自己寫了,我們只需要引用_delay_ms(1);讓它延時1ms,然后再設循環(huán)延時nms,這樣就擺脫了_delay_ms(double _ms)中_ms的范圍限制。




評論


技術專區(qū)

關閉