搜索
bottom↓
回复: 42

哈哈,51也可以跟WINAVR一样有_delay_us(),_delay_ms()函数了,可以根据晶振自动调整,代

[复制链接]

出0入0汤圆

发表于 2011-2-4 15:37:01 | 显示全部楼层 |阅读模式
如题
点击此处下载 ourdev_614624ZCX5XV.pdf(文件大小:56K) (原文件名:WinAVR延时函数理解 .pdf)


我写的头文件 delay.h   最后面的需要自己扩展



#ifndef _DELAY_H_
#define _DELAY_H_
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//时钟频率,需要设置
#ifndef _F_CPU_
#define _F_CPU_
//#define F_CPU_1000000
//#define F_CPU_1843200
//#define F_CPU_2000000
//#define F_CPU_3686400
//#define F_CPU_4000000
//#define F_CPU_7372800
//#define F_CPU_8000000
  #define F_CPU_11059200
//#define F_CPU_12000000
//#define F_CPU_14745600
//#define F_CPU_16000000
//#define F_CPU_18432000
//#define F_CPU_20000000
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//XT精确延时函数
#ifndef _DELAY_XT_H_
#define _DELAY_XT_H_
#include <intrins.h>
//延时专用全局变量
unsigned char _DELAY_I=0,_DELAY_J=0,_DELAY_K=0;
//精确延时1T秒
#define _delay_1T()            {_nop_();}
//精确延时2xT秒,x范围1-255
#define _delay_2xT(x)         {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--);}
//精确延时10xT秒,xx范围1-255
#define _delay_10xT(x)        {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--)\
                                    {for(_DELAY_J=3;_DELAY_J>0;_DELAY_J--);};}
//精确延时100xT秒,xx范围1-255
#define _delay_100xT(x)       {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--)\
                                    {for(_DELAY_J=48;_DELAY_J>0;_DELAY_J--);};}
//精确延时xKT秒,x范围1-255
#define _delay_xKT(x)            {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--)\
                                    {for(_DELAY_J=2;_DELAY_J>0;_DELAY_J--)\
                                        {for(_DELAY_K=248;_DELAY_K>0;_DELAY_K--);};};}
//精确延时10xKT秒,xx范围1-255
#define _delay_10xKT(x)        {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--)\
                                    {for(_DELAY_J=20;_DELAY_J>0;_DELAY_J--)\
                                        {for(_DELAY_K=248;_DELAY_K>0;_DELAY_K--);};};}
//精确延时100xKT秒,xx范围1-255
#define _delay_100xKT(x)       {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--)\
                                    {for(_DELAY_J=200;_DELAY_J>0;_DELAY_J--)\
                                        {for(_DELAY_K=248;_DELAY_K>0;_DELAY_K--);};};}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//uS,mS延时函数自动设置
#ifdef F_CPU_11059200  //F=11.0592MHz,T=1.08507uS
#define _delay_1us() _delay_1T()
#define _delay_10us() {_delay_2xT(4);_delay_1T();}
#define _delay_100us() _delay_2xT(46)
#define _delay_1ms() _delay_10xT(92)
#endif

#ifdef F_CPU_12000000  //F=12.0000MHz,T=1.00000uS
#define _delay_1us() _delay_1T()
#define _delay_10us() _delay_2xT(5)
#define _delay_100us() _delay_2xT(50)
#define _delay_1ms() _delay_xKT(1)
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////





////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2011-2-4 15:40:23 | 显示全部楼层
C语言代码汇编以后的效果,此时的晶振11.0592M,晶振频率需要利用define设置

    15:         _delay_1us();
C:0x008F    00       NOP      
    16:         _delay_10us();
C:0x0090    750804   MOV      _DELAY_I(0x08),#0x04
C:0x0093    D508FD   DJNZ     _DELAY_I(0x08),C:0093
C:0x0096    00       NOP      
    17:         _delay_100us();
C:0x0097    75082E   MOV      _DELAY_I(0x08),#0x2E
C:0x009A    D508FD   DJNZ     _DELAY_I(0x08),C:009A
    18:         _delay_1ms();
