在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
芯片精品文章合集(500篇!) 创芯人才网--重磅上线啦!
查看: 5732|回复: 1

[转贴]精确到1个机器周期的延迟函数

[复制链接]
发表于 2008-9-6 21:05:47 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
延迟函数恐怕是任何一个51程序中最经常用到的部分,可以用空循环来做,也可以使用基于
定时器的中断来做。两种方法各有优缺点,各自适应不同的应用场合。今天,我按照自己的
思路给出这个问题的解答。这个解答是基于空循环的方法,我讲解的顺序是先用汇编来实现,
然后想办法把这段汇编嵌入到C程序中。以下的时间单位假定为机器周期,而不是具体的多少
毫秒微秒之类的。

第一步:汇编实现。
我们需要用到3个寄存器和3个内存单元。寄存器用来循环计数,内存单元用来保存循环次数
的初始数值。假定寄存器使用r5、r6和r7,对应的内存单元用标号记为delayr5、delayr6和
delayr7。也许你已经有所领悟,我使用3个寄存器和3个内存单元就是要做3层嵌套循环,r5
和delayr5用于控制最外层,r6和delayr6用于控制中间层,r7和delayr7用于控制最内层。
在你的汇编程序最开始,你应该定义好delayr5、delayr6和delayr7,比如像下面这样:
        示例代码1:
        delayr5                data        30h
        delayr6         data        31h
        delayr7                data        32h
也就是说,delayr5占据了地址编号为30h的内部RAM,delayr6占据了地址编号为31h的内部RAM,
delayr7占据了地址编号为32h的内部RAM。具体的地址根据你的程序需要自行修改。注意不要
使得内存单元地址和寄存器冲突。好了,现在把我的汇编子程序给你:
        示例代码2:
        DELAY:
                        mov r5,delayr5                ;2个机器周期
        DELAY1:
                        mov r6,delayr6                ;2个机器周期
        DELAY2:
                        mov r7,delayr7                ;2个机器周期
        DELAY3:
                        djnz r7,DELAY3                ;2个机器周期
                        djnz r6,DELAY2                ;2个机器周期
                        djnz r5,DELAY1                ;2个机器周期
                        ret                        ;RET指令本身要用2个机器周期才能返回
假定,你已经给delayr5、delayr6和delayr7赋予了正确的数值,当你调用DELAY的时候就会进
行一段时间的空循环,然后返回,达到延迟的目的。而且,根据我的设计,你应该遵守下面的
调用顺序:
        示例代码3:
        ;其它代码
                        mov delayr5,#0XXh        ;2个机器周期
                        mov delayr6,#0XXh        ;2个机器周期
                        mov delayr7,#0XXh        ;2个机器周期
                        lcall DELAY                 ;LCALL指令本身要用2个机器周期才能转
                                                ;到DELAY处去执行
        ;其它代码
在示例代码3中,#0XXh表示某些合适的数值(后面再说如何得到这些数值),那么前3行就是赋
值,第4行是调用。那么包含前3条赋值代码在内,上面的程序一共运行了多少个机器周期呢?
答案是:2{ [ ( delayr7 + 2 )delayr6 + 2 ]delayr5 } + 2 + 2 + 2 + 2 + 2 + 2    公式1
现在,假定你要延迟100个机器周期的时间,那么你就要想办法把循环初始数值算好,然后给
delayr5、delayr6和delayr7赋值就行了,也就是替换示例代码3中的那些#0XXh。这可不是一个
简单的活,你怎么知道你给的初始数值就一定能使公式1算出100个机器周期呢?所以,一个很
重要的工作就是要逆向计算,算出公式1中的那些delay常数。我给你准备了一个VC程序,用于
计算这些数值,你自己下载来使用。注意#0XXh不能是零。

第二步:如何得到初始数值。
仔细看好下面的说明,你才能学会如何得到初使数值,使得延迟误差收敛到1个机器周期之内。

