avr-gcc中关于delay延时函数的应用修改版[ourdev]
2013-02-06 16:09
405 查看
在51中我们的延时函数都是自己编写的,无论是在汇编中还是在C言语中。虽然有模板,有时还是有点烦。呵呵。不过在应用avr 单片机的时候我们就有福了。因为avr-gcc 提供给我们很方便的delay 延时函数, 只有在源文件包含:
就可以使用了。这个头文件定义了两个级别的延时函数分别是:
不过不可以高兴的太早,因为要在你的avr-gcc中正确使用它们是有条件的,下面我将慢慢道来。
这个参数和 Makefile 中的 F_CPU 值有关,Makefile 所定义的的F_CPU 变量的值会传递给编译器。你如果用***R_studio 4.1X来编辑和调试,用内嵌***R-GCC的进行编译,并且让***R_studio 帮你自动生成Makefile 的话,那你可以在:
Project -> Configuration Options -> Gerneral -> Frequency 如下图:
写下你的F_CPU的值,F_CPU这个值表示你的***R单片机的工作频率。单位是 Hz ,不是 MHZ,不要写错。如 7.3728M 则 F_CPU = 7372800。
你会发现在"delay.h" 头文件中有这个样的一个定义如下:
这是为了在你没有定义F_CPU这个变量(包括空),或是***R_studio Frequency没有给值的时候,提供一个默认的 1MHz频率值。让编译器编译时不至于出错。
下面是这两个函数的实体:
你会发现他们都分别调用了 _delay_loop_1(); 和_delay_loop_2(); 这两个函数
而这两个函数又如下所示:
从其函数注释里面可以了解到,该函数用来延迟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
依此类推。
该函数延时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)延时也是一样的!!这些函数的延时都是最长的延时。
这两个函数都是avr-gcc 的 inline汇编格式写的,具体的语法规则我就不多说了。可以参考avr-libc。不过这两个函数很简单,很容易明白。一个是字节递减,一个是字递减。如果你认真看上面几个函数,你就会发现要正确使用它们是有如下条件的:
1. 首先,你要正确定义你的 F_CPU 的值,也就是你的***R单片机实际的频率。否则延时不准。(延时只在数字上不准确,具体可以计算)
2. 你在编译时一定要打开优化,Makefile中OPT 不要选 0 ,如果***R_studio 不要选O0 。
3. 你在使用这两个delay()时,传递给两个函数的实参要使用常量,不要使用变量。
4. 设置的时间参数__ms , __us 是有范围的,不要超过范围。__ms:1 - [262.14 ms / (F_CPU/1e6) ], __us:1- [768 us / (F_CPU/1e6)] 。 [...] 表取整数部分.(此处结论错误?)。
__us的最大值应该是768us(1M频率下) MAX_VALUE = 256*3/F_CPU s,最小值3个时钟周期MIN_VALUE = 1*3/F_CPU
s;
,__ms最大值MAX_VALUE = 65536*4/F_CPU s,MIN_VALUE = 1*4/F_CPU s;
只有具备了上面的条件你才可以正确使用延时函数 _delay_us () 和 _delay_ms () 。对于第三个条件,为什么要选用常量,还有第二个条件为什么要打开优化选项。这是为了让编译器在编译的时候就把延时的值计算好,而不是把它编译到程序中,在运行时才进行计算,那样的话,一是会增加代码的长度,还会使你的延时程序的延时时间加长,或是变得不可预料。产生时序的错误。
在08版本中已经修改,具体函数如下:
当__us过大的时候,就会调用_delay_ms();由上面可以知道8M时候_delay_ms最小可以延时4/8000000=0.5us 1M时,最小延时4/1000000=4us,可以连接上。
当__ms过大时,只采用__ticks --的方式延时。先延时一个262ms(1M,32ms 8M),然后用递减方式。
#include <util/delay.h>
就可以使用了。这个头文件定义了两个级别的延时函数分别是:
void _delay_us (double __us) ; //微秒级 void _delay_ms (double __ms); //毫秒级
不过不可以高兴的太早,因为要在你的avr-gcc中正确使用它们是有条件的,下面我将慢慢道来。
这个参数和 Makefile 中的 F_CPU 值有关,Makefile 所定义的的F_CPU 变量的值会传递给编译器。你如果用***R_studio 4.1X来编辑和调试,用内嵌***R-GCC的进行编译,并且让***R_studio 帮你自动生成Makefile 的话,那你可以在:
Project -> Configuration Options -> Gerneral -> Frequency 如下图:
写下你的F_CPU的值,F_CPU这个值表示你的***R单片机的工作频率。单位是 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这个变量(包括空),或是***R_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) ); }
从其函数注释里面可以了解到,该函数用来延迟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
依此类推。
void _delay_loop_2(uint16_t __count) { __asm__ volatile ( "1: sbiw %0,1" "\n\t" "brne 1b" : "=w" (__count) : "0" (__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)延时也是一样的!!这些函数的延时都是最长的延时。
这两个函数都是avr-gcc 的 inline汇编格式写的,具体的语法规则我就不多说了。可以参考avr-libc。不过这两个函数很简单,很容易明白。一个是字节递减,一个是字递减。如果你认真看上面几个函数,你就会发现要正确使用它们是有如下条件的:
1. 首先,你要正确定义你的 F_CPU 的值,也就是你的***R单片机实际的频率。否则延时不准。(延时只在数字上不准确,具体可以计算)
2. 你在编译时一定要打开优化,Makefile中OPT 不要选 0 ,如果***R_studio 不要选O0 。
3. 你在使用这两个delay()时,传递给两个函数的实参要使用常量,不要使用变量。
4. 设置的时间参数__ms , __us 是有范围的,不要超过范围。__ms:1 - [262.14 ms / (F_CPU/1e6) ], __us:1- [768 us / (F_CPU/1e6)] 。 [...] 表取整数部分.(此处结论错误?)。
__us的最大值应该是768us(1M频率下) MAX_VALUE = 256*3/F_CPU s,最小值3个时钟周期MIN_VALUE = 1*3/F_CPU
s;
,__ms最大值MAX_VALUE = 65536*4/F_CPU s,MIN_VALUE = 1*4/F_CPU s;
只有具备了上面的条件你才可以正确使用延时函数 _delay_us () 和 _delay_ms () 。对于第三个条件,为什么要选用常量,还有第二个条件为什么要打开优化选项。这是为了让编译器在编译的时候就把延时的值计算好,而不是把它编译到程序中,在运行时才进行计算,那样的话,一是会增加代码的长度,还会使你的延时程序的延时时间加长,或是变得不可预料。产生时序的错误。
在08版本中已经修改,具体函数如下:
void _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); }
当__us过大的时候,就会调用_delay_ms();由上面可以知道8M时候_delay_ms最小可以延时4/8000000=0.5us 1M时,最小延时4/1000000=4us,可以连接上。
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 = 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); }
当__ms过大时,只采用__ticks --的方式延时。先延时一个262ms(1M,32ms 8M),然后用递减方式。
相关文章推荐
- avr-gcc中关于delay延时函数的应用(原发于2007-11-16)
- Avr-Gcc中关于delay函数的应用
- 关于函数指针在单片机上的应用
- 翻译和修改的AVR-gcc3.3的makefile。(用法:make clean /make all)
- 关于linux中的延时函数
- DOS下面关于精确延时1ms的函数(理论上可以非常精确延时到10微妙级别)
- 关于各种型号单片机delay函数不起作用问题的解决方法
- 关于sharepoint2007的服务器场查看、批量修改应用程序池的帐号以及备用访问映射问题
- 关于STDLIB.H头文件中的函数应用
- js关于setTimeout实现延时执行函数并进行传参
- 【函数应用】PHP中关于URL的函数处理
- 关于gcc编译器中函数不用进行原型声明的解释
- 关于oracle的函数,存储过程,触发器,序列,视图,左右连接一些的应用 带案例
- C++初学者(关于函数指针的应用)
- [windows c]关于指针函数和参数可变函数同时应用情况的疑问
- Day21、Unix开始:操作系统基础、gcc的一些应用、标准C库函数及帮助
- 关于java函数参数的修改能否带出来
- 关于gcc内置函数和c隐式函数声明的认识以及一些推测
- 3.关于延时函数的配置
- 关于字符函数的一些应用总结