AAVVRR 发表于 2009-5-13 17:33:03

winavr中使用_delay_ms(float x)函数的小疑问

在winavr的AVR/INCLUDE/UTIL/中有一个现成的delay.h文件用它可以在程序中方便地实现延时函数:_delay_ms(float x)和_delay_us(float x)可是在编程时包含了这个文件,实际上运行起来会发现延时误差可能很大,按说x取1000也是可以的,实际上运行结果明显小很多,程序中已经正确设置了cpu频率。winavr是20070525版。

void_c 发表于 2009-5-13 17:49:38

楼主看来没理解GCC _delay_ms用法。

snoopyzz 发表于 2009-5-13 17:52:03

-_-
F_CPU又不是不能直接改...
何苦复制文件...

lgx0301 发表于 2009-5-13 17:53:30

多此一举,根本不用去改什么。认真看看吧,多试试也可以。

snoopyzz 发表于 2009-5-13 18:04:11

我用ICC,只是从LZ提到的随便说说,WINAVR没用过...

cpcgdut 发表于 2009-5-13 18:09:25

/** \file */
/** \defgroup util_delay <util/delay.h>: Convenience functions for busy-wait delay loops
    \code
    #define F_CPU 1000000UL// 1 MHz
    //#define F_CPU 14.7456E6
    #include <util/delay.h>
    \endcode

    \note As an alternative method, it is possible to pass the
    F_CPU macro down to the compiler from the Makefile.
    Obviously, in that case, no \c \#define statement should be
    used.


    The functions in this header file are wrappers around the basic
    busy-wait functions from <util/delay_basic.h>.They are meant as
    convenience functions where actual time values can be specified
    rather than a number of cycles to wait for.The idea behind is
    that compile-time constant expressions will be eliminated by
    compiler optimization so floating-point expressions can be used
    to calculate the number of delay cycles needed based on the CPU
    frequency passed by the macro F_CPU.

    \note In order for these functions to work as intended, compiler
    optimizations <em>must</em> be enabled, and the delay time
    <em>must</em> be an expression that is a known constant at
    compile-time.If these requirements are not met, the resulting
    delay will be much longer (and basically unpredictable), and
    applications that otherwise do not use floating-point calculations
    will experience severe code bloat by the floating-point library
    routines linked into the application.

    The functions available allow the specification of microsecond, and
    millisecond delays directly, using the application-supplied macro
    F_CPU as the CPU clock frequency (in Hertz).

*/

它的意思是说,在包含头文件的语句前加上F_CPU的定义或直接在Makefile中定义
_delay_ms(double __ms)
居然用到了浮点运算...

aozima 发表于 2009-5-13 18:15:56

看delay.h中的内容:
#ifndef F_CPU
/* prevent compiler error by supplying a default */
# warning "F_CPU not defined for <util/delay.h>"
# define F_CPU 1000000UL
#endif

也就是说,如果你没有定义F_CPU 那么他就默认定义为1000000UL并给出提示!

所以你可以在程序中:
#define F_CPU8000000UL
#include <util/delay.h>
_delay_ms(5);
..................

不过我们一般不这么做..因同一个项目的F_CPU都是一样的.而且可能这个程序以后用在别的项目中会有变化
于是我们一般直接在Makefile中就定义了
编译的时候会使用命令行:avr-gcc *** -DF_CPU=8000000UL -c main.c ***
这样就相当于你的程序(main.c)已经定义了:
#define F_CPU8000000UL

关于楼上说的"居然用到了浮点运算..."请自行查阅本坛相关贴子

void_c 发表于 2009-5-13 18:25:30

居然用到了浮点运算...
——————————————————————
好像“浮点”是洪水猛兽。


IAR,GCC “浮点”延时一个简单测试:

WinAVR 20090313,OS优化,8m时钟:


#define F_CPU 8000000UL
#include <util/delay.h>

    int main(void)
    {
      
          asm volatile("nop");
          _delay_ms(1.2345);   //9877个周期,误差一个周期(1.2345ms准确延时是9876个周期)
          asm volatile("nop");
          
          while(1);
    }


IAR EWAVR 5.20 High speed优化,8m时钟:
#define F_CPU 8000000UL
#include <intrinsics.h>
#define _delay_ms(A)\
__delay_cycles( (unsigned long) ( (double)(F_CPU)*((A)/1000.0) + 0.5))


    int main(void)
    {
      
         asm("nop");
         _delay_ms(1.2345); //9876个周期,误差0
         asm("nop");
          
          while(1);
    }


————————————————————————————————
【8楼】 cpcgdut

要占用多少程序空间和数据空间先不说,红色部分恐怕已经1ms了吧。
——————————————————————————————

看楼下回复,只能说楼下太小看GCC了。



WinAVR 20090313,OS优化,8m时钟:

#define F_CPU 8000000UL
#include <util/delay.h>

    int main(void)
    {
      
          asm volatile("nop");
          _delay_ms(0.001);   //9个周期,误差一个周期(0.001ms准确延时是8个周期)
          asm volatile("nop");
          
          while(1);
    }


IAR EWAVR 5.20 High speed优化,8m时钟:
#define F_CPU 8000000UL
#include <intrinsics.h>
#define _delay_ms(A)\
__delay_cycles( (unsigned long) ( (double)(F_CPU)*((A)/1000.0) + 0.5))


    int main(void)
    {
      
         asm("nop");
         _delay_ms(0.001); //8个周期,误差0
         asm("nop");
          
          while(1);
    }

cpcgdut 发表于 2009-5-13 18:32:20

楼上,看看头文件的代码能不能实现1us的延时:

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 = 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);
}

要占用多少程序空间和数据空间先不说,红色部分恐怕已经1ms了吧。

snoopyzz 发表于 2009-5-13 18:33:46

保护压栈+参数传递+函数调用
早就超过1us了...

looker 发表于 2009-5-13 18:55:19

于是我们一般直接在Makefile中就定义了
编译的时候会使用命令行:avr-gcc *** -DF_CPU=8000000UL -c main.c ***
这样就相当于你的程序(main.c)已经定义了:
#define F_CPU8000000UL
——————————————————————————————————————————————
使用AVRStudio + WinGCC时,不用自己写makefile,只需要如下图中设置就可以了么?
http://cache.amobbs.com/bbs_upload782111/files_15/ourdev_444627.png
(原文件名:QQ截图未命名7.png)

yyccaa 发表于 2009-5-13 19:46:02

8楼和9楼显然不知道为什么delay的头文件要求开启优化的原因,建议开启优化后,读一下gcc生成的汇编代码。

AAVVRR 发表于 2009-5-13 19:55:24

以上各位大虾都有道理,小可受教了,现在的问题是,如楼上所示,已经在项目设置中指定了频率,例如400000,就用的原始文件include/util/delay.h程序中_delay_ms(1000);实际运行起来明显比1秒短很多,改了delay.h就正常了,因为以前一直用出厂默认设置1000000,所以没有问题,建议大家先实际试一下看看?

cpcgdut 发表于 2009-5-13 20:31:38

【11楼】 yyccaa

呵呵,没往下看,还要求参数必须是常量的,优化后外面那层全没了:
000001ae <DelayMS>:
1ae:        00 97               sbiw        r24, 0x00        ; 0
1b0:        39 f0               breq        .+14           ; 0x1c0 <DelayMS+0x12>
1b2:        3f ef               ldi        r19, 0xFF        ; 255
1b4:        23 2f               mov        r18, r19
1b6:        00 00               nop
1b8:        21 50               subi        r18, 0x01        ; 1
1ba:        e9 f7               brne        .-6              ; 0x1b6 <DelayMS+0x8>
1bc:        01 97               sbiw        r24, 0x01        ; 1
1be:        d1 f7               brne        .-12           ; 0x1b4 <DelayMS+0x6>
1c0:        08 95               ret

000001c2 <DelayUS>:
1c2:        88 23               and        r24, r24
1c4:        19 f0               breq        .+6              ; 0x1cc <DelayUS+0xa>
1c6:        00 00               nop
1c8:        81 50               subi        r24, 0x01        ; 1
1ca:        e9 f7               brne        .-6              ; 0x1c6 <DelayUS+0x4>
1cc:        08 95               ret

000001ce <main>:
DelayMS(500);
1ce:        84 ef               ldi        r24, 0xF4        ; 244
1d0:        91 e0               ldi        r25, 0x01        ; 1
1d2:        0e 94 d7 00         call        0x1ae        ; 0x1ae <DelayMS>
_delay_ms(1);
1d6:        80 ed               ldi        r24, 0xD0        ; 208
1d8:        97 e0               ldi        r25, 0x07        ; 7
1da:        01 97               sbiw        r24, 0x01        ; 1
1dc:        f1 f7               brne        .-4              ; 0x1da <main+0xc>

void_c 发表于 2009-5-13 20:38:44

【12楼】 AAVVRR
程序中_delay_ms(1000);实际运行起来明显比1秒短很多,
______________________________________________________________

旧版WinAVR,_delay_ms()最大延时是 262.14ms/(F_CPU/1000000),
_delay_ms(1000)超出延时范围。

新版WinAVR,_delay_ms()有最大延时是 6553.5ms,误差在1%以内。

AAVVRR 发表于 2009-5-14 10:16:45

上官先生正解!昨天去掉了20070525改装20090313最新版,又发现新的问题:用老板编一个bootload文件很正常,换新版后,提示在形成elf文件过程中程序范围超出,没办法只有换回原来的,看来延时延长不了那么多,(老板并无说明)

sophy.lin 发表于 2010-4-7 23:20:20

那不禁想到,要是在AVRStudio在直接设置晶振频率,同时也在程序中加入#define F_CPU8000000UL
如果二者的频率了一样,那编译时会默认选择哪个?

aozima 发表于 2010-4-13 23:27:03

回复【16楼】sophy.lin
那不禁想到,要是在AVRStudio在直接设置晶振频率,同时也在程序中加入#define F_CPU8000000UL
如果二者的频率了一样,那编译时会默认选择哪个?
-----------------------------------------------------------------------
#define F_CPU8000000UL
......
#define F_CPU4000000UL
......
F_CPU == ?

sophy.lin 发表于 2010-4-14 00:22:38

楼上的,麻烦解释清楚下,
就算是重复定义F_CPU,
那是不是以最近的一个为准
若是以最近的一个为准,那怎么知道到底是菜单中设置的还是代码中定义的哪个最近?

yajira 发表于 2010-4-14 09:09:37

回复【18楼】sophy.lin
楼上的,麻烦解释清楚下,
就算是重复定义F_CPU,
那是不是以最近的一个为准
若是以最近的一个为准,那怎么知道到底是菜单中设置的还是代码中定义的哪个最近?
-----------------------------------------------------------------------

大哥,delay.h里面那个定义是需要你没有定义F_CPU才会定义的呢
#ifndef
#define
宏早就防止你这种情况出现了

ilawp 发表于 2010-4-14 10:23:13

mark。我用延迟都是在不重要的场合,要准的话用定时器,要短的话加空指令自己数数

tcp1985 发表于 2010-11-11 15:31:57

先MARK了
平时我比较少用AVR的,学习一下~~

Rainfieldwood 发表于 2013-8-8 22:13:07

先Mark了,这论坛的好东西真多.学习学习
页: [1]
查看完整版本: winavr中使用_delay_ms(float x)函数的小疑问