C:0x009D    75085C   MOV      _DELAY_I(0x08),#0x5C
C:0x00A0    750903   MOV      _DELAY_J(0x09),#0x03
C:0x00A3    D509FD   DJNZ     _DELAY_J(0x09),C:00A3
C:0x00A6    D508F7   DJNZ     _DELAY_I(0x08),C:00A0

出0入0汤圆

发表于 2011-2-4 18:29:15 | 显示全部楼层
马克思

出0入0汤圆

发表于 2011-2-4 19:22:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-4 20:05:02 | 显示全部楼层
1us人工翻译过来后是
lcall delay1us;
nop
ret
这样是1us吗?
如果编译器不好或者要用通用编译器的话
mov a,r0
push acc
mov a,r1
push acc
........//中间省略,你懂的
lcall delay1us
nop
.....
pop acc
mov r1,a
pop acc
mov r0,a
ret
这样的话还是1us吗?
如果中间pop或者push出错,
ret的时候错误的把其他sp的数据返回给pc那就跑飞了。
幸亏这还是51。。。。。。

出0入0汤圆

发表于 2011-2-4 21:58:14 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2011-2-4 22:27:01 | 显示全部楼层
哈哈,既然有人关注,所以就把源码发上来了。
在一楼。

嘿嘿嘿

出0入0汤圆

发表于 2011-2-4 23:53:02 | 显示全部楼层
MARK ,Thank you

出0入0汤圆

 楼主| 发表于 2011-2-5 00:54:33 | 显示全部楼层
【4楼】 hzr0071

哥们
发上源代码应该就明白了吧。

是直接用define替换
跟内联函数有些像

WINAVR似乎也是这么搞得

这个办法用在其他编译器上一样 如 ICCAVR


其实调用的不是函数,所以没有压栈出栈操作

最终结果用PROTUES联调验证过,相当准

个人的想法,主要是感觉winavr的delay函数太帅了,也自己弄个在其他编译器上用

出0入0汤圆

发表于 2011-2-5 01:43:30 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-5 03:43:52 | 显示全部楼层
"//精确延时2xT秒,x范围1-255
#define _delay_2xT(x)         {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--);} "

put that in your code, compile it under different optimization settings and take a look at the assembly output and ask yourself why.

出0入0汤圆

发表于 2011-2-5 08:08:57 | 显示全部楼层
Mark.

出0入0汤圆

发表于 2011-2-5 08:36:43 | 显示全部楼层
jh

出0入0汤圆

发表于 2011-2-5 09:09:04 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2011-2-5 09:09:20 | 显示全部楼层
【10楼】 millwood0
使用的编译器是  Keil uVision3  设置等都是默认

#define _delay_2xT(x)         {for(_DELAY_I=x;_DELAY_I>0;_DELAY_I--);}

编译以后就是下面的效果:
C:0x0090    750804   MOV      _DELAY_I(0x08),#0x04
C:0x0093    D508FD   DJNZ     _DELAY_I(0x08),C:0093

没有在其他的编译器和选项中使用。

这个是精确延时,延时2x个周期

汇编没怎么学,但是反汇编后的简单的语句还只能看懂的

其实我试过好几种书写方式,这种是代码最少的。
x相当于立即数

出0入0汤圆

发表于 2011-2-5 09:30:20 | 显示全部楼层
无论如何都要顶

出0入0汤圆

 楼主| 发表于 2011-2-5 10:34:19 | 显示全部楼层
刚刚试了下用这个头文件真的很方便

延时可以很精确的

哈哈哈哈

方便WINAVR程序直接移植

出0入0汤圆

发表于 2011-2-5 10:36:10 | 显示全部楼层
MARK

出0入4汤圆

发表于 2011-2-5 10:42:52 | 显示全部楼层
不同的编译器或版本,不同的优化等级都会对这部份时间产生影响,无法保证一定严格的定时时间。

出0入0汤圆

 楼主| 发表于 2011-2-5 10:48:10 | 显示全部楼层
【19楼】 banyai
不同的编译器或版本,不同的优化等级都会对这部份时间产生影响,无法保证一定严格的定时时间。

也是,用之前只要监控一下汇编代码就可以

