汇编语言延时
凉凉不冷 Lv4

汇编语言DELAY函数

重要指令理解

1
DJNZ	RN,REL

先将寄存器,中的数减,判断结果是否为

不为程序就跳转到行标,的地方执行,

否则,为就不转移,继续执行下一条指令。

范例.50ms延时程序

  • 汇编语言的一大优势就是能够精确控制程序的执行时间

  • 在使用晶振时,一个机器周期为,可用双重循环方法的延时50ms程序

1
2
3
4
5
6
7
DELAY:
DEL: MOV R7,#200 ;1个机器周期,执行1次,耗时1μs
D1: MOV R6,#125 ;1个机器周期,执行200次,耗时1μs*200=200μs
DJNZ R6,$ ;2个机器周期,执行125*200次,
;耗时2μs*125*200=50000μs
DJNZ R7,D1 ;2个机器周期,执行200次,耗时2μs*200=400μs
RET ;2个机器周期,执行1次,耗时2μs

以上延时程序的延时时间为[1+200+50000+400+2]μs=50.603μs

需要注意的是,用软件实现延时程序,不允许有中断,否则将严重影响定时的准确性。

间隔1s的延时函数设计

MOV赋值的最大数为,可考虑将50ms延时程序进行改进,

方案一

1
2
3
4
5
6
7
8
9
10
11
DELAY:	
MOV R5,#20 ;1个机器周期,执行1次,耗时1μs
D1: MOV R7,#200 ;1个机器周期,执行20次,耗时1μs*20 = 20μs
D2: MOV R6,#125 ;1个机器周期,执行200*20次,
;耗时1μs*200*20 = 4000μs
DJNZ R6,$ ;2个机器周期,执行125*200*20次
;耗时2μs*125*200*20 = 1000000μs
DJNZ R7,D2 ;2个机器周期,执行200*20次,耗时2μs*200*20 = 8000μs
DJNZ R5,D1 ;2个机器周期,执行20次,耗时2μs*20 = 40μs
RET ;2个机器周期,执行1次,耗时2μs
END

现仅针对进行分析,其延时时间计算公式如下

但可以看出直接套用之前的公式进行多次循环其误差是比较大的,所以我们可以改变寄存器中数的组合方式,以进行优化;

方案二

1
2
3
4
5
6
7
8
9
DELAY:
MOV R5,#10 ; 1
D1: MOV R7,#223 ; 1 * 10 = 10
D2: MOV R6,#223 ; 1 * 223 *10 = 2230
DJNZ R6,$ ; 2 * 223 * 223 *10 = 994580 (49729 * 2)
DJNZ R7,D2 ; 2 * 223 *10 = 4460
DJNZ R5,D1 ; 2 * 10 = 40
RET ; 2
END ; 1,001,323

方案三

从网上随便扒来的一种组合情况

1
2
3
4
5
6
7
8
9
DELAY:
MOV R7,#10 ;1个机器周期,执行1次,耗时1μs
D1: MOV R6,#200 ;1个机器周期,执行10次,耗时1μs*10 = 10μs
D2: MOV R5,#248 ;1个机器周期,执行200*10次,耗时1μs*200*10 = 2000μs
DJNZ R5,$ ;2个机器周期,执行248*200*10次,耗时2μs*240*200*10 = 960000μs
DJNZ R6,D2 ;2个机器周期,执行200*10次,耗时2μs*200*10 = 40000μs
DJNZ R7,D1 ;2个机器周期,执行10次,耗时1μs*10 = 10μs
RET ;2个机器周期,执行1次,耗时2μs
END

对比方案三和方案二可以看出,方案二的误差是最小的,所以我们最后采用方案二来作为我们的延时函数

最终完整代码呈现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
      org   00h
Start:
MOV P2,#0FEH ;打开P2_0的LED
ACALL DELAY ;延迟1s
MOV P2,#0FFH ;熄灭ED
ACALL DELAY ;延迟1s
AJMP START ;无限循环
DELAY:
MOV R5,#10 ; 1
D1: MOV R7,#223 ; 1 * 10 = 10
D2: MOV R6,#223 ; 1 * 223 *10 = 2230
DJNZ R6,$ ; 2 * 223 * 223 *10 = 994580 (49729 * 2)
DJNZ R7,D2 ; 2 * 223 *10 = 4460
DJNZ R5,D1 ; 2 * 10 = 40
RET ; 2
END ; 1,001,314

总结

在刚开始接触汇编延时的时候,其实还是比较懵逼的(前三节课都在zhua梦jio),这次任务总的来说,重难点在这条指令上,再其次就是标号的跳转嵌套起来容易出错;

在这里总结一个方法:

DJNZ RN,REL这一指令中,跳转至REL时,可以将次语句与REL语句看成一个括号;

括号内部指令的执行次数都要乘以RN

通过如上方法可以快速地计算出每条指令的执行次数;

以此种方法来看嵌套的情况,也是迎刃而解;

同时还能检查出代码中的BUG,譬如我在写代码时犯了如下错误:

1
2
3
4
5
6
7
8
9
DELAY:
MOV R7,#223 ; 1
D2: MOV R5,#10 ; 1 * 10 = 10
D1: MOV R6,#223 ; 1 * 223 *10 = 2230
DJNZ R6,$ ; 2 * 223 * 223 *10 = 994580 (49729 * 2)
DJNZ R7,D2 ; 2 * 223 *10 = 4460
DJNZ R5,D1 ; 2 * 10 = 40
RET ; 2
END ; 1,001,323

此代码中有两段错误,分别是

  • 的位置相反
    • 用视为括号的方式来理解,很容易发现这样子的括号是自相矛盾的,两个括号重合了
  • 的位置相反
    • DJNZ RN,RELRN是循环条件,也是循环次数,,循环条件一定要在括号的外部初始化!!!

掌握如上提到的两个主要问题(再提一遍不嫌啰嗦):

  • DJNZ RN,REL这个语句与之对应的REL视为括号,括号内部的执行次数都要乘以RN
  • 循环条件一定要在括号的外部初始化!

最终的仿真结果如下

1sDelay

完事儿~收工啦!

参考网站:

djnz_百度百科 (baidu.com)

汇编语言软件延时1s的实现方法_汇编语言_脚本之家 (jb51.net)

lucy&david

 Comments