作为单片机心得的第二章,我们聊聊C语言的延时程序。又被问起汇编的事,汇编写的延时程序可以一个个指令数着花了多少时间,能很精确。但也花大力气。 我下面要谈的方法是一个通用快捷的方法,同时也能达到接近汇编的精度,重点是这点精度就足够的了。延时程序虽然不能做时钟,但是时序方面总是要用到延时程序的,这非常重要。例如Lcd1602和Lcd12864。这些事后的章节再说。
工具:一台能测量频率的万能表(有20KHz档就足够)。如果有一台示波器那更精确。(示波器能显示频率值,更准确)
软件:STC-ISP 这是宏晶公司的51专用烧录软件。它还带有好多辅助功能。我们要的正是在辅助功能里。可以到STCMCU官网下载。
回想初学单片机时,看到这个延时程序,我有两个想法,一个是:这也太浪费运算,这延时过程中什么也不能运算;另一个是:这个延时的时间是怎么定的,它准不准的?
我后来才知道,这点延时的时间,要是少到单片机的几个至几百个周期,这点时间真的没法利用的。还有就是范例上的延时程序是经验值,早就过时了。
我是个对待编程严谨的人,也不愿浪费算法效率,虽说延时序程是无奈,但也要压榨到精确的时间,防止浪费。我见过好多代码都写Delay(5),这实际上是延时8ms,我用5us的延时程序替代它,运行正常。8ms/5us=8000/5=1600倍,我的乖乖。这说明写范例也伤不起,能运行,管你初学者学得怎样,给你能用的代码就可以。
//////////////////////// 华丽的分界线 ////////////////////
下面说谈谈实操。
(一)测量
我先说说怎么测量延时代码的延时值,这我的方法也是有误差的,误差1.5-2us左右。我们在main里面写一个循环:
while(1)
{
Delay(x);
LED=!LED;
}
当然,你要将LED和Delay定义一下,这不多交代。
我们要有两个前提条件:一个条件是MCU芯片是什么,另一个条件是系统时钟是多少。(8051就是晶振频率;用内部RC产生时钟的80C51或STC15F则指IRC频率;stm32指驱动内核的时钟。)
我说的有点多了,理解不了。那么我缩小一下范围,只谈 AT89c51 和stc-89c51 stc-90c51等。
假设晶振频率为11.0592MHz (这个最常用)
我们先定义X的值;例如X=1;
编译好,烧录,再运行。用万能表测量LED端输出的频率 F 。
那么这个Delay(1)延时的时间 T 为:(注:T的单位是s,F的单位是Hz,单位很关键。)
公式1:T=1/F/2/X
(恶补时间单位关系:1s(秒)=1000ms(毫秒)=1000*1000us(微秒);换个角度看 m=0.001,u=0.000001)
(恶补频率单位关系:1MHz=1000KHz=1000*1000Hz;换个角度看 K=1000,M=1000000)
有一点技巧,当 F 用 Hz 做单位,则 T 计算出来,单位就是 s 。
当 F 用 KHz 做单位,则 T 计算出来,单位就是 ms 。
——我的数学头脑已经要爆裂了!!
多说一句,当X=1时,算得的 T 结果是最大的。X的值从1到几十,当值增大,结果 T 会慢慢减少。当X>50时,T值会稳定恒定。所以最好将X定在50。
你也可自己试1,10,50,100,150,200的结果。
这是为什么呢?
void Delay(uchar n); 有参数n,当调用Delay时,实现cpu运行时,要先压栈cpu多个寄存器,再分配在栈上写入n的值,这要花时间 ta 。Delay内部做循环,每次循环花的时间是 tz 。当Delay返回,要出栈cpu多个寄存器,花时间 tb 。那么Delay的实际延时间是 T = ta + n*tz + tb = ( ta+tb ) + n*tz 。很明显的多了ta+tb的时间出来。所以当n=1时,结果明显的误差偏大,当n>50时误差才会不显大。
听不明白的话……。当你没问过好了。
(二)因素
得到这个公式,还不是全部,我们要知道两条准则:
准则1:芯片的等级相同,计算的结果是可以互用的。 AT89c51 和stc-89c51 stc-90c51是在同一级别的。
准则2:同一块芯片,不同系统时钟则延时代码不通用。
上面两句话,怎么理解呢?
就是说,同一级别的指令运行速度一样,才能让延时程序通用。因为延时程序是用指令运行来实现的。另外系统时钟不同,即使同一指令,也会耗时不等,所以延时也不等。芯片和系统时钟这两个因素决定了同一延时程序的不同时间值。
所以,可以为系统时钟11.0592MHz写一个 Delay_11_0592MHz.c
又为6MHz写一个 Delay_6MHz.c 等等,如些类推。当你用上哪个晶振再写也行。
不同的80c51也可以兼容的话,就要用条件编译的方法将不同芯片专用的Delay代码区分开。但可以用上同一个 Delay.H
不懂条件编译的方法,还是老实的分开不同的C文件好了。(因为内容的统一性的原因,我不打算在本章节谈条件编译。)
(三)修正Delay
这很关键。
我们要有一个靠谱的代码。因为有些代码不好调节,有些会被编译器“代码优化”掉。
100us以下不能用Delayus(n)来实现,因为上面说过压栈出栈时间,造成的误差对于这个时间段来说太大了。无解。只能用固定的代码。
我们用STC-ISP来算延时程序好了。若想程序简单化也可以每次编程都它算一下。
我自己倒是想写成一个函数库。这是后话。
见下图。
第一步:选定8051程序集。
说好的AT89C51呢,不见。这是STC公司的,自然没有AT的事。但你要知道AT89C51在运行速度上与STC89C51一样的,所以是同一等级,所以一样选STC-Y1就可以了。其它的STC51可以看右侧的解释。也可看下图
第二步 系统频率
说的就是系统时钟的频率,这个好办。不多说。
第三步 定时长度
写上你要延时的时间值。毫秒(ms),微秒(us)
STC-Y1(11.0592MHz)要注意最小是2us,不能再小了。
第四步 生成C代码,再复制代码,贴到你的C文件里。
我只说了怎么解决精准代码。没说更方便的方法。这将在下一编解说。
维京猎人 2017-8-27