这个在51上验证了,完全好使


编写这个主要想实现精确延时,想WINAVR的delay函数一样

WINAVR是使用的double型数据,但是最后优化就改为内联了。在此处不能这么办

WinAVR延时函数理解 .txt
WinAVR延时函数理解
WinAVR20081205 延时函数 :
void
_delay_loop_1(uint8_t __count)
{
__asm__ volatile (
"1: dec %0" "\n\t"
"brne 1b"
: "=r" (__count)
: "0" (__count)
);
}
准确延时是3*__count个时钟周期 (0<__count<256)
void
_delay_loop_2(uint16_t __count)
{
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__count)
: "0" (__count)
);
}
准确延时是4*__count+1个时钟周期 (0<__count<256*256-1)
_delay_loop_1() 最小延时是3个时钟周期,最大延时是256*3个时钟周期
_delay_loop_2() 最小延时是4+1个时钟周期,最大延时是256*256*4+1个时钟周期
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);
}
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);
第 1 页
WinAVR延时函数理解 .txt
}
_dealy_us()最小延时 与 _delay_loop_1()相同,是3个时钟周期,
_delay_us(0)就是最小延时,相当于_delay_loop_1(1),
在8M时钟下,_delay_us(0.375)也是最小延时(0.375us是3个时钟周期)
_delay_us(0.7499999)仍然是最小延时,相当于_delay_loop_1(1),
而_delay_us(0.74999999)则相当于_delay_loop_1(2)了。
_dealy_ms() 最小延时与 _delay_loop_2()相同,是4+1个时钟周期。
_delay_ms(0)就是最小延时,相当于_delay_loop_2(1),
在8M时钟下,_delay_ms(0.0005)也是最小延时(0.0005ms相当于是4个时钟周期)
_delay_ms(0.0009999999)仍然是最小延时,相当于_delay_loop_2(1),
而_delay_ms(0.00099999999)则相当于_delay_loop_2(2)了。
_dealy_us(__us)延时精度范围:
(0 , 3个时钟周期),误差(0,3)个时钟周期
(3个时钟周期 , 768us/(F_CPU/1000000)),误差(-2,0)个时钟周期
(768us/(F_CPU/1000000) , 262.14ms/(F_CPU/1000000)),误差(-2,+1)个时钟周期,
(262.14ms/(F_CPU/1000000)) , 6553.5ms),误差8M时钟下(约+0.18ms,约+57ms)
_dealy_ms(__ms)延时精度范围:
(0 , 4个时钟周期),误差(1,5)个时钟周期
(4个时钟周期 , 262.14ms/(F_CPU/1000000)),误差(-2,+1)个时钟周期,
(262.14ms/(F_CPU/1000000)) , 6553.5ms),误差8M时钟下(约+0.18ms,约+57ms)
_dealy_us(__us)最大延时6553.5ms,即_delay_us(6553500);
_delay_ms(__ms)最大延时6553.5ms,即_delay_ms(6553.5);
本人水平有限,如果错漏之处,多谢指正。
补充:
1.WinAVR延时库函数看起来,又是double,又是if、else,难道不耗时间?
不耗时间。WinAVR延时库函数,double都可以优化成常量,而整型常量,浮点常量运算,结果仍然仍然是常量。
编译器只要开优化,不会编译出额外的代码出来。if,else也一样。if判断条件是一个常量,也就是说某个分支一定为真,
另外的分支一定为假,编译器优化时,不会编译出额外代码。需要注意,如果要调用WinAVR延时库函数,则必须要开优
化。
2.怎么没有计算函数调用,返回时间?
WinAVR延时库函数全部都是内联函数,没有函数调用和返回开销。当然,这也有一个副作用,每处延时函数都会编译出一
段代码,
占据更多的代码空间。
3.旧版WinAVR _delay_us()最大延时768us/(F_CPU/1000000),delay_ms()最大延时是262.14ms/(F_CPU/1000000)。
新版WinAVR_delay_us()和_delay_ms()最大延时都是6553.5ms,不过误差也相对较大,每0.1ms多7个时钟周期,8M时钟
下,误差约为+0.88%。
----------------------------------
附IARAVR延时函数:
#ifndef __IAR_DELAY_H__
#define __IAR_DELAY_H__
#include <intrinsics.h>
#include "hal_type.h"
#define _delay_loop_1(A) __delay_cycles(3*(A))
#define _delay_loop_2(A) __delay_cycles(4*(A)+1)
#define _delay_us(A)\
__delay_cycles( (uint32) ( (double)(F_CPU) *((A)/1000000.0) + 0.5))
#define _delay_ms(A)\
__delay_cycles( (uint32) ( (double)(F_CPU)*((A)/1000.0) + 0.5))
#define _delay_s(A)\
__delay_cycles( (uint32) ( (double)(F_CPU)*((A)/1.0) + 0.5))
#endif
第 2 页

