89S51单片机实现误差几微秒的计时程序
我们都知道单片机是工作在脉冲之下,而晶振这个电子元件就是核心,所以单片机里的计时计数器都是通过晶振的脉冲数量来计算的,晶振的规格主要就就是它的振荡频率,有12MHZ,11.0592MHZ,8MHZ等等,我们知道,机器周期就是通过 12 * 晶振的频率的倒数 得到的,比如使用12MHZ的单片机它的机器周期就是1微秒,也就是执行一个单指令周期需要的时间。说到这里很多初学者会有个和我开始一样的疑问,就是为什么要搞个1.0592MHZ的晶振呢?用整数不好么?我开始也是摸不着头脑,但是当我学习到串口通讯的时候,就恍然大悟了,具体为什么这里就不介绍了,不明白的可以去了解下串口通讯,相信你也会恍然大悟。
我的实验板上用的是89S51芯片,使用的晶振是8MHZ的,所以我的89S51执行一个单周期指令就需要12* 1/8 = 1.5微秒,好了有了这个就可以开始了:
首先我们用汇编写一个延迟程序,该程序有三个参数,分别为x,y,z,用的是三个循环镶套的结构,至于为什么不用2个,,很简单,因为我们要赋值的寄存器是8位的,所以范围只能是256,如果用2层那么最大只能是256*256=65536,而我们的一秒钟可是1000000微秒,所以显然不够,用三层我们就可以得到最大256*256*256=16777216,这样才够用,好了分析下面的这个延时程序到底延迟多久:
我们的目的就是要使得这个延迟尽量达到1000000微秒的时间,所以x,y,z的值还不确定,先不管,先分析下每一行执行的时间,说明一下,这里的mov是单周期指令,执行一次就是1.5微秒(别问我怎么来的,前面算的),而djnz是个双周期指令,执行一次就是1.5*2=3微秒,知道这个了就看下面程序每行后面的具体计算时间
YS_1S: mov r0,#x 1.5 (只执行了一次,所以1.5微秒)
d1: mov r1,#y 1.5*x (执行了x次,每次1.5微秒)
d2: mov r2,#z 1.5*x*y 同上
d3: djnz r2,d3 2*1.5*x*y*z (双周期指令所以还要乘2)
djnz r1,d2 2*1.5*x*y 同上
djnz r0,d1 2*x 同上
ret 1.5
现在我们把每行相加,1.5 + 1.5*x + 1.5*x*y + 2*1.5*x*y*z + 2*1.5*x*y + 2*x + 1.5得到这个公式,现在要求这个公式算出来大概等于 1000000, 我们把这个公式加以简化,就的到如下方程:
3+4.5*x+4.5*x*y+3*x*y*z = 1000000
现在我们要求x,y,z的值,怎么求呢?随便用自己熟悉的语言,用穷举法,我是用C++写的,如下:
int x,y,z;
for(x=1;x<=256;x++)
for(y=1;y<=256;y++)
for(z=1;z<=256;z++)
//这里我没写1000000是因为很有可能会出现这个方程不会完全等于1000000,所以我还是取了个 范围,所以为什么题目说是几微秒的误差了,
if(((999990>=(3+4.5*x+4.5*x*y+3*x*y*z)))&&((1000000<=(3+4.5*x+4.5*x*y+3*x*y*z))))
{
cout <
最后我得到的是x=205 y=171 z=8;这三个参数放到公式里算出来是999993,所以这个延迟就误差7微秒,把这三个值放到开始的延迟程序对应的xyz里,烧写到89S51里,随便用个发光2极管做闪烁,就可以看到效果了,再用秒表做一个粗造的测试,就OK了。
其实,我这个方法很笨,但我的目的是学习,而且自己的学习有点小小的成果后,拿出来和别人分享,哪怕是错误的,至少我知道错误在哪里,改正错误,这又是一种进步。
评论