程序界面如图所示。操作顺序如下:
1 设定晶体频率,单位Hz。不认识KHz、MHz这些单位,只能输入数字。
2 比如AT89C51这种芯片,一个机器周期包括12个时钟周期,那么要把12这个数字设置好。
3 但后点击设定。只要你修改上述两个数值之一时,必需要重新点设定按钮。
4 最大和最小的延迟时间,是根据你输入的晶体频率和分频系数自动计算出来的,你要延迟
的时间只能在这两者之间,超出无效。
5 设定好延迟时间,点计算,就可以得到delayr5、delayr6和delayr7。

以下几个问题需要解答。
A   是不是所有的延迟时间都能精确地达到?
答:不是。根据公式1,延迟的机器周期只能是偶数,不能是奇数。即便是偶数,也不见得能
找到精确的delayr5、delayr6和delayr7。
B   不能精确延迟怎么办?
答:你看见了,程序的界面有一个参数,实际延迟时间与预期值的误差。举例说明,假定12MHz
晶体,12分频(AT89C51),延迟1001微妙,可是实际算出来的量只能延迟1000微妙,差1微妙。
我把这个差值表示为以机器周期为单位的量显示出来,在本例是-1。那么你可以很简单的修改
代码,加上一个nop就行了,如果是-2,加2个nop,这个差值很小,不会让你加很多nop。那如
果差值是正的怎么办?例如你要延迟123458微妙,实际出来的是123460。你可以这样,把要延
迟的数值稍微改小一些,比如123456,那样的话,计算出来的差值是零,然后你人为加2个nop
就OK了。
C   太大或者太小的延迟时间怎么办?
答:如果你要延迟6个微妙,直接写6个nop不是更好吗?如果你要延迟100秒,可以每次延迟20秒,
共5次调用不就OK了,况且延迟100秒,好像也没有必要精确到1微妙。
D   如果差值是-10000,我岂不是要写10000个nop?
答:你好笨。汉字一写一划,二写二划,三写三划,这么说万字要写10000划了。如果差10000个,
你可以再搞一个延迟10000的调用不就行了,大拆小,小拆无。

第三步:嵌入C程序中。
上面是汇编的代码,用C直接写精确的延迟程序是不可能的,只能混合编成,把汇编程序嵌入进
去。当然要做一些小的变化以适应C语言程序。
1 程序需要6个内存空间用来保存变量,你必须有足够空间。像下面这样,在你的main之前:
        示例代码4:
        unsigned char data DelayConstantInner  = 0;        //内层延迟时间常数
        unsigned char data DelayConstantMiddle = 0;        //中层延迟时间常数
        unsigned char data DelayConstantOuter  = 0;        //外层延迟时间常数
        unsigned char data DelayCounterInner   = 0;        //内层延迟计数器
        unsigned char data DelayCounterMiddle  = 0;        //中层延迟计数器
        unsigned char data DelayCounterOuter   = 0;        //外层延迟计数器
2 声明延迟子程序,如果你把这个子程序放在main之前,那么可以声明与实现一起做,如下:
        示例代码5:
        void TimeDelay()                     
        {
                #pragma asm
                DELAY000:
                        mov DelayCounterOuter, DelayConstantOuter
                DELAY001:
                        mov DelayCounterMiddle,DelayConstantMiddle
                DELAY002:
                        mov DelayCounterInner, DelayConstantInner
                DELAY003:
                        djnz DelayCounterInner, DELAY003
                        djnz DelayCounterMiddle,DELAY002
                        djnz DelayCounterOuter, DELAY001
                #pragma endasm  
}
如果你要调用它,为了保持精确性和与上面汇编的兼容性,应该这样做:
        示例代码6:
        DelayConstantInner  = XXX;
        DelayConstantMiddle = XXX;
        DelayConstantOuter  = XXX;
        TimeDelay()
这样,生成的汇编代码与上面完全一致。XXX的数值最小是1,最大是255,不能用0。
3 当然,在C程序中嵌入汇编,还要设置一些编译器选项,这个你自己找书看。这一步很重要哦,要
不你编译根本通不过。
原代码用VC2005打开。

                               
登录/注册后可看大图

DelayTime.rar (1.33 KB, 下载次数: 3 )
原代码.rar (41.8 KB, 下载次数: 3 )
发表于 2014-11-19 15:47:17 | 显示全部楼层
謝謝分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-11-25 21:39 , Processed in 0.025989 second(s), 9 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表