AVR 中 delay 函数的调用注意事项!delay_ns delay_ms

发布者:xxoke624最新更新时间:2016-01-11 来源: eefocus关键字:AVR  delay  函数  注意事项 手机看文章 扫描二维码
随时随地手机看文章

早就知道AVR的编译器有自带的延时子函数(或者说是头文件),但一直没时间一探究竟,今天终于揭开了其内幕。

AVR编译器众多,可谓是百家齐鸣,本人独尊WinAVR.
说明:编译器版本WinAVR-20080610
先说winAVR的_Delay.h_肯定是在Include文件夹下了,进去一看果然有,可打开一看,其曰:“This file has been moved to ."
在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);
}

-----------------------------------------------------------------------------------------------------------------------------------------------
_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、分析程序发现上面两个子函数,分别using _delay_loop_1() and using_delay_loop2()
2、还有一点,用此头文件时,必须设置主频和优化项,否则会出现如下提示:
#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 won't work as designed"
#endif
3、通过查找发现_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.
*/
上面翻译如下:
循环变量为8位,所以可达256(其值256和0等同),每次循环好执行3个CPU时钟,不包括程序调用和退出该函数所花费的时间。
如此,当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.
*/
上面翻译如下:
循环变量为16位,所以可达65536(其值65536和0等同),每次循环好执行4个CPU时钟,不包括程序调用和退出该函数所花费的时间。
如此,当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)延时也是一样的!!这些函数的延时都是最长的延时。

重量级函数出场>>>>>>>>>>>>>_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 (1MHZ时,能输入的最大值为262)

从上面可以看出来,当用延时函数时,若不加注意会出错的(毕竟人们很难经常记住这两个最大值),那还有什么补偿的办法呢?
#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延时函数 主频为8MHz

_delay_loop_2(2000); // 16-bit count,4 cycles/loop

} // 2000*4/FREQ

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

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

void delay_nms(unsigned int n) //N ms延时函数 

unsigned int i=0; 
for (i=0;i delay_1ms(); 

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

 

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

个人的一些理解,欢迎拍砖:

(1)_delay_us(double __us)调用了子函数 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)也一样,调用了子函数 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函数延时1us、1ms跟晶振频率没有关系,晶振频率只是决定了参数取值范围,在计数延时时候F_CPU这个值抵消掉了。

(3)上文中的解决办法是自己写了个延时1ms的函数,其实跟自带的是一样,自带的如下:

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,跟上文是一样。就是自带的函数里面不用考虑时钟频率,因为它最终被约分。所以这个1ms延时不必自己写了,我们只需要引用 _delay_ms(1);让它延时1ms,然后再设循环延时nms,这样就摆脱了 _delay_ms(double _ms)中_ms的范围限制。


关键字:AVR  delay  函数  注意事项 引用地址:AVR 中 delay 函数的调用注意事项!delay_ns delay_ms

上一篇:AVR外部晶振是否起振的测试小程序
下一篇:基于WINAVR + avr studio 4 使用外部晶振时及延时可能遇到的问题

推荐阅读最新更新时间:2024-03-16 14:43

