精确延时.docx
《精确延时.docx》由会员分享,可在线阅读,更多相关《精确延时.docx(13页珍藏版)》请在冰豆网上搜索。
精确延时
KEIL单片机C语言精确延时值的计算
2011-11-2020:
54|发布者:
pcb_dz|查看:
111|评论:
0|来自:
关于单片机C语言的精确延时,网上很多都是大约给出延时值没有准确那值是多少,也就没有达到精确高的要求,而51hei给出的本函数克服了以上缺点,能够精确计数出要延时值且精确达到1us,本举例所用CPU为STC12C5412系列12倍速的单片机,只要修改一下参数值其它系例单片机也通用,适用范围宽。
共有三条延时函数说明如下:
函数调用分两级:
一级是小于10US的延时,二级是大于10US的延时
//====================小于10US的【用1US级延时】====================
//----------微秒级延时---------
for(i=X;i>X;i--)延时时间=(3+5*X)/12 提示(单位us, X不能大于255)
//================大于10US<小于21.9955Ms的可用【10US级延时函数】===========
voidDelay10us(ucharMs)
{
uchardatai;
for(;Ms>0;Ms--)
for(i=26;i>0;i--);
}
i=[(延时值-1.75)*12/Ms-15]/4
如想延时60US则 i=[(60-1.75)*12/6-15]/4=25.375≈26;修改i的值=26,再调用上面的【10US级延时函数】Delay10us(6);则就精确延时60US;
如果想延时64US可以用这二种函数组合来用:
Delay10us(6);for(i=9;i>X;i--) 共延时64US
//==============对于大于20Ms的可用中断来实现程序运行比较好===============
中断用定时器0,1Ms中断:
voidtimer0(void)interrupt1
{
TL0=(0xffff-1000+2)%0x100;TH0=(0xffff-1000+2)/0x100;//每毫秒执行一次
if(DelayMs_1>0)DelayMs_1--;//大于20Ms延时程序
}
函数调用
voidDelayMs(uinta)//延时a×1(ms)的时间。
{
DelayMs_1=a;
while(DelayMs_1);
}
如果延时50Ms则函数值为 DelayMs(50)
//+++++++++++++++++++++++++公式推算来由++++++++++++++++++++++++++++
二级延时C语言反汇编见上传图1:
//=======================STC单片机延时指令时间====================
指今 时间(1us) 指今 时间(1us)
MOV A Rn 1 SETB C 1
MOV Rn #data 2 SUBB A #data 2
DJNZ Rn rel 4
Dec Rn 3 SJMP rel 3
JC rel 3 MOV Rndirect 4
Lcalladdr16 6 RET 4
//--------------
JNZ 3 XRL 2
SUBB 3
使用Delay10us(ucharMs)最多能延时21995.5us;
其中循环外函数调用与返回用的时间=(6+4)进入+退出(1+1+2+3+,4)=10+11=21/12=1.75us;
循环内函数时间MS=Ms*(1+1+2+3,+2+4*I+3+3)/12=Ms*(15+4*I)/12;
所以如果延时60us时用【10US级延时函数】I的值为:
60=循环外时间+循环内时间=1.75us+Ms*(15+4*I)/12;
则i=[(60-1.75)*12/6-15]/4=25.375≈26即如果Ms=6,i=26;
本算法中的12是STC12单片机与普通标准速率比。
C语言写程序时,初学者遇到的一个难题时精确延时程序的设计。
我刚开始用C语言写程序时同样遇到了这个问题,后来参考了一些文章和实际设计后才知道了精确延时程序的设计。
我现在就用两种方法来实现,一种是while()语句,另一种是for()语句,这两种语句均可产生汇编语句中的DJNZ语句,以12MHZ晶振为例(说明:
在编写C程序时,变量尽量使用unsignedchar,如满足不了才使用unsignedint):
1.delay=99;while(--delay);
产生的汇编代码为:
000FH MOV 08H,#63H
0012H DJNZ 08H,0012H
这样产生的延时时间为:
(99+1)×2us。
最小延时时间为2us,若加上对delay赋值语句,则最小为4us。
2.for(i=delay;i>0;i--);
产生的汇编代码同while()语句。
下面来举例几个延时函数:
一.500ms延时子程序
voiddelay500ms(void)
{
unsignedchari,j,k;
for(i=15;i>0;i--)
for(j=202;j>0;j--)
for(k=81;k>0;k--);
}
产生的汇编代码:
C:
0x0800 7F0F MOV R7,#0x0F
C:
0x0802 7ECA MOV R6,#0xCA
C:
0x0804 7D51 MOV R5,#0x51
C:
0x0806 DDFE DJNZ R5,C:
0806
C:
0x0808 DEFA DJNZ R6,C:
0804
C:
0x080A DFF6 DJNZ R7,C:
0802
C:
0x080C 22 RET
计算分析:
程序共有三层循环
一层循环n:
R5*2=81*2=162us DJNZ 2us
二层循环m:
R6*(n+3)=202*165=33330us DJNZ 2us+R5赋值1us=3us
三层循环:
R7*(m+3)=15*33333=499995us DJNZ 2us+R6赋值1us=3us
循环外:
5us 子程序调用2us+子程序返回2us+R7赋值1us =5us
延时总时间=三层循环+循环外=499995+5=500000us=500ms
计算公式:
延时时间=[(2*R5+3)*R6+3]*R7+5
二.200ms延时子程序
voiddelay200ms(void)
{
unsignedchari,j,k;
for(i=5;i>0;i--)
for(j=132;j>0;j--)
for(k=150;k>0;k--);
}
三.10ms延时子程序
voiddelay10ms(void)
{
unsignedchari,j,k;
for(i=5;i>0;i--)
for(j=4;j>0;j--)
for(k=248;k>0;k--);
}
四.1s延时子程序
voiddelay1s(void)
{
unsignedcharh,i,j,k;
for(h=5;h>0;h--)
for(i=4;i>0;i--)
for(j=116;j>0;j--)
for(k=214;k>0;k--);
}
以上的这先希望对大家有帮组,如有不足之处请指出,如有更好的方法也可以告诉我,大家一起分享
C语言最大的缺点就是实时性差,我在网上到看了一些关于延时的讨论,其中有篇文章51单片机KeilC延时程序的简单研究,作者:
InfiniteSpaceStudio/isjfk
写得不错,他是用while(--i);产生DJNZ来实现精确延时,后来有人说如果while里面不能放其它语句,否则也不行,用do-while就可以,具体怎样我没有去试.所有这些都没有给出具体的实例程序来.还看到一些延时的例子多多少少总有点延时差.为此我用for循环写了几个延时的子程序贴上来,希望能对初学者有所帮助.(晶振12MHz,一个机器周期1us.)
voidDelay(uchari)
{
while(--i);
}
21:
voidDelay(uchari)
22:
{
23:
while(--i);
C:
0x001F DFFE DJNZ R7,Delay(C:
001F)
24:
}
一.500ms延时子程序
程序:
voiddelay500ms(void)
{
unsignedchari,j,k;
for(i=15;i>0;i--)
for(j=202;j>0;j--)
for(k=81;k>0;k--);
}
产生的汇编:
C:
0x0800 7F0F MOV R7,#0x0F
C:
0x0802 7ECA MOV R6,#0xCA
C:
0x0804 7D51 MOV R5,#0x51
C:
0x0806 DDFE DJNZ R5,C:
0806
C:
0x0808 DEFA DJNZ R6,C:
0804
C:
0x080A DFF6 DJNZ R7,C:
0802
C:
0x080C 22 RET
计算分析:
程序共有三层循环
一层循环n:
R5*2=81*2=162us DJNZ 2us
二层循环m:
R6*(n+3)=202*165=33330us DJNZ 2us+R5赋值1us=3us
三层循环:
R7*(m+3)=15*33333=499995us DJNZ 2us+R6赋值1us=3us
循环外:
5us 子程序调用2us+子程序返回2us+R7赋值1us =5us
延时总时间=三层循环+循环外=499995+5=500000us=500ms
计算公式:
延时时间=[(2*R5+3)*R6+3]*R7+5
二.200ms延时子程序
程序:
voiddelay200ms(void)
{
unsignedchari,j,k;
for(i=5;i>0;i--)
for(j=132;j>0;j--)
for(k=150;k>0;k--);
}
产生的汇编
C:
0x0800 7F05 MOV R7,#0x05
C:
0x0802 7E84 MOV R6,#0x84
C:
0x0804 7D96 MOV R5,#0x96
C:
0x0806 DDFE DJNZ R5,C:
0806
C:
0x0808 DEFA DJNZ R6,C:
0804
C:
0x080A DFF6 DJNZ R7,C:
0802
C:
0x080C 22 RET
三.10ms延时子程序
程序:
voiddelay10ms(void)
{
unsignedchari,j,k;
for(i=5;i>0;i--)
for(j=4;j>0;j--)
for(k=248;k>0;k--);
}
产生的汇编
C:
0x0800 7F05 MOV R7,#0x05
C:
0x0802 7E04 MOV R6,#0x04
C:
0x0804 7DF8 MOV R5,#0xF8
C:
0x0806 DDFE DJNZ R5,C:
0806
C:
0x0808 DEFA DJNZ R6,C:
0804
C:
0x080A DFF6 DJNZ R7,C:
0802
C:
0x080C 22 RET
四.1s延时子程序
程序:
voiddelay1s(void)
{
unsignedcharh,i,j,k;
for(h=5;h>0;h--)
for(i=4;i>0;i--)
for(j=116;j>0;j--)
for(k=214;k>0;k--);
}
产生的汇编
C:
0x0800 7F05 MOV R7,#0x05
C:
0x0802 7E04 MOV R6,#0x04
C:
0x0804 7D74 MOV R5,#0x74
C:
0x0806 7CD6 MOV R4,#0xD6
C:
0x0808 DCFE DJNZ R4,C:
0808
C:
0x080A DDFA DJNZ R5,C:
0806
C:
0x080C DEF6 DJNZ R6,C:
0804
C:
0x080E DFF2 DJNZ R7,C:
0802
C:
0x0810 22 RET
在精确延时的计算当中,最容易让人忽略的是计算循环外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响.
设晶振频率为12M,单片机为AT89C51。
则知一个机器周期为1us。
现有延时函数如下:
3:
voiddelay(void)
4:
{
5:
unsignedchari,j,k;
6:
for(i=15;i>0;i--)
7:
for(j=202;j>0;j--)
8:
for(k=81;k>0;k--);
9:
}
以上就是利用C51语言编写的一个延时函数,下面这段代码则是利用keil软件翻译的一段汇编语言程序段,大家可以对照着看。
机器周期数
C:
0x0800 7F0F MOV R7,#0x0F 1
C:
0x0802 7ECA MOV R6,#0xCA 1
C:
0x0804 7D51 MOV R5,#0x51 1
C:
0x0806 DDFE DJNZ R5,C:
0806 2
C:
0x0808 DEFA DJNZ R6,C:
0804 2
C:
0x080A DFF6 DJNZ R7,C:
0802 2
C:
0x080C 22 RET 2
通过上面的汇编语言程序段(0x0F=15,0xCA=202,0x51=81),我们可以计算出该延时子程序的具体延时时间:
Td=[(2×81+1)×202+202×2+1]×15+15×2+1+2
=(163×202+405)×15+35
=33331×15+33
=499978us=499.978ms
通过与C语言相比较,我们可以得出一个公式(对于C51,这个公式中的格式就跟上面的一样,数据类型必须是unsignedchar,而且是三重循环,延时函数不带任何形参):
Td=[2×j×k+3×j+3]×i+3.....................①
下面是调用了上面的延时函数的C51主函数,紧跟其后的汇编语言是利用keil软件翻译的一段汇编语言程序段。
11:
voidmain(void)
12:
{
13:
p10=1;
14:
while
(1)
15:
{
16:
p10=~p10;
17:
delay();
18:
}
机器周期数
C:
0x0819 D290 SETB p10(0x90.0) 1
C:
0x081B B290 CPL p10(0x90.0) 1
C:
0x081D 120800 LCALL delay(C:
0800) 2
C:
0x0820 80F9 SJMP C:
081B 2
倘若我们需要一个带有形参的延时函数,方便在程序中根据不同的情形调用,延时时间不一致,我们的延时时间又如何计算呢?
下面我们来看看这个延时函数,跟上面的一样,只是把延时函数加了个形参,我们用同样的方法来计算它的延时时间。
3:
voiddelay(unsignedchari)
4:
{
5:
unsignedcharj,k;
6:
for(;i>0;i--)
7:
for(j=202;j>0;j--)
8:
for(k=81;k>0;k--);
9:
}
机器周期数
C:
0x800 EF MOV A,R7
C:
0x0801 D3 SETB C
C:
0x0802 9400 SUBB A,#0x00
C:
0x0804 400B JC C:
0811
C:
0x0806 7ECA MOV R6,#0xCA
C:
0x0808 7D51 MOV R5,#0x51
C:
0x080A DDFE DJNZ R5,C:
080A
C:
0x080C DEFA DJNZ R6,C:
0808
C:
0x080E 1F DEC R7
C:
0x080F 80EF SJMP delay(C:
0800)
C:
0x0811 22 RET
我们看看,R7的值是多少呢?
在下面,主函数里面,赋值为0x0F,就是15。
通过上面这段汇编程序段,我们用同样的方法来计算一下它的延时时间为(机器周期数请自己查阅书籍):
Td=[2×202×81+3×202+8]×15+3
=[32724+614]×15+3
=33338×15+3
=500073us=500.073ms
由此,我们可以得出一个公式为:
Td=[2×j×k+3×j+8]×i+3.....................②
下面是调用了voiddelay(unsignedchari)延时函数的main()函数机器汇编代码。
11:
voidmain(void)
12:
{
13:
p10=1;
14:
while
(1)
15:
{
16: