总结:如何用好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
总结完毕!不正确之处还望各位不吝指正!谢谢 研究了半天的成果也没人顶一下? 绝对赞同,我吃过这个方面的亏 要沉了 不错,不过还是要加上函数调用的时间啊。 傻瓜都知道的东西。你还以为发现了新国内,要人家怎么顶????? 谢谢经验总结与分享! Cool ! to:5楼 不要以为自己什么都明白,都懂。
谢谢楼主的共享! 受教 谢谢,好东西! 延时函数能带给来很方便得到延时的效果。 但比较准确的长时间的延时还是需要计时器来控制。
实际上,楼主在总结中应该说明的还有一个问题,就是最短延时时间。谢谢 avr-qq说的好,还有最小延时,那我再补充一下。
_delay_us(double __us)函数的最小延时为:MIN_VALUE = 3000000/F_CPU;
_delay_ms(double __ms)函数的最小延时为:MIN_VALUE = 4000/F_CPU;
如果你的延时小于这个限制值的话延时效果就是不对的。 不错..谢谢!! /** \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比呀 搂主叙述的最小延时的论据并不足,《_delay_us(double __us)函数的最小延时为:MIN_VALUE = 3000000/F_CPU;
_delay_ms(double __ms)函数的最小延时为:MIN_VALUE = 4000/F_CPU; 》
只是一个结论,没有给出理由。我粗看好像有点问题,至少没有包括 CALL,的RET 执行时间。
我想13楼的朋友给出的咚咚,也没包括这个部分的论述。请13楼的朋友出高招。谢谢 收下 回去试试。 这帖太牛×了,感谢楼主~
虽然我早知道我18B20的驱动程序无法在小于4MHZ时钟下正确工作的原因就是因为短延时困难,但
并不知道“_delay_us(double __us)函数的最小延时为:MIN_VALUE = 3000000/F_CPU; ”这么精
确。所以今天我只小小改动,18B20的驱动就已经可以工作在1MHZ时钟下了……
感谢~ 感谢楼主提供, 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 型的运算是很消耗时间的。。。。。。 //时钟: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
看看我的这个,我是进行过软件仿真的 我错了
./emotion/em037.gif
太小看WINAVR了
编译器进行了优化,跟比ICCAVR强得多
我的代码是在iccavr上调试的
_delay_us(double __us)
在小延时下,特性还是相当的好
不得不承认 WINAVR 太优秀了 本人就没有自己写过延时函数,就用GCC的库函数。 mark 谢谢了 支持这样的精神 _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); 感谢楼主的热心 想问下26楼新版的WINAVR最新有多新,我用的是2008年6月份版这会没有发现有这么大的延时呢? WinAVR2008 就是了,不过长延时是非精确延时,误差小于1%. 学习一下 请教下,在新版WinAVR中利用延时是不是要关掉优化呀?如果不关优化程序貌似不按正常步骤执行。 记号 记号 延时是要开优化的,否则不准确 很好,谢谢分享! 给大学里的学生提供了具体的解析,受益匪浅。 很好很有用,尽管我知道在头文件里有说明 mark 很经典的经验 鸟人,刚用到这个,哈哈,帮你挖下坟 受教了 mark mark 记号 mark 好东西当然要顶 mark! good~~ 一直拿来就用,不知所以然。看楼主的清楚多了 good~~问题虽小,但也需要我们细心研究~~ good~~ mark 以后一定会用得上,记号一下
谢谢 mark mark mark 顶一下 顶 MARK啊 本人初学 受教了啊 不错 谢谢,很好,好实用!!!!!!!!mark!!! 谢谢,有点了解了。 MARK,上课去了,晚上回来看,谢谢作者分享 问个问题:在写程序的时候一般要#define F_CPU XXXXXXX;这句话貌似没什么用啊,是用来干什么的呢?
#include <avr/io.h>
#define F_CPU 1000000
#include <util/delay.h>
int main()
{
。。 貌似知道了,刚才没看_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()函数的前提。
粗略分析,望指正 再细细分析一下,发现楼主的关于只能延时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 ,函数延时会有误差,但基本还可以使用。写得有点乱,可以去分析原函数。 我也试过,最大延时限制不是如楼主所说
最大延时为6.5535秒,分辨率(或者最大误差)(resoiution)0.1ms 回复【66楼】IGO_AVR
-----------------------------------------------------------------------
回复【67楼】hzn1948
-----------------------------------------------------------------------
我用的GCC版本可能和你们现在用的版本不一样,我用的版本很旧了(WinAVR-20060421),所以提供的延时函数不一定相同,请注意查看帮助手册。 回复【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;
}
} 再次碰到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();
} 假如调试时按真实频率运行的话,又如何能调试呢?
另外,中断程序里用豪秒级延时----此路不通 mark mark!!!!!!!!!! mark 谢谢分享经验 顶起,要研究下 {:victory:} 这是好资料呀,我已经收藏,多谢了. 谢谢,学习了 有些收获 ,很好,谢谢分享! 这东西什么时候都有用 留着备用 很好,使新手可以少走弯路!感谢了! 很经典的经验
页:
[1]