my_avr 发表于 2007-9-7 18:25:47

总结:如何用好WinAVR里的延时函数

刚对WinAVR自带的延时函数进行一下研究,有些收获,与大家分享,不对之处请斧正,谢谢。

    先看_delay_loop_1(uint8_t __count)函数,从其函数注释里面可以了解到,该函数用来延迟3个晶振时钟周期,不包括程序调用和退出该函数所花费的时间。该函数的形参__count是一个8位的变量,由此,我们就可以根据系统采用的晶振频率算出该函数最大的延迟时间了:
1MHz时:MAX_DELAY_TIME = (1/1000000)*3*256 = 0.000768 S = 768 uS
8MHz时:MAX_DELAY_TIME = (1/8000000)*3*256 = 0.000096 S = 96uS
............
F_CPU   MAX_DELAY_TIME = (1/F_CPU)*3*256
依此类推。


    同样再看_delay_loop_2(uint16_t __count)函数,该函数延时4个晶振周期,形参是一个16位的变量,同样我们也可以算出该函数最大的延迟时间:
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.8mS
............
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

总结完毕!不正确之处还望各位不吝指正!谢谢

my_avr 发表于 2007-9-7 22:44:59

研究了半天的成果也没人顶一下?

minier 发表于 2007-9-7 23:22:08

绝对赞同,我吃过这个方面的亏

my_avr 发表于 2007-9-8 11:32:41

要沉了

loves123 发表于 2007-9-9 01:05:42

不错,不过还是要加上函数调用的时间啊。

aini 发表于 2007-9-9 06:58:13

傻瓜都知道的东西。你还以为发现了新国内,要人家怎么顶?????

armok 发表于 2007-9-9 07:02:14

谢谢经验总结与分享! Cool !

haoge 发表于 2007-9-9 07:54:51

to:5楼 不要以为自己什么都明白,都懂。


谢谢楼主的共享!

ngwang 发表于 2007-9-9 09:03:24

受教

yingying_8213 发表于 2007-9-9 10:16:54

谢谢,好东西!

avr-qq 发表于 2007-9-9 10:56:24

延时函数能带给来很方便得到延时的效果。 但比较准确的长时间的延时还是需要计时器来控制。

实际上,楼主在总结中应该说明的还有一个问题,就是最短延时时间。谢谢

my_avr 发表于 2007-9-9 13:00:38

avr-qq说的好,还有最小延时,那我再补充一下。

_delay_us(double __us)函数的最小延时为:MIN_VALUE = 3000000/F_CPU;
_delay_ms(double __ms)函数的最小延时为:MIN_VALUE = 4000/F_CPU;

如果你的延时小于这个限制值的话延时效果就是不对的。

hyz_avr 发表于 2007-9-9 13:22:33

不错..谢谢!!

aini 发表于 2007-9-9 15:47:15

/** \ingroup util_delay

    Delay loop using an 8-bit counter \c __count, so up to 256
    iterations are possible.(The value 256 would have to be passed
    as 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.
*/
/** \ingroup util_delay

    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.

*/


各位好好看看吧,delay.h文件件里写的明明白白的还研究什么呀?真的搞不懂,如此烂贴怎么搞成“酷”了。
怎么和21IC比呀

avr-qq 发表于 2007-9-13 13:08:53

搂主叙述的最小延时的论据并不足,《_delay_us(double __us)函数的最小延时为:MIN_VALUE = 3000000/F_CPU;
                                 _delay_ms(double __ms)函数的最小延时为:MIN_VALUE = 4000/F_CPU; 》
只是一个结论,没有给出理由。我粗看好像有点问题,至少没有包括 CALL,的RET 执行时间。

我想13楼的朋友给出的咚咚,也没包括这个部分的论述。请13楼的朋友出高招。谢谢

cool_hawk 发表于 2007-9-24 21:03:07

收下

sem521562 发表于 2008-8-20 10:55:08

回去试试。

bg4uvr 发表于 2008-8-20 12:04:35

这帖太牛×了,感谢楼主~

虽然我早知道我18B20的驱动程序无法在小于4MHZ时钟下正确工作的原因就是因为短延时困难,但
并不知道“_delay_us(double __us)函数的最小延时为:MIN_VALUE = 3000000/F_CPU; ”这么精
确。所以今天我只小小改动,18B20的驱动就已经可以工作在1MHZ时钟下了……

