汇编语言DELAY函数
重要指令理解
先将寄存器,中的数减,判断结果是否为,
不为程序就跳转到行标,的地方执行,
否则,为就不转移,继续执行下一条指令。
范例.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,REL的RN是循环条件,也是循环次数,,循环条件一定要在括号的外部初始化!!!
掌握如上提到的两个主要问题(再提一遍不嫌啰嗦):
- 将DJNZ RN,REL这个语句与之对应的REL视为括号,括号内部的执行次数都要乘以RN
- 循环条件一定要在括号的外部初始化!
最终的仿真结果如下
完事儿~收工啦!
参考网站:
djnz_百度百科 (baidu.com)
汇编语言软件延时1s的实现方法_汇编语言_脚本之家 (jb51.net)