点击此处下载 ourdev_614624ZCX5XV.pdf(文件大小:56K) (原文件名:WinAVR延时函数理解 .pdf)

出0入0汤圆

发表于 2011-2-5 11:12:18 | 显示全部楼层
楼主是高手哦,学习学习

出0入0汤圆

 楼主| 发表于 2011-2-5 11:48:40 | 显示全部楼层
【21楼】 downtoearth

嘿嘿嘿
以前光捣鼓模拟电路,单片机捣鼓的不多
但对算法,优化等有些研究

出0入0汤圆

发表于 2011-2-5 11:59:38 | 显示全部楼层
mark!!!

出0入0汤圆

发表于 2011-2-5 13:00:34 | 显示全部楼层
mark

出0入264汤圆

发表于 2011-2-5 14:22:25 | 显示全部楼层
ths study

出0入0汤圆

发表于 2011-2-5 15:08:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-5 15:11:19 | 显示全部楼层
51有1T、4T、6T、12T的你这不能通用的诶

出0入0汤圆

发表于 2011-2-5 15:14:17 | 显示全部楼层
回复【22楼】fHimemhacker F.H.
-----------------------------------------------------------------------

我用笔抄下,希望楼主多发点算法和代码优化的贴,待我这种菜鸟多多学习。

出0入0汤圆

 楼主| 发表于 2011-2-5 16:34:40 | 显示全部楼层
回复【27楼】madara
-----------------------------------------------------------------------

也是,其实这个只是一个思路

51有1T、4T、6T、12T的你这不能通用的诶
大不了把结果乘个系数就行了

或者 可以用#ifdef命令 根据包含的头文件自动选择系数

这个精度试过相当高的,如果还有较高的要求,也可以连着用 哈哈哈
比如11.0592M下

#define _delay_10us() {_delay_2xT(4);_delay_1T();}

出0入0汤圆

发表于 2011-2-5 18:25:40 | 显示全部楼层
m

出0入0汤圆

发表于 2011-2-5 19:57:18 | 显示全部楼层
收藏一下,谢谢

出0入0汤圆

发表于 2011-2-6 19:33:25 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-7 05:56:03 | 显示全部楼层
不错! 问下能支持 8051F系列单片机吗? 是1T的

出0入0汤圆

 楼主| 发表于 2011-2-7 08:13:20 | 显示全部楼层
回复【33楼】aahui
-----------------------------------------------------------------------

都一样吧,自己开发

出100入0汤圆

发表于 2011-2-7 08:29:42 | 显示全部楼层
标 记 号

出0入0汤圆

发表于 2011-2-7 09:04:42 | 显示全部楼层
一定要mark

出0入0汤圆

发表于 2011-2-7 09:52:31 | 显示全部楼层
拜拜。。。

出0入0汤圆

发表于 2012-5-31 06:36:46 | 显示全部楼层

出0入0汤圆

发表于 2012-5-31 08:42:30 | 显示全部楼层
mark 表示关注

出0入0汤圆

发表于 2012-5-31 08:46:05 | 显示全部楼层
mark , 学习了

出0入0汤圆

发表于 2012-5-31 08:47:06 | 显示全部楼层
正好收下。懒得自己写了。谢了。

出0入0汤圆

发表于 2012-5-31 09:17:10 | 显示全部楼层
mark 表示关注
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 00:17

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表