原创 avr-gcc中关于delay延时函数的应用[转]

2009-5-9 08:18 4546 9 10 分类: MCU/ 嵌入式


在51中我们的延时函数都是自己编写的,无论是在汇编中还是在C言语中。虽然有模板,有时还是有点烦。呵呵。不过在应用avr
单片机的时候我们就有福了。因为avr-gcc 提供给我们很方便的delay 延时函数, 只有在源文件包含:
#i nclude
<util/delay.h>  
就可以使用了。这个头文件定义了两个级别的延时函数分别是:
void  _delay_us
(double __us) ;      //微秒级
void  _delay_ms (double
__ms);    //毫秒级

不过不可以高兴的太早,因为要在你的avr-gcc中正确使用它们是有条件的,下面我将慢慢道来。

这个参数和
Makefile 中的 F_CPU 值有关,Makefile 所定义的的F_CPU 变量的值会传递给编译器。你如果用AVR_studio
4.1X来编辑和调试,用内嵌AVR-GCC的进行编译,并且让AVR_studio 帮你自动生成Makefile
的话,那你可以在:
                                           Project ->
Configuration Options -> Gerneral -> Frequency   如下图:
513465154.jpg
写下你的F_CPU的值,F_CPU这个值表示你的AVR单片机的工作频率。单位是
Hz ,不是 MHZ,不要写错。如 7.3728M   则 F_CPU = 7372800  。
你会发现在"delay.h"
头文件中有这个样的一个定义如下:
#ifndef F_CPU
# warning "F_CPU not defined for
<util/delay.h>"
# define F_CPU 1000000UL    //
1MHz
#endif

这是为了在你没有定义F_CPU这个变量(包括空),或是AVR_studio
Frequency没有给值的时候,提供一个默认的 1MHz频率值。让编译器编译时不至于出错。

下面是这两个函数的实体:
void
_delay_us(double __us)        //  微秒
{
uint8_t __ticks;
double __tmp =
((F_CPU) / 3e6) * __us;   // 3e6 是因为调用的_delay_loop_1()是三条指令的
if (__tmp <
1.0)
  __ticks = 1;
else if (__tmp > 255)
  __ticks = 0; /* i.e. 256
*/
else
  __ticks =
(uint8_t)__tmp;
_delay_loop_1(__ticks);
}

void _delay_ms(double
__ms)       // 毫秒
{
uint16_t __ticks;
double __tmp = ((F_CPU) / 4e3) *
__ms;  // 4e3 是因为调用的_delay_loop_2()是四条指令的

if (__tmp <
1.0)
  __ticks = 1;
else if (__tmp > 65535)
  __ticks = 0; /* i.e.
65536 */
else
  __ticks =
(uint16_t)__tmp;
_delay_loop_2(__ticks);
}

你会发现他们都分别调用了  _delay_loop_1();
和_delay_loop_2(); 这两个函数
而这两个函数又如下所示:
void  _delay_loop_1(uint8_t
__count)
{
__asm__ volatile (
  "1: dec %0" "\n\t"
  "brne
1b"
  : "=r" (__count)
  : "0"
(__count)
);
}

void  _delay_loop_2(uint16_t
__count)
{
__asm__ volatile (
  "1: sbiw %0,1" "\n\t"
  "brne
1b"
  : "=w" (__count)
  : "0" (__count)
);
}

这两个函数都是avr-gcc

inline汇编格式写的,具体的语法规则我就不多说了。可以参考avr-libc。不过这两个函数很简单,很容易明白。一个是字节递减,一个是字递减。如果你认真看上面几个函数,你就会发现要正确使用它们是有如下条件的:
        1.
首先,你要正确定义你的 F_CPU 的值,也就是你的AVR单片机实际的频率。否则延时不准。
        2.
你在编译时一定要打开优化,Makefile中OPT 不要选 0 ,如果AVR_studio 不要选O0 。
        3.
你在使用这两个delay()时,传递给两个函数的实参要使用常量,不要使用变量。
        4. 设置的时间参数__ms , __us
是有范围的,不要超过范围。__ms:1 - [262.14 ms / (F_CPU/1e6) ],  __us:1- [768 us /
(F_CPU/1e6)]   。 [...]  表取整数部分.

对于第4条范围,来个例子:
      如果F_CPU =
7372800,则__ms范围:1 - 35  ,而__us范围:1 - 104 。

只有具备了上面的条件你才可以正确使用延时函数
_delay_us () 和 _delay_ms ()
。对于第三个条件,为什么要选用常量,还有第二个条件为什么要打开优化选项。这是为了让编译器在编译的时候就把延时的值计算好,而不是把它编译到程序中,在运行时才进行计算,那样的话,一是会增加代码的长度,还会使你的延时程序的延时时间加长,或是变得不可预料。产生时序的错误。




The maximal possible delay is 768 us / F_CPU in MHz.
The maximal possible
delay is 262.14 ms / F_CPU in MHz.
  
         us(768)
ms(262.14)
1M:     768     262    
2M:     384    
131
3.6864M  208     71
4M:     192     65
7.3728   104    
38
8M:      96      32

文章评论1条评论)

登录后参与讨论

用户205677 2009-7-16 22:27

ul是hz的意思,谢谢指点,高人。
相关推荐阅读
用户1494767 2014-09-27 12:21
zigbee学习--osalInitTasks
http://85kaifa.taobao.com [学习 记录中....随时更改] zigbee学习--osalInitTasks  (OSAL_SampleApp.c) /****...
用户1494767 2012-08-12 18:31
ubuntu 10.04下nfs挂载2440
1、安装端口映射器portmap(可选)    sudo apt-get install portmap 2、在终端提示符后键入以下命令安装NFS服务器   sudo apt-get ins...
用户1494767 2012-08-09 22:33
Linux下DNW的PC端USB驱动和写入工具【转】
1. 下载Linux下DNW的PC端USB驱动和写入工具 文件名: dnw_linux.zip 下载后把后缀名改成.tgz #或者搜索 linux dnw 2. 编译PC端USB驱动和写入...
用户1494767 2012-01-06 17:01
STM32F207 uCOS-II移植笔记(下)
  第十步:main函数中变为:主要是将时钟初始化去掉,并建立一个启动任务        int main(void)        {           CPU_INT08U  o...
用户1494767 2012-01-05 09:51
STM32F207 uCOS-II移植笔记(上)
                                     STM32F207 uCOS-II移植笔记(上) 第一步: 建立STM32F207工程,已经有相关文档说明。其中stm...
用户1494767 2011-12-15 18:04
转]ubuntu 环境变量设置方法
环境变量配置文件 在Ubuntu中有如下几个文件可以设置环境变量 1、/etc/profile:在登录时,操作系统定制用户环境时使用的第一个文件,此文件为系统的每个用户设置环境信息,...
我要评论
1
9
关闭 站长推荐上一条 /2 下一条