搜索
bottom↓
回复: 84

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

[复制链接]

出0入42汤圆

发表于 2007-9-7 18:25:47 | 显示全部楼层 |阅读模式
刚对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 = 96  uS
............
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.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

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

出0入42汤圆

 楼主| 发表于 2007-9-7 22:44:59 | 显示全部楼层
研究了半天的成果也没人顶一下?

出0入0汤圆

发表于 2007-9-7 23:22:08 | 显示全部楼层
绝对赞同,我吃过这个方面的亏

出0入42汤圆

 楼主| 发表于 2007-9-8 11:32:41 | 显示全部楼层
要沉了

出0入0汤圆

发表于 2007-9-9 01:05:42 | 显示全部楼层
不错,不过还是要加上函数调用的时间啊。

出0入0汤圆

发表于 2007-9-9 06:58:13 | 显示全部楼层
傻瓜都知道的东西。你还以为发现了新国内,要人家怎么顶?????
头像被屏蔽

出0入0汤圆

发表于 2007-9-9 07:02:14 | 显示全部楼层
谢谢经验总结与分享! Cool !

出0入0汤圆

发表于 2007-9-9 07:54:51 | 显示全部楼层
to:5楼 不要以为自己什么都明白,都懂。


谢谢楼主的共享!

出0入0汤圆

发表于 2007-9-9 09:03:24 | 显示全部楼层
受教

出0入0汤圆

发表于 2007-9-9 10:16:54 | 显示全部楼层
谢谢,好东西!

出0入0汤圆

发表于 2007-9-9 10:56:24 | 显示全部楼层
延时函数能带给来很方便得到延时的效果。 但比较准确的长时间的延时还是需要计时器来控制。

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

出0入42汤圆

 楼主| 发表于 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;

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

出0入0汤圆

发表于 2007-9-9 13:22:33 | 显示全部楼层
不错..谢谢!!

出0入0汤圆

发表于 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比呀

出0入0汤圆

发表于 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楼的朋友出高招。谢谢

出0入0汤圆

发表于 2007-9-24 21:03:07 | 显示全部楼层
收下

出0入0汤圆

发表于 2008-8-20 10:55:08 | 显示全部楼层
回去试试。

出0入0汤圆

发表于 2008-8-20 12:04:35 | 显示全部楼层
这帖太牛×了,感谢楼主~

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

感谢~

出0入0汤圆

发表于 2009-2-25 20:06:37 | 显示全部楼层
感谢楼主提供,

出0入0汤圆

发表于 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 型的运算是很消耗时间的。。。。。。

出0入0汤圆

发表于 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 uint  unsigned 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

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

出0入0汤圆

发表于 2009-3-10 17:53:48 | 显示全部楼层
我错了  


太小看WINAVR了

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

我的代码是在iccavr上调试的


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

不得不承认 WINAVR 太优秀了

出0入0汤圆

发表于 2009-3-11 22:14:52 | 显示全部楼层
本人就没有自己写过延时函数,就用GCC的库函数。

出0入0汤圆

发表于 2009-3-28 21:31:59 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-3-29 09:12:59 | 显示全部楼层
谢谢了

出0入0汤圆

发表于 2009-3-29 09:36:29 | 显示全部楼层
支持这样的精神

出0入0汤圆

发表于 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);

出0入0汤圆

发表于 2009-4-2 20:46:32 | 显示全部楼层
感谢楼主的热心

出0入0汤圆

发表于 2009-4-16 15:38:05 | 显示全部楼层
想问下26楼新版的WINAVR最新有多新,我用的是2008年6月份版这会没有发现有这么大的延时呢?

出0入0汤圆

发表于 2009-4-16 19:03:32 | 显示全部楼层
WinAVR2008 就是了,不过长延时是非精确延时,误差小于1%.

出0入0汤圆

发表于 2009-4-16 21:02:03 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2009-4-18 16:17:29 | 显示全部楼层
请教下,在新版WinAVR中利用延时是不是要关掉优化呀?如果不关优化程序貌似不按正常步骤执行。

出0入0汤圆

发表于 2009-4-27 11:03:23 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-9-12 17:08:49 | 显示全部楼层
记号

出0入42汤圆

发表于 2009-9-12 18:58:23 | 显示全部楼层
延时是要开优化的,否则不准确

出0入0汤圆

发表于 2009-9-12 20:50:06 | 显示全部楼层
很好,谢谢分享!

出0入0汤圆

发表于 2009-10-5 19:35:39 | 显示全部楼层
给大学里的学生提供了具体的解析,受益匪浅。

出0入0汤圆

发表于 2009-10-6 14:04:50 | 显示全部楼层
很好很有用,尽管我知道在头文件里有说明

出0入0汤圆

发表于 2009-10-7 11:12:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-26 10:08:22 | 显示全部楼层
很经典的经验

出0入0汤圆

发表于 2009-11-18 19:55:47 | 显示全部楼层
鸟人,刚用到这个,哈哈,帮你挖下坟

出0入0汤圆

发表于 2009-11-22 15:21:04 | 显示全部楼层
受教了

出0入0汤圆

发表于 2009-11-23 14:43:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-27 23:57:54 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-28 22:07:19 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-11-29 19:10:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-29 19:16:33 | 显示全部楼层
好东西当然要顶

出0入0汤圆

发表于 2010-3-25 20:19:34 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-3-26 01:18:27 | 显示全部楼层
good~~

出0入0汤圆

发表于 2010-3-26 02:54:24 | 显示全部楼层
一直拿来就用,不知所以然。看楼主的清楚多了

出0入0汤圆

发表于 2010-3-28 17:09:49 | 显示全部楼层
good~~问题虽小,但也需要我们细心研究~~

出0入0汤圆

发表于 2010-3-28 18:55:23 | 显示全部楼层
good~~

出0入0汤圆

发表于 2010-3-29 09:14:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-29 13:19:38 | 显示全部楼层
以后一定会用得上,记号一下
谢谢

出0入0汤圆

发表于 2010-6-17 10:28:12 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-17 10:38:24 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-28 16:38:07 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-28 18:17:14 | 显示全部楼层
顶一下

出0入0汤圆

发表于 2010-9-26 22:36:30 | 显示全部楼层

出0入0汤圆

发表于 2011-2-10 11:59:52 | 显示全部楼层
MARK啊 本人初学 受教了啊

出0入0汤圆

发表于 2011-2-25 11:00:22 | 显示全部楼层
不错

出0入0汤圆

发表于 2011-2-27 10:42:25 | 显示全部楼层
谢谢,很好,好实用!!!!!!!!mark!!!

出0入0汤圆

发表于 2011-3-1 17:20:13 | 显示全部楼层
谢谢,有点了解了。

出0入0汤圆

发表于 2011-3-1 17:31:00 | 显示全部楼层
MARK,上课去了,晚上回来看,谢谢作者分享

出0入0汤圆

发表于 2011-3-15 17:21:46 | 显示全部楼层
问个问题:在写程序的时候一般要#define F_CPU XXXXXXX;这句话貌似没什么用啊,是用来干什么的呢?
#include <avr/io.h>
#define F_CPU 1000000
#include <util/delay.h>

int main()
{
。。

出0入0汤圆

发表于 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()函数的前提。

粗略分析,望指正

出0入0汤圆

发表于 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 ,函数延时会有误差,但基本还可以使用。写得有点乱,可以去分析原函数。

出0入0汤圆

发表于 2011-3-16 08:20:48 | 显示全部楼层
我也试过,最大延时限制不是如楼主所说
最大延时为6.5535秒,分辨率(或者最大误差)(resoiution)0.1ms

出0入42汤圆

 楼主| 发表于 2011-3-16 08:25:55 | 显示全部楼层
回复【66楼】IGO_AVR
-----------------------------------------------------------------------

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

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

出0入0汤圆

发表于 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;
        }


}

出0入0汤圆

发表于 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[4]={0x90,0x09,0x60,0x06};
void Motor_Config();
void StepMotor_Config();
/**********************************************************

延时函数

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

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

主函数

**********************************************************/
int  main(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();
}

出0入0汤圆

发表于 2011-5-2 09:14:41 | 显示全部楼层
假如调试时按真实频率运行的话,又如何能调试呢?
另外,中断程序里用豪秒级延时----此路不通

出0入0汤圆

发表于 2011-10-16 12:23:03 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-12 10:44:58 | 显示全部楼层
mark!!!!!!!!!!

出0入0汤圆

发表于 2012-2-12 10:52:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-16 17:29:17 | 显示全部楼层
谢谢分享经验

出0入0汤圆

发表于 2012-2-16 22:37:03 | 显示全部楼层
顶起,要研究下

出0入0汤圆

发表于 2012-4-21 01:31:14 | 显示全部楼层

出0入0汤圆

发表于 2012-6-1 08:59:39 | 显示全部楼层
这是好资料呀,我已经收藏,多谢了.

出0入0汤圆

发表于 2012-8-23 17:49:45 | 显示全部楼层
谢谢,学习了

出0入0汤圆

发表于 2012-9-6 22:01:39 | 显示全部楼层
有些收获 ,很好,谢谢分享!

出0入0汤圆

发表于 2012-9-13 10:58:36 | 显示全部楼层
这东西什么时候都有用

出0入0汤圆

发表于 2012-10-15 16:10:20 | 显示全部楼层
留着备用

出0入0汤圆

发表于 2012-10-15 17:52:45 | 显示全部楼层
很好,使新手可以少走弯路!感谢了!

出0入0汤圆

发表于 2012-10-15 22:00:56 | 显示全部楼层
很经典的经验
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-7 18:54

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表