用C51编写单片机延时函数

参考了 51单片机 Keil C 延时程序的简单研究,自己也亲身测试和计算了一些已有的延时函数。

这里假定单片机是时钟频率为12MHz,则一个机器周期为:1us.
参考了 51单片机 Keil C 延时程序的简单研究后,我们可知道, 在Keil C中获得最为准确的延时函数将是
None.gif void  delay(unsigned  char  t)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
while(--t);
ExpandedBlockEnd.gif}
反汇编代码如下:

执行DJNZ指令需要2个机器周期,RET指令同样需要2个机器周期,根据输入t,在不计算调用delay()所需时间的情况下,具体时间延时如下:
tDelay Time (us)
12×1+2 =4
22×2+2=6
N2×N+2=2(N+1)


当在main函数中调用delay(1)时, 进行反汇编如下:

调用delay()时,多执行了两条指令,其中MOV R, #data需要1个机器周期,LJMP需要2个机器周期,即调用delay()需要3us.

Keil C仿真截图与计算过程:



加上调用时间,准确的计算时间延时与Keil C仿真对比如下:(可见,仿真结果和计算结果是很接近的)
tDelay Time (us)仿真11.0592Mhz时钟(us)
13+2×1+2 =7 | 7.7(实际)7.60
23+2×2+2=9 | 9.99.76
N3+2×N+2=2N+5 | (2N+5)*1.1/
311 | 12.111.94
1535 | 38.5 37.98
100205 | 225.5222.44
255515 | 566.5558.81

也就是说,这个延时函数的精度为2us,最小的时间延时为7us,最大的时间延时为3+255×2+2=515us.  
实际中使用11.0592MHz的时钟,这个延时函数的精度将为2.2us,最小时间延时为7.7us, 最大时间延时为566.5us.
这个时间延时函数,对于与DS18B20进行单总线通信,已经足够准确了。

现在,我们将时钟换成11.0592MHz这个实际用到的频率,每个机器周期约为1.1us.
现在让我们来分析一下这个之前用过的延时函数:
None.gif // 延时函数, 对于11.0592MHz时钟, 例i=10,则大概延时10ms.
None.gif
void  delayMs(unsigned  int  i)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    unsigned 
int j;
InBlock.gif    
while(i--)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
for(j = 0; j < 125; j++);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

它的反汇编代码如下:

分析: T表示一个机器周期(调用时间相对于这个ms级的延时来说,可忽略不计)
 1   C: 0000       MOV   A,    R7       ;1T 
 2                    DEC   R7                ;1T   低8位字节减1
 3                    MOV   R2,   0x06   ;2T
 4                    JNZ   C: 0007           ;2T   若低8位字节不为0, 则跳到C: 0007
 5                    DEC   R6                ;1T   低8位字节为0, 则高8位字节减1
 6  C: 0007       ORL   A,   R2         ;1T
 7                    JZ      C:001D         ;2T   若高8位也减为0, 则RET
 8                    CLR   A                  ;1T   A清零
 9                    MOV   R4,   A        ;1T   R4放高位
10                    MOV   R5,   A        ;1T   R5放低位
11  C:000D      CLR   C                  ;1T   C清零
12                    MOV   A,   R5        ;1T   
13                    SUBB   A, #0x7d    ;1T   A  =  A - 125
14                    MOV   A,   R4        ;1T   
15                    SUBB   A,  #0x00   ;1T   A 
16                    JNC  C: 0000            ;2T   A为零则跳到C: 0000
17                    INC   R5                 ;1T   R5增1
18                    CJNE R5,#0x00, C:001B ;2T   R5 > 0 , 跳转到C:000D
19                    INC   R4                 ;1T
20  C:001B      SJMP      C:000D    ;2T
21  C:001D      RET

对于delayMs(1), 执行到第7行就跳到21行, 共需时12T, 即13.2us
对于delayMs(2), 需时9T+13T+124×10T+7T+12T = 9T+13T+1240T+7T+12T =1281T =1409.1us.
对于delayMs(3), 需时9T×(3-1)+(13T+124×10T+7T)×(3-1)+12T 
                                =1269T×(3-1)+12T=2550T=2805us.
对于delayMs(N),N>1, 需时1269T×(N-1)+12T = 1269NT-1257T=(1395.9N-1382.7)us.

利用Keil C仿真delayMs(1) = 0.00166558s = 1.67ms 截图如下:




