对于需要精确计时的场景,用C语言开发单片机应用就有些困难了,本文将在开源软件SDCC环境下对延时程序进行深入分析,不正确的地方请大家指正。
首先是单片机的三个周期:
先看这个延时程序代码:
void delay(unsigned int val) { unsigned int j = 0; for (; val>0; val--) { for (j=0; j<125; j++) { ; } } }
通过SDCC对其进行编译,生成*.asm(sdcc -c *.c)。虽然我们可以使用C语言开发单片机应用,但是并不代表我们不需要掌握汇编语言,在很多场合下虽然不需要我们编写汇编语言代码,但是看懂编译器生成的汇编语言代码的能力还是需要具备的。
_delay:
ar2 = 0x02
ar3 = 0x03
ar4 = 0x04
ar5 = 0x05
ar6 = 0x06
ar7 = 0x07
ar0 = 0x00
ar1 = 0x01
mov r2,dpl ; 寄存器寻址方式,将DPTR低字节存入r2中,2个机器周期
mov r3,dph; 寄存器寻址方式,将DPTR低字节存入r3中,2个机器周期
; comm.c:7: unsigned int j = 0;
00104$:
; comm.c:8: for (; val>0; val--)
mov a,r2; 寄存器寻址,2个机器周期
orl a,r3; r3与a进行逻辑或操作,1个机器周期
jz 00110$; 如果是零,则将r4置0,并传给a,在jz 00108$将跳到结束,2个机器周期
mov r4,#0x01; 否则执行内循环,立即数寻址,1个机器周期
sjmp 00111$; 短无条件跳转指令,2个机器周期
00110$:
movr4,#0x00; 立即数寻址,1个机器周期
00111$:
mov a,r4; 寄存器寻址,2个机器周期
jz 00108$; 判断外循环是否结束,2个机器周期
; comm.c:10: for (j=0; j<125; j++)
mov r4,#0x7D; 0x7D为125,立即数寻址,1个机器周期
mov r5,#0x00; 立即数寻址,1个机器周期
00103$:
dec r4; 把内循环改为for (j=125; j>0; j--)了,寄存器寻址,1个机器周期
cjne r4,#0xff,00121$; r4与0xff比较,不等于转移到00121$处,等于时表明需要向上借位,2个机
; 器周期
dec r5; 寄存器寻址,1个机器周期
00121$:
mov a,r4; 寄存器寻址,2个机器周期
orl a,r5; r3与a进行逻辑或操作,1个机器周期
jnz 00103$; 不为零转到内循环开始处,2个机器周期
; comm.c:8: for (; val>0; val--)
dec r2; 寄存器寻址,1个机器周期
cjne r2,#0xff,00123$; 判断是非需要借位,2个机器周期
dec r3; 寄存器寻址,1个机器周期
00123$:
sjmp 00104$; 短无条件跳转指令,2个机器周期
00108$:
ret; 2个机器周期
val*34+2个机器周期,如果采用f_osc=11.0592MHz晶振,机器周期Tcy=1.085us,则delay函数的精度为(1*34+2)*1.085us=39.06us。
|