感谢~

616058952 发表于 2009-2-25 20:06:37

感谢楼主提供,

flyerhacker 发表于 2009-3-10 17:31:13

static __inline__ void
_delay_us(double __us)
{
        uint8_t __ticks;
        double __tmp = ((F_CPU) / 3e6) * __us;
        if (__tmp < 1.0)
                __ticks = 1;
        else if (__tmp > 255)
                __ticks = 0;        /* i.e. 256 */
        else
                __ticks = (uint8_t)__tmp;
        _delay_loop_1(__ticks);
}

在1M晶振下,如何实现10us以下的精确延时?????
double 型的运算是很消耗时间的。。。。。。

flyerhacker 发表于 2009-3-10 17:32:34

//时钟:1.000M

#ifndef __DELAY_1M_H
#define __DELAY_1M_H

#include <iom16v.h>
#include <macros.h>

#ifndef uint
#define uintunsigned int
#endif
#ifndef uchar
#define uchar unsigned char
#endif

void delay_nus(uint x)
{   
        if(x>=51) x=x-51;
        else if(x<25)return;
        else if(x<30)return;
        else if(x<35)return;
        else if(x<40)return;
        else if(x<45)return;
        else if(x<50)return;
        else if(x<51)return;
        x=(x>>1);
        x+=(x>>2);
        x+=(x>>4);
        x+=(x>>8);
        x=(x>>2);
        while(x--)
        {}
}   

void delay_nms(uint x)
{   
        uint i;
        while(x--)
        {
        i=164;
        while(i--);
        NOP();
        }
}

#endif

看看我的这个,我是进行过软件仿真的

flyerhacker 发表于 2009-3-10 17:53:48

我错了
./emotion/em037.gif

太小看WINAVR了

编译器进行了优化,跟比ICCAVR强得多

我的代码是在iccavr上调试的


_delay_us(double __us)
在小延时下,特性还是相当的好

不得不承认 WINAVR 太优秀了

guiltcool 发表于 2009-3-11 22:14:52

本人就没有自己写过延时函数,就用GCC的库函数。

tyff 发表于 2009-3-28 21:31:59

mark

chiwenhu 发表于 2009-3-29 09:12:59

谢谢了

sctwp 发表于 2009-3-29 09:36:29

支持这样的精神

void_c 发表于 2009-3-29 13:48:38

_dealy_us() 最小延时是3个时钟周期,
_dealy_ms() 最小延时是4+1个时钟周期。

新版WinAVR, _dealy_us(),_dealy_ms()最大延时都是6553.5ms(实际延时为6553.5+57ms左右,误差约 0.87%)
即:
_delay_us(6553500);
_delay_ms(6553.5);

51mcu2008 发表于 2009-4-2 20:46:32

感谢楼主的热心

talangtingyu 发表于 2009-4-16 15:38:05

想问下26楼新版的WINAVR最新有多新,我用的是2008年6月份版这会没有发现有这么大的延时呢?

void_c 发表于 2009-4-16 19:03:32

WinAVR2008 就是了,不过长延时是非精确延时,误差小于1%.

dongdaxing 发表于 2009-4-16 21:02:03

学习一下

xw10501 发表于 2009-4-18 16:17:29

请教下,在新版WinAVR中利用延时是不是要关掉优化呀?如果不关优化程序貌似不按正常步骤执行。

wear778899 发表于 2009-4-27 11:03:23

记号

zijian2614 发表于 2009-9-12 17:08:49

记号

albert_w 发表于 2009-9-12 18:58:23

延时是要开优化的,否则不准确

sangreal 发表于 2009-9-12 20:50:06

很好,谢谢分享!

albatross 发表于 2009-10-5 19:35:39

给大学里的学生提供了具体的解析,受益匪浅。

QQ373466062 发表于 2009-10-6 14:04:50

很好很有用,尽管我知道在头文件里有说明

eric_wang 发表于 2009-10-7 11:12:22

mark

sxndwg 发表于 2009-10-26 10:08:22

很经典的经验

youdianchedan 发表于 2009-11-18 19:55:47

鸟人,刚用到这个,哈哈,帮你挖下坟

kangkang 发表于 2009-11-22 15:21:04

受教了

wangxiaoacc 发表于 2009-11-23 14:43:33