由分析可知具体的计算延时时间与Keil C仿真延时对比如下:
iTime Delay仿真延时
113.2us1.67ms
21409.1us3.31ms
32805us4.96ms
N(1395.9N-1382.7)us
1012.6ms16.50ms
2026.5ms32.98ms
3040.5ms49.46ms
5068.4ms82.43ms
100138.2ms164.84ms
200277.8ms329.56ms
500696.6ms824.13ms
10001394.5ms1648.54ms
15002092.5ms2472.34ms
20002790.4ms3296.47ms
55.6ms8.26ms
73100.5ms120.34ms
7201003.7ms = 1s1186.74ms


计算delayMs(10)得到延时时间为:12576.3us约等于12.6ms,接近我们认为的10ms。

计算结果和仿真结果只要delayMs(1)有很大出入, 其它都接近, 在接受范围内. 

经过以上分析,可见用C语言来做延时并不是不太准确,只是不容易做到非常准确而已,若有一句语句变了,延时时间很可能会不同,因为编译程序生成的汇编指令很可能不同。


PS:
对于每条51单片机汇编指令的字长和所需机器周期汇总如下:转自:http://bbs.mcustudy.com/printpage.asp?BoardID=2&ID=1454
Appendix E - 8051 Instruction Set

Arithmetic Operations

Mnemonic Description Size Cycles
ADD A,Rn  Add register to Accumulator (ACC). 1 1
ADD A,direct  Add direct byte to ACC. 2 1
ADD A,@Ri  Add indirect RAM to ACC. 1 1
ADD A,#data  Add immediate data to ACC. 2 1
ADDC A,Rn  Add register to ACC with carry. 1 1
ADDC A,direct  Add direct byte to ACC with carry. 2 1
ADDC A,@Ri  Add indirect RAM to ACC with carry. 1 1
ADDC A,#data  Add immediate data to ACC with carry. 2 1
SUBB A,Rn  Subtract register from ACC with borrow. 1 1
SUBB A,direct  Subtract direct byte from ACC with borrow 2 1
SUBB A,@Ri  Subtract indirect RAM from ACC with borrow. 1 1
SUBB A,#data  Subtract immediate data from ACC with borrow. 2 1
INC A  Increment ACC. 1 1
INC Rn  Increment register. 1 1
INC direct  Increment direct byte. 2 1
INC @Ri  Increment indirect RAM. 1 1
DEC A  Decrement ACC. 1 1
DEC Rn  Decrement register. 1 1
DEC direct  Decrement direct byte. 2 1
DEC @Ri  Decrement indirect RAM. 1 1
INC DPTR  Increment data pointer. 1 2
MUL AB  Multiply A and B Result: A <- low byte, B <- high byte. 1 4
DIV AB  Divide A by B Result: A <- whole part, B <- remainder.  1 4
DA A  Decimal adjust ACC. 1 1
Logical Operations

Mnemonic Description Size Cycles
ANL A,Rn  AND Register to ACC. 1 1
ANL A,direct  AND direct byte to ACC. 2 1
ANL A,@Ri  AND indirect RAM to ACC. 1 1
ANL A,#data  AND immediate data to ACC. 2 1
ANL direct,A  AND ACC to direct byte. 2 1
ANL direct,#data  AND immediate data to direct byte. 3 2
ORL A,Rn  OR Register to ACC. 1 1
ORL A,direct  OR direct byte to ACC. 2 1
ORL A,@Ri  OR indirect RAM to ACC. 1 1
ORL A,#data  OR immediate data to ACC. 2 1
ORL direct,A  OR ACC to direct byte. 2 1
ORL direct,#data  OR immediate data to direct byte. 3 2
XRL A,Rn  Exclusive OR Register to ACC. 1 1
XRL A,direct  Exclusive OR direct byte to ACC. 2 1
XRL A,@Ri  Exclusive OR indirect RAM to ACC. 1 1
XRL A,#data  Exclusive OR immediate data to ACC. 2 1
XRL direct,A  Exclusive OR ACC to direct byte. 2 1
XRL direct,#data  XOR immediate data to direct byte. 3 2
CLR A  Clear ACC (set all bits to zero). 1 1
CPL A  Compliment ACC. 1 1
RL A  Rotate ACC left. 1 1
RLC A  Rotate ACC left through carry. 1 1
RR A  Rotate ACC right. 1 1
RRC A  Rotate ACC right through carry. 1 1
SWAP A  Swap nibbles within ACC. 1 1
Data Transfer

