本帖最后由 rengran 于 2023-11-21 16:23 编辑
1、 为什么delay_ms()函数中的1ms定时的循环次数是6000? 在视频中,我们看到有一个延时函数delay_ms,其中一毫秒的计算方法中使用到了一个while(--i)的空循环,其中循环次数i=系统时钟频率SYSclk/6000,(见图6.1,)z至于为什么是6000,可能有人就会说一点点的试出来的,其实不然。如果从1试到6000,那得多大的工作量啊。本质上还得从程序运行指令中入手,确定循环次数,然后通过实验进行调整。 图6.1 在KEIL中有调试功能,利用调试功能,查看汇编指令的运行,我们就能得出6000这个数是怎么来的了。 首先,程序运行到delay_ms(3)函数处,会运行两个指令: MOV WR6 #0x0003; (1) LCALL delay_ms(C:0x1EF9); (2) (1)指令是将立即数3存到WR6寄存器当中;(2)指令是一个调用指令,就是下一步将要进入到0x1EF9处去执行,这也就是说,delay_ms函数入口就在0x1EF9处(见图6.2) 图6.2 其次就是进入到延时函数delay_ms中去执行。如图6.3所示,延时函数的执行位置在0x1EF9处,首先执行了一条语句:MOV WR4 WR6;然后就进入了i的赋值语句,其后的四条语句就是1ms的循环,循环的次数就是i的值。
图6.3
图6.3 ①0xFF1EFF 7D13 MOV WR2,WR6 ②0xFF1F01 1B14 DEC WR2,#0x01 ③xFF1F03 7D31 MOV WR6,WR2 ④0xFF1F05 78F8 JNE C:0x1EFF 查询STC32G的手册得知,从寄存器到寄存器的MOV的指令需要的时钟数是1个时钟数,一个减一指令DEC也需要一个时钟数,不等跳转指令JNE需要三个时钟数,所以每一次循环需要6个时钟。假设一个时钟周期为T0,那么i次循环的总时长就是6*T0*i。T0=1/Fosc,i=Fosc/N,总时长若为0.001秒,即6*T0*i=0.001,带入后为:6*1/Fosc*Fosc/N=0.001,所以N的值为6000。
2、 delay_ms()函数为什么不准确? 有以下几个方面: ① 从进入delay_ms(J)函数到跳出该函数,要执行 MOV WR6 #0x0003; 1次 每次1个T0 LCALL delay_ms(C:0x1EF9); 1次 每次3个T0 MOV WR4 WR6; J次 每次1个T0 1ms的循环语句 假定这地方的1ms是准确的 还有下面判断J是否为0的四个语句语句 MOV WR2,WR4 J次 每次1个T0 DEC WR2,#0x01 J次 每次1个T0 MOV WR4,WR2 J次 每次1个T0 JNE C:0x1EFB J次 每次3个T0 计时Jms,其实总时间为J(毫秒)+(10*J+4)*T0。所以说定时时间越长的话,J就要越大,多用的时间(10*J+4)*T0也就越大。这就是导致这个函数不准确的地方之一。 ② 由于i=Fosc/N,i和N都是整数,如果Fosc/N的结果不是整数的话,系统会自动舍去小数的部分,舍去的部分也是导致定时时间越长误差越大的原因之一。
|