mark

wjy6264 发表于 2009-11-27 23:57:54

mark

Think_lx 发表于 2009-11-28 22:07:19

记号

river_ucan 发表于 2009-11-29 19:10:46

mark

airwolf09921 发表于 2009-11-29 19:16:33

好东西当然要顶

xiyangzuilehdu 发表于 2010-3-25 20:19:34

mark!

jj.deng 发表于 2010-3-26 01:18:27

good~~

xgl_1982 发表于 2010-3-26 02:54:24

一直拿来就用,不知所以然。看楼主的清楚多了

jj.deng 发表于 2010-3-28 17:09:49

good~~问题虽小,但也需要我们细心研究~~

gdmfq 发表于 2010-3-28 18:55:23

good~~

beixue 发表于 2010-3-29 09:14:38

mark

sophy.lin 发表于 2010-3-29 13:19:38

以后一定会用得上,记号一下
谢谢

xingcastle 发表于 2010-6-17 10:28:12

mark

ball648500361 发表于 2010-6-17 10:38:24

mark

120908069 发表于 2010-8-28 16:38:07

mark

CHENXIAOTIAN 发表于 2010-8-28 18:17:14

顶一下

guxingganyue 发表于 2010-9-26 22:36:30

kihell 发表于 2011-2-10 11:59:52

MARK啊 本人初学 受教了啊

dfzcx 发表于 2011-2-25 11:00:22

不错

Lanou2180 发表于 2011-2-27 10:42:25

谢谢,很好,好实用!!!!!!!!mark!!!

csq463276932 发表于 2011-3-1 17:20:13

谢谢,有点了解了。

IGO_AVR 发表于 2011-3-1 17:31:00

MARK,上课去了,晚上回来看,谢谢作者分享

IGO_AVR 发表于 2011-3-15 17:21:46

问个问题:在写程序的时候一般要#define F_CPU XXXXXXX;这句话貌似没什么用啊,是用来干什么的呢?
#include <avr/io.h>
#define F_CPU 1000000
#include <util/delay.h>

int main()
{
。。

IGO_AVR 发表于 2011-3-15 17:39:22

貌似知道了,刚才没看_delay_us();
补充一点:
_delay_us(double __us)
{
        uint8_t __ticks;
        double __tmp = ((F_CPU) / 3e6) * __us;
        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);
}
F_CPU是代入_delay_us(double __us)里进行延时CLK数量计算的。
eg:#define F_CPU 16000000
   _delay_us(30);
30*(F_CPU/3*10E6)=160;
delay_loop1(160)=1/F_CPU*160*3=1/16000000*(160*3)=30us.所以#define F_CPU 这句是使用_delay_us()函数的前提。

粗略分析,望指正

IGO_AVR 发表于 2011-3-15 20:40:50

再细细分析一下,发现楼主的关于只能延时262.14 ms的说法是错误的(也可能是我理解错了楼主的意思,我不知道这句话什么意思:比如这样调用延时函数_delay_us(780)会怎么样呢??那就会和调用_delay_loop_1(0)一样的效果了!)按照此函数作者的意思,是说如果超过了MAX_VALUE = 65536*4000/F_CPU的话,将会有(1/10)*(__ms*10)ms(由WHILE循环引入,汇编是准确的延时,C语言就不一定了)的延时误差,而不是和_delay_loop_1(0)一样。所以说,即使超过了MAX_VALUE ,函数延时会有误差,但基本还可以使用。写得有点乱,可以去分析原函数。

hzn1948 发表于 2011-3-16 08:20:48

我也试过,最大延时限制不是如楼主所说
最大延时为6.5535秒,分辨率(或者最大误差)(resoiution)0.1ms

my_avr 发表于 2011-3-16 08:25:55

回复【66楼】IGO_AVR
-----------------------------------------------------------------------

回复【67楼】hzn1948
-----------------------------------------------------------------------

我用的GCC版本可能和你们现在用的版本不一样,我用的版本很旧了(WinAVR-20060421),所以提供的延时函数不一定相同,请注意查看帮助手册。

woshizhuyumi 发表于 2011-3-25 15:04:32

回复【68楼】my_avr
-----------------------------------------------------------------------

