新闻  |   论坛  |   博客  |   在线研讨会
51单片机KeilC延时程序的简单研究
tongxin | 2009-04-13 15:36:14    阅读:1931   发布文章

51单片机 Keil C 延时程序的简单研究
%A 应用单片机的时候,经常会遇到需要短时间延时的情况。需要的延时时间很短,
%A
%A 一般都是几十到几百微妙(us)。有时候还需要很高的精度,比如用单片机驱动DS18B20
%A
%A 的时候,误差容许的范围在十几us以内,不然很容易出错。这种情况下,用计时器往
%A
%A 往有点小题大做。而在极端的情况下,计时器甚至已经全部派上了别的用途。这时就
%A
%A 需要我们另想别的办法了。
%A
%A     以前用汇编语言写单片机程序的时候,这个问题还是相对容易解决的。比如用的
%A
%A 是12MHz晶振的51,打算延时20us,只要用下面的代码,就可以满足一般的需要:
%A
%A         mov     r0, #09h
%A
%A loop:   djnz    r0, loop
%A
%A 51单片机的指令周期是晶振频率的1/12,也就是1us一个周期。mov r0, #09h需要2个
%A
%A 极其周期,djnz也需要2个极其周期。那么存在r0里的数就是(20-2)/2。用这种方法,
%A
%A 可以非常方便的实现256us以下时间的延时。如果需要更长时间,可以使用两层嵌套。
%A
%A 而且精度可以达到2us,一般来说,这已经足够了。
%A
%A     现在,应用更广泛的毫无疑问是Keil的C编译器。相对汇编来说,C固然有很多优
%A
%A 点,比如程序易维护,便于理解,适合大的项目。但缺点(我觉得这是C的唯一一个缺
%A
%A 点了)就是实时性没有保证,无法预测代码执行的指令周期。因而在实时性要求高的
%A
%A 场合,还需要汇编和C的联合应用。但是是不是这样一个延时程序,也需要用汇编来实
%A
%A 现呢?为了找到这个答案,我做了一个实验。
%A
%A     用C语言实现延时程序,首先想到的就是C常用的循环语句。下面这段代码是我经
%A
%A 常在网上看到的:
%A
%A void delay2(unsigned char i)
%A
%A {
%A
%A     for(; i != 0; i--);
%A
%A }
%A
%A 到底这段代码能达到多高的精度呢?为了直接衡量这段代码的效果,我把 Keil C 根
%A
%A 据这段代码产生的汇编代码找了出来:
%A
%A              ; FUNCTION _delay2 (BEGIN)
%A
%A                                            ; SOURCE LINE # 18
%A
%A ;---- Variable ‘i‘ assigned to Register ‘R7‘ ----
%A
%A                                            ; SOURCE LINE # 19
%A
%A                                            ; SOURCE LINE # 20
%A
%A 0000         ?C0007:
%A
%A 0000 EF                MOV     A,R7
%A
%A 0001 6003              JZ      ?C0010
%A
%A 0003 1F                DEC     R7
%A
%A 0004 80FA              SJMP    ?C0007
%A
%A                                            ; SOURCE LINE # 21
%A
%A 0006         ?C0010:
%A
%A 0006 22                RET    
%A
%A              ; FUNCTION _delay2 (END)
%A
%A 真是不看不知道~~~一看才知道这个延时程序是多么的不准点~~~光看主要的那四条语
%A
%A 句,就需要6个机器周期。也就是说,它的精度顶多也就是6us而已,这还没算上一条
%A
%A lcall 和一条 ret。如果我们把调用函数时赋的i值根延时长度列一个表的话,就是:
%A
%A i    delay time/us
%A
%A 0    6
%A
%A 1    12
%A
%A 2    18
%A
%A ...
%A
%A 因为函数的调用需要2个时钟周期的lcall,所以delay time比从函数代码的执行时间
%A
%A 多2。顺便提一下,有的朋友写的是这样的代码:
%A
%A void delay2(unsigned char i)
%A
%A {
%A
%A     unsigned char a;
%A
%A     for(a = i; a != 0; a--);
%A
%A }
%A
%A 可能有人认为这会生成更长的汇编代码来,但是事实证明:
%A
%A              ; FUNCTION _delay2 (BEGIN)
%A
%A                                            ; SOURCE LINE # 18
%A
%A ;---- Variable ‘i‘ assigned to Register ‘R7‘ ----
%A
%A                                            ; SOURCE LINE # 19
%A
%A                                            ; SOURCE LINE # 21
%A
%A ;---- Variable ‘a‘ assigned to Register ‘R7‘ ----
%A
%A 0000         ?C0007:
%A
%A 0000 EF                MOV     A,R7
%A
%A 0001 6003              JZ      ?C0010
%A
%A 0003 1F                DEC     R7
%A
%A 0004 80FA              SJMP    ?C0007
%A
%A                                            ; SOURCE LINE # 22
%A
%A 0006         ?C0010:
%A
%A 0006 22                RET    
%A
%A              ; FUNCTION _delay2 (END)
%A
%A 其生成的代码是一样的。不过这的确不是什么好的习惯。因为这里实在没有必要再引
%A
%A 入多余的变量。我们继续讨论正题。有的朋友为了得当更长的延时,甚至用了这样的
%A
%A 代码:
%A
%A void delay2(unsigned long i)
%A
%A {
%A
%A     for(; i != 0; i--);
%A
%A }
%A
%A 这段代码产生的汇编代码是什么样子的?其实不用想也知道它是如何恐怖的$#^%&%
%A
%A $......让我们看一看:
%A
%A              ; FUNCTION _delay2 (BEGIN)
%A
%A                                            ; SOURCE LINE # 18
%A
%A 0000 8F00        R     MOV     i+03H,R7
%A
%A 0002 8E00        R     MOV     i+02H,R6
%A
%A 0004 8D00        R     MOV     i+01H,R5
%A
%A 0006 8C00        R     MOV     i,R4
%A
%A                                            ; SOURCE LINE # 19
%A
%A                                            ; SOURCE LINE # 20
%A
%A 0008         ?C0007:
%A
%A 0008 E4                CLR     A
%A
%A 0009 FF                MOV     R7,A
%A
%A 000A FE                MOV     R6,A
%A
%A 000B FD                MOV     R5,A
%A
%A 000C FC                MOV     R4,A
%A
%A 000D AB00        R     MOV     R3,i+03H
%A
%A 000F AA00        R     MOV     R2,i+02H
%A
%A 0011 A900        R     MOV     R1,i+01H
%A
%A 0013 A800        R     MOV     R0,i
%A
%A 0015 C3                CLR     C
%A
%A 0016 120000      E     LCALL   ?C?ULCMP
%A
%A 0019 601A              JZ      ?C0010
%A
%A 001B E500        R     MOV     A,i+03H
%A
%A 001D 24FF              ADD     A,#0FFH
%A
%A 001F F500        R     MOV     i+03H,A
%A
%A 0021 E500        R     MOV     A,i+02H
%A
%A 0023 34FF              ADDC    A,#0FFH
%A
%A 0025 F500        R     MOV     i+02H,A
%A
%A 0027 E500        R     MOV     A,i+01H
%A
%A 0029 34FF              ADDC    A,#0FFH
%A
%A 002B F500        R     MOV     i+01H,A
%A
%A 002D E500        R     MOV     A,i
%A
%A 002F 34FF              ADDC    A,#0FFH
%A
%A 0031 F500        R     MOV     i,A
%A
%A 0033 80D3              SJMP    ?C0007
%A
%A                                            ; SOURCE LINE # 21
%A
%A 0035         ?C0010:
%A
%A 0035 22                RET    
%A
%A              ; FUNCTION _delay2 (END)
%A
%A 呵呵,这倒是的确可以延迟很长时间~~~但是毫无精度可言了。
%A%A
%A

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
最近文章
寂寞如雪
2009-05-19 19:01:18
夜色花
2009-05-19 18:56:22
没有爱可以重来
2009-05-19 18:54:59
推荐文章
最近访客