Mnemonic Description Size Cycles
MOV A,Rn  Move register to ACC. 1 1
MOV A,direct  Move direct byte to ACC. 2 1
MOV A,@Ri  Move indirect RAM to ACC. 1 1
MOV A,#data  Move immediate data to ACC. 2 1
MOV Rn,A  Move ACC to register. 1 1
MOV Rn,direct  Move direct byte to register. 2 2
MOV Rn,#data  Move immediate data to register. 2 1
MOV direct,A  Move ACC to direct byte. 2 1
MOV direct,Rn  Move register to direct byte. 2 2
MOV direct,direct  Move direct byte to direct byte. 3 2
MOV direct,@Ri  Move indirect RAM to direct byte. 2 2
MOV direct,#data  Move immediate data to direct byte. 3 2
MOV @Ri,A  Move ACC to indirect RAM. 1 1
MOV @Ri,direct  Move direct byte to indirect RAM. 2 2
MOV @Ri,#data  Move immediate data to indirect RAM. 2 1
MOV DPTR,#data16  Move immediate 16 bit data to data pointer register. 3 2
MOVC A,@A+DPTR  Move code byte relative to DPTR to ACC (16 bit address). 1 2
MOVC A,@A+PC  Move code byte relative to PC to ACC (16 bit address). 1 2
MOVX A,@Ri  Move external RAM to ACC (8 bit address). 1 2
MOVX A,@DPTR  Move external RAM to ACC (16 bit address). 1 2
MOVX @Ri,A  Move ACC to external RAM (8 bit address). 1 2
MOVX @DPTR,A  Move ACC to external RAM (16 bit address). 1 2
PUSH direct  Push direct byte onto stack. 2 2
POP direct  Pop direct byte from stack. 2 2
XCH A,Rn  Exchange register with ACC. 1 1
XCH A,direct  Exchange direct byte with ACC. 2 1
XCH A,@Ri  Exchange indirect RAM with ACC. 1 1
XCHD A,@Ri  Exchange low order nibble of indirect RAM with low order nibble of ACC. 1 1
Boolean Variable Manipulation

Mnemonic Description Size Cycles
CLR C  Clear carry flag. 1 1
CLR bit  Clear direct bit. 2 1
SETB C  Set carry flag. 1 1
SETB bit  Set direct bit. 2 1
CPL C  Compliment carry flag. 1 1
CPL bit  Compliment direct bit. 2 1
ANL C,bit  AND direct bit to carry flag. 2 2
ANL C,/bit  AND compliment of direct bit to carry. 2 2
ORL C,bit  OR direct bit to carry flag. 2 2
ORL C,/bit  OR compliment of direct bit to carry. 2 2
MOV C,bit  Move direct bit to carry flag. 2 1
MOV bit,C  Move carry to direct bit. 2 2
JC rel  Jump if carry is set. 2 2
JNC rel  Jump if carry is not set. 2 2
JB bit,rel  Jump if direct bit is set. 3 2
JNB bit,rel  Jump if direct bit is not set. 3 2
JBC bit,rel  Jump if direct bit is set & clear bit. 3 2
Program Branching

Mnemonic Description Size Cycles
ACALL addr11  Absolute subroutine call. 2 2
LCALL addr16  Long subroutine call. 3 2
RET  Return from subroutine. 1 2
RETI  Return from interrupt. 1 2
AJMP addr11  Absolute jump. 2 2
LJMP addr16  Long jump. 3 2
SJMP rel  Short jump (relative address). 2 2
JMP @A+DPTR  Jump indirect relative to the DPTR. 1 2
JZ rel  Jump relative if ACC is zero. 2 2
JNZ rel  Jump relative if ACC is not zero. 2 2
CJNE A,direct,rel  Compare direct byte to ACC and jump if not equal. 3 2
CJNE A,#data,rel  Compare immediate byte to ACC and jump if not equal. 3 2
CJNE Rn,#data,rel  Compare immediate byte to register and jump if not equal. 3 2
CJNE @Ri,#data,rel  Compare immediate byte to indirect and jump if not equal. 3 2
DJNZ Rn,rel  Decrement register and jump if not zero. 2 2
DJNZ direct,rel  Decrement direct byte and jump if not zero. 3 2
Other Instructions

Mnemonic Description Size Cycles
NOP  No operation. 1 1
其它可查看《单片机基础》-李广弟等编著,P70 “MCS-51单片机指令汇总”

转载于:https://www.cnblogs.com/fengmk2/archive/2007/03/12/672477.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值