楼主好,我在用mega128的时候,调用到了delay_ms(50000);但是winavr2010不但不执行,还停在了这句上,不往下执行,不知是为何
int main(void)
{

        init();
        _delay_ms(50000);
        change();                        //chagne在此处不行,不能实现stop功能,轮子都转,只实现init
       

        if(1)
        {
       OCR0=0x00;                //左正
       OCR1A=0x0000;
       
       OCR2=0x00;                 //右反
       OCR1B=0X0000;
        }


}

IGO_AVR 发表于 2011-5-1 11:03:30

再次碰到WINAVR里的掩饰函数的问题,之前以为自己已经清楚了,现在又卡住了,唉~,问题如下:
我想用中断来改变PORTA口的电平而达到输出脉冲的效果,可是当我的源程序像下面一样的时候,可以只看问题所在句^-^。示波器读出来的波形高电平一直延时500US,但是当我将问题所在句的n直接改成5的话,就延时5us了,不知道原因,还有,问个AVR调试时候的问题,为什么在调试的时候全速运行比我不调试全速运行慢很多呢,比方说,我下面的程序讲问题所在句改成5之后,在调试状态下全速运行,脉冲频率为1HZ.......,而脱机运行时频率就有200K了?
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 7372800
#include <util/delay.h>

volatile unsigned char n=50;
unsigned char phase={0x90,0x09,0x60,0x06};
void Motor_Config();
void StepMotor_Config();
/**********************************************************

延时函数

**********************************************************/

/**********************************************************

主函数

**********************************************************/
intmain(void)
{
unsigned char i=0;
sei();
MCUCR|=(1<<ISC11);
GICR|=1<<INT1;
DDRD|=0X80;
PORTD|=(1<<PD2)|(1<<PD3);
DDRC=0XFF;
StepMotor_Config();
while(1)
        {
/*        for(i=0;i<4;i++)
        {
        PORTA=phase;
        _delay_us(n);
        }
        */
        PORTA=0XFF;
        _delay_us(n);               //问题所在句。。。。注意//
        PORTA=0X00;
        _delay_us(n);               //问题所在句。。。。注意//

        }


}


void Motor_Config()
{
PORTA=0X00;
DDRA=0XFF;
PORTC=0XFF;
DDRC=0XFF;
PORTB=0XFF;
DDRB=0XFF;

sei();
MCUCR|=(1<<ISC11);
GICR|=1<<INT1;
DDRD|=0X80;
TCCR2 = (1<<WGM21)|(1<<WGM20)|(0<<CS22)|(1<<CS21)|(0<<CS20)|(1<<COM21)|(0<<COM20);
OCR2= 200;
}


void StepMotor_Config()
{
PORTB=0XFF;
DDRB=0XFF;
PORTA=0X00;
DDRA=0XFF;
}


ISR(INT1_vect)
{
cli();
OCR2+=10;
if(OCR2>240)
        OCR2=0;
        PORTC=~PORTC;
       
n+=10;                     //问题所在句。。。。注意//
//while(1);
_delay_ms(10);
sei();
}

hzn1948 发表于 2011-5-2 09:14:41

假如调试时按真实频率运行的话,又如何能调试呢?
另外,中断程序里用豪秒级延时----此路不通

polun2011 发表于 2011-10-16 12:23:03

mark

Lanou2180 发表于 2012-2-12 10:44:58

mark!!!!!!!!!!

sweet_136 发表于 2012-2-12 10:52:48

mark

shin555 发表于 2012-2-16 17:29:17

谢谢分享经验

lz80650904 发表于 2012-2-16 22:37:03

顶起,要研究下

shin555 发表于 2012-4-21 01:31:14

{:victory:}

lryxr2507 发表于 2012-6-1 08:59:39

这是好资料呀,我已经收藏,多谢了.

zhangtomcom 发表于 2012-8-23 17:49:45

谢谢,学习了

yamp3 发表于 2012-9-6 22:01:39

有些收获 ,很好,谢谢分享!

amnumber 发表于 2012-9-13 10:58:36

这东西什么时候都有用

Grampus 发表于 2012-10-15 16:10:20

留着备用

xaftp 发表于 2012-10-15 17:52:45

很好,使新手可以少走弯路!感谢了!

BG2UKY 发表于 2012-10-15 22:00:56

很经典的经验
页: [1]
查看完整版本: 总结:如何用好WinAVR里的延时函数