[KungFu8位 MCU] 用C51编写单片机延时函数

[复制链接]
867|5
 楼主| 123ycli 发表于 2019-6-27 17:27 | 显示全部楼层 |阅读模式
本帖最后由 123ycli 于 2019-6-27 17:32 编辑

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

执行DJNZ指令需要2个机器周期,RET指令同样需要2个机器周期,根据输入t,在不计算调用delay()所需时间的情况下,具体时间延时如下: 156395d148d3603fbb.png
 楼主| 123ycli 发表于 2019-6-27 17:34 | 显示全部楼层
当在main函数中调用delay(1)时, 进行反汇编如下:
433665d148d7c794d9.png
调用delay()时,多执行了两条指令,其中MOV R, #data需要1个机器周期,LJMP需要2个机器周期,即调用delay()需要3us.
 楼主| 123ycli 发表于 2019-6-27 17:35 | 显示全部楼层
Keil C仿真截图与计算过程:
518315d148dadad4ae.png
514625d148db95f882.png
加上调用时间,准确的计算时间延时与Keil C仿真对比如下:(可见,仿真结果和计算结果是很接近的)
805235d148dd6f15ca.png
也就是说,这个延时函数的精度为2us,最小的时间延时为7us,最大的时间延时为3+255×2+2=515us.   
实际中使用11.0592MHz的时钟,这个延时函数的精度将为2.2us,最小时间延时为7.7us, 最大时间延时为566.5us.
这个时间延时函数,对于与DS18B20进行单总线通信,已经足够准确了。
 楼主| 123ycli 发表于 2019-6-27 17:38 | 显示全部楼层
现在,我们将时钟换成11.0592MHz这个实际用到的频率,每个机器周期约为1.1us.
现在让我们来分析一下这个之前用过的延时函数:
  1. //延时函数, 对于11.0592MHz时钟, 例i=10,则大概延时10ms.
  2. void delayMs(unsigned int i)
  3. {
  4.     unsigned int j;
  5.     while(i--)
  6.     {
  7.         for(j = 0; j < 125; j++);
  8.     }
  9. }

它的反汇编代码如下:
126025d148e4b5345a.png
 楼主| 123ycli 发表于 2019-6-27 17:40 | 显示全部楼层
分析: T表示一个机器周期(调用时间相对于这个ms级的延时来说,可忽略不计)
  1. 1  C:0000      MOV   A,    R7       ;1T
  2. 2                   DEC   R7                ;1T   低8位字节减1
  3. 3                   MOV   R2,   0x06   ;2T
  4. 4                   JNZ   C:0007          ;2T   若低8位字节不为0, 则跳到C:0007
  5. 5                   DEC   R6                ;1T   低8位字节为0, 则高8位字节减1
  6. 6 C:0007      ORL   A,   R2         ;1T
  7. 7                   JZ      C:001D         ;2T   若高8位也减为0, 则RET
  8. 8                   CLR   A                  ;1T   A清零
  9. 9                   MOV   R4,   A        ;1T   R4放高位
  10. 10                   MOV   R5,   A        ;1T   R5放低位
  11. 11 C:000D      CLR   C                  ;1T   C清零
  12. 12                   MOV   A,   R5        ;1T   
  13. 13                   SUBB   A, #0x7d    ;1T   A = A-125
  14. 14                   MOV   A,   R4        ;1T   
  15. 15                   SUBB   A,  #0x00   ;1T   A
  16. 16                   JNC  C:0000           ;2T   A为零则跳到C:0000
  17. 17                   INC   R5                 ;1T   R5增1
  18. 18                   CJNE R5,#0x00, C:001B ;2T   R5>0, 跳转到C:000D
  19. 19                   INC   R4                 ;1T
  20. 20 C:001B      SJMP      C:000D    ;2T
  21. 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 截图如下:

880065d148eec6cd2f.png
235215d148f0081924.png
 楼主| 123ycli 发表于 2019-6-27 17:42 | 显示全部楼层
由分析可知具体的计算延时时间与Keil C仿真延时对比如下:
26555d148f573e583.png
计算delayMs(10)得到延时时间为:12576.3us约等于12.6ms,接近我们认为的10ms。

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

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

本版积分规则

40

主题

324

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部