使用AVR单片机驱动舵机
1.舵机驱动的基本原理   (可以参考http://blog.sina.com.cn/s/blog_8240cbef01018hu1.html)    控制信号由接收机的通道进入信号调制芯片,获得直流偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后,电压差的正负输出到电机驱动芯片决定电机的正反转。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。 简单的来讲,就是输出一个周期为20Ms,不同的占空比对应舵机转过不同的角度。 难点主要在于 舵机控制信号需要保持,这样就比用脉冲控制步进电机要复杂一些。 你需要
[单片机]
使用<font color='red'>AVR</font>单片机驱动舵机
AVR单片机实现LED彩灯控制器设计
如果你想改变LED接的管脚,请修改hardware.h文件。如果想修改LED的亮度,请修改globals.h 中的Timings 段定义。本设计外接了两个按钮,一个是选择工作模式,另一个是电源的开关。当你按下模式按钮1.5秒以上时,进入自动模式,会自动演示所有的预设模式。 8个LED灯直接连接到Vcc 上,不需要限流电阻。本制作利用到同步定时器,及使用睡眠的方式节省电力。 实物图: 电路图:(点击可以放大) PCB图:(点击可以放大)
[工业控制]
<font color='red'>AVR</font>单片机实现LED彩灯控制器设计
接地电阻测试仪作用及使用注意事项
1.测量方法 测量直流电阻是变压器试验中的一个重要项目。通过测量,可以检查出 设备 的导电回路有无接触不良、焊接不良、线圈故障及接线错误等缺陷。在中、小型变压器的实际测量中,大多采用直流电桥法,当被试线圈的电阻值在1欧以上的一般用单臂电桥测量,1欧以下的则用双臂电桥测量。在使用双臂电桥接线时,电桥的电位桩头要靠近被测电阻,电流桩头要接在电位桩头的上面。测量前,应先估计被测线圈的电阻值,将电桥倍率选钮置于适当位置,将非被测线圈短路并接地,然后打开电源开关充电,待充足电后按下检流计开关,迅速调节测量臂,使检流计指针向检流计刻度中间的零位线方向移动,进行微调,待指针平稳停在零位上时记录电阻值,此时,被测线圈电阻值=倍率数×测量臂电阻值。
[测试测量]
附加相位噪声测试技术及测试过程注意事项
1引言 相位噪声是频率源和频率控制器件的一个重要指标。频率源相位噪声的测试是时间频率专业计量测试人员经常进行的工作,有大量文章介绍,但是频率控制器件的相位噪声即附加相位噪声的测试却很少有文章提及。本文简单介绍了相位噪声的定义,详细介绍了附加相位噪声的测试过程,给出了实际的测试结果,指出了附加相位噪声测试过程中的一些注意事项,希望对附加相位噪声测试人员有一定的借鉴意义。 2频率源相位噪声的定义 频率源的输出信号,一般可表示为: 在相位噪声测量中,实际的测试结果量并非上述定义的谱密度,而是信号调制边带功率与总信号功率之比 按照国际上早年推荐的定义和近些年美国国家标准技术研究院NIST对有关特征量的规定,单边带相位噪声作为
[测试测量]
附加相位噪声测试技术及测试过程<font color='red'>注意事项</font>
PIC单片机之注意事项
MPLAB IDE v8.92 只能支持英文目录,所以一般而言是把相应的工程拷到D盘中,然后打开编译,最后如果想要保存工程,应该拷回想要保存的位置 当在中文目录中会出现“找不到指定路径”的错误,如下: 关于超大数组问题: 本实验用的编译器是xc16-gcc.exe,集成开发环境是MPLAB IDE v8.92,当前的编译器配置如下: -g -Wall -mlarge-code -mlarge-data -Os -fno-ivopts 如下图: 其中这里有一个超大的数组: 这时候编译会有这样的一个错误: 从提示可以知道,这是一个数据超出的错误,但是这里是超出了数据段,而不是说超出了芯片的fla
[单片机]
STM32是如何进入中断函数
中断相信很多人都知道是什么意思,不同的任务有不同的优先级,高任务优先级会比低优先级先执行。在嵌入式系统中, 任务的调度和切换都是根据优先级来判断的。 中断可以分为软中断和硬中断。一开始接触到的一般都是软中断,软中断就是中断程序包含在主程序里面,当中断条件满足时,直接跳转到中断函数执行,然后再返回。就相当于判断语句。 刚开始接触STM32的小伙伴可能会发现main.c里面没有中断程序也没用跳转判断语句。例如:定时器中断 #include system.h #include SysTick.h #include led.h #include time.h int main() { u8 i; SysTic
[单片机]
静压式液位传感器的使用注意事项
  静压式液位传感器是液位传感器中常用的一种,主要针对于行业中的液体介质进行检测。用户在使用静压式液位传感器的时候需要注意一些问题,如果在使用中出现失误是很容易造成测量失败的。今天小编就来为大家具体介绍一下静压式液位传感器的使用注意事项吧,希望可以帮助到大家。   静压式液位传感器的使用注意事项:   1) 静压式液位传感器位置应使其有一条垂直于液面的畅通的声通道。   2)静压式液位传感器不应与粗糙罐壁、横梁和梯子等物相交。   3) 静压式液位传感器应安装在符合规定的温度范围并适合于其防护等级及构成材料的区域内,前盖应能够接近进行编程、接线和观察显示值。   4) 传感器应远离高压或强电工作区、接触器及可控硅控制驱动器。
[测试测量]
AVR单片机的串口查询设计
* Code adapted from Atmel AVR ApplICation Note AVR306 * PolLEDmode driver forUART, this is the similar to the * library default putchar() and getchar() in ICCAVR */ #include #include #include “uart.h” /* iniTIalize UART */ void InitUART( unsigned char baudrate ) { UBRR = baudrate; /* set the baud rate */ UCR = BIT
[单片机]
<font color='red'>AVR</font>单片机的串口查询设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved