avr-gcc中关于delay延时函数的应用(原发于2007-11-16)

在51中我们的延时函数都是自己编写的,无论是在汇编中还是在C言语中。虽然有模板,有时还是有点烦。呵呵。不过在应用avr 单片机的时候我们就有福了。因为avr-gcc 提供给我们很方便的delay 延时函数, 只有在源文件包含:
#include <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     如下图:

写下你的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; 
  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; 
  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 ()  。对于第三个条件,为什么要选用常量,还有第二个条件为什么要打开优化选项。这是为了让编译器在编译的时候就把延时的值计算好,而不是把它编译到程序中,在运行时才进行计算,那样的话,一是会增加代码的长度,还会使你的延时程序的延时时间加长,或是变得不可预料。产生时序的错误。

发现一写就写了两个多小时,看起来不长。看来说出来很容易,写下来还是要费点工夫的,希望对你的有所帮助。呵呵。分享快乐,共同进步。

ps: 最近看到别人问 1e6, 4e3 ,这是arv-gcc 中一种表示方法 1e6 = 1000000, 4e3 =4000 ,是一种指数表示法。 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值