学习冲哥视频(6):1ms延时函数为什么要循环6000次?为什么有误差?
本帖最后由 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)函数处,会运行两个指令: MOVWR6#0x0003; (1) LCALLdelay_ms(C:0x1EF9); (2)(1)指令是将立即数3存到WR6寄存器当中;(2)指令是一个调用指令,就是下一步将要进入到0x1EF9处去执行,这也就是说,delay_ms函数入口就在0x1EF9处(见图6.2)
图6.2其次就是进入到延时函数delay_ms中去执行。如图6.3所示,延时函数的执行位置在0x1EF9处,首先执行了一条语句:MOVWR4 WR6;然后就进入了i的赋值语句,其后的四条语句就是1ms的循环,循环的次数就是i的值。
图6.3①0xFF1EFF7D13 MOV WR2,WR6②0xFF1F011B14 DEC WR2,#0x01③xFF1F037D31 MOV WR6,WR2④0xFF1F0578F8 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)函数到跳出该函数,要执行 MOVWR6#0x0003; 1次 每次1个T0 LCALL delay_ms(C:0x1EF9); 1次 每次3个T0 MOVWR4 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的结果不是整数的话,系统会自动舍去小数的部分,舍去的部分也是导致定时时间越长误差越大的原因之一。
delay_ms延时函数有误差的第三个原因:
③由于delay_ms延时函数需要在主程序中调用,当程序中含有中断,延时函数执行过程中会被中断打断去系统去执行中断,此时的delay_ms会停止运行,所以定时的实际时间就会延长,导致定时不准。
页:
[1]