1、精确延时KEIL单片机C语言精确延时值的计算 2011-11-20 20:54| 发布者: pcb_dz| 查看: 111| 评论: 0|来自: 关于单片机C语言的精确延时,网上很多都是大约给出延时值没有准确那值是多少,也就没有达到精确高的要求,而51hei给出的本函数克服了以上缺点,能够精确计数出要延时值且精确达到1us,本举例所用CPU为STC12C5412系列12倍速的单片机,只要修改一下参数值其它系例单片机也通用,适用范围宽。共有三条延时函数说明如下:函数调用分两级:一级是小于10US的延时,二级是大于10US的延时/=小于10US的【用1US级延时】=/-微秒级延时-for(i=X;
2、iX;i-) 延时时间=(3+5*X)/12 提示(单位us, X不能大于255)/=大于10US0;Ms-) for(i=26;i0;i-);i=(延时值-1.75)*12/Ms-15/4如想延时60US则 i=(60-1.75)*12/6-15/4=25.37526; 修改i的值=26,再调用上面的【10US级延时函数】Delay10us(6); 则就精确延时60US;如果想延时64US可以用这二种函数组合来用: Delay10us(6); for(i=9;iX;i-) 共延时64US/=对于大于20Ms的可用中断来实现程序运行比较好=中断用定时器0, 1Ms中断:void timer0(
3、void) interrupt 1 TL0=(0xffff-1000+2)%0x100;TH0=(0xffff-1000+2)/0x100; /每毫秒执行一次 if(DelayMs_10)DelayMs_1-;/大于20Ms延时程序 函数调用void DelayMs(uint a)/延时 a1(ms)的时间。 DelayMs_1=a; while(DelayMs_1);如果延时50Ms则 函数值为 DelayMs(50)/+公式推算来由+二级延时C语言反汇编见上传图1: /=STC单片机延时指令时间=指今 时间(1us) 指今 时间(1us)MOV A Rn 1 SETB C 1MOV Rn
4、#data 2 SUBB A #data 2 DJNZ Rn rel 4 Dec Rn 3 SJMP rel 3 JC rel 3 MOV Rn direct 4 Lcall addr16 6 RET 4 /-JNZ 3 XRL 2SUBB 3使用Delay10us(uchar Ms) 最多能延时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的值
5、为:60=循环外时间+循环内时间=1.75us+ Ms*(15+4*I)/12;则i=(60-1.75)*12/6-15/4=25.37526即如果Ms=6, i=26;本算法中的12是STC12单片机与普通标准速率比。C语言写程序时,初学者遇到的一个难题时精确延时程序的设计。我刚开始用C语言写程序时同样遇到了这个问题,后来参考了一些文章和实际设计后才知道了精确延时程序的设计。 我现在就用两种方法来实现,一种是while()语句,另一种是for()语句,这两种语句均可产生汇编语句中的DJNZ语句,以12MHZ晶振为例(说明:在编写C程序时,变量尽量使用unsigned char,如满足不了才使
6、用unsigned int): 1.delay99;while(-delay);产生的汇编代码为:000FH MOV 08H,#63H0012H DJNZ 08H,0012H这样产生的延时时间为:(99+1)2us。最小延时时间为2us,若加上对delay赋值语句,则最小为4us。 2.for(i=delay;i0;i-);产生的汇编代码同while()语句。 下面来举例几个延时函数: 一. 500ms延时子程序void delay500ms(void) unsigned char i,j,k;for(i=15;i0;i-)for(j=202;j0;j-)for(k=81;k0;k-);产生的
7、汇编代码:C:0x0800 7F0F MOV R7,#0x0FC:0x0802 7ECA MOV R6,#0xCAC:0x0804 7D51 MOV R5,#0x51C:0x0806 DDFE DJNZ R5,C:0806C:0x0808 DEFA DJNZ R6,C:0804C:0x080A DFF6 DJNZ R7,C:0802C:0x080C 22 RET 计算分析:程序共有三层循环一层循环n:R5*2 = 81*2 = 162us DJNZ 2us二层循环m:R6*(n+3) = 202*165 = 33330us DJNZ 2us + R5赋值 1us = 3us三层循环: R7*(
8、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延时子程序void delay200ms(void) unsigned char i,j,k; for(i=5;i0;i-) for(j=132;j0;j-) for(k=150;k0;k-); 三. 10ms延时子程序void de
9、lay10ms(void) unsigned char i,j,k; for(i=5;i0;i-) for(j=4;j0;j-) for(k=248;k0;k-); 四. 1s延时子程序void delay1s(void) unsigned char h,i,j,k; for(h=5;h0;h-) for(i=4;i0;i-) for(j=116;j0;j-) for(k=214;k0;k-);以上的这先希望对大家有帮组,如有不足之处请指出,如有更好的方法也可以告诉我,大家一起分享C语言最大的缺点就是实时性差,我在网上到看了一些关于延时的讨论,其中有篇文章51单片机 Keil C 延时程序的简
10、单研究,作者:InfiniteSpace Studio/isjfk 写得不错,他是用while(-i);产生DJNZ 来实现精确延时,后来有人说如果while里面不能放其它语句,否则也不行,用do-while就可以,具体怎样我没有去试.所有这些都没有给出具体的实例程序来.还看到一些延时的例子多多少少总有点延时差.为此我用for循环写了几个延时的子程序贴上来,希望能对初学者有所帮助.(晶振12MHz,一个机器周期1us.)void Delay(uchar i)while(-i); 21: void Delay(uchar i) 22: 23: while(-i); C:0x001F DFFE D
11、JNZ R7,Delay(C:001F) 24: 一. 500ms延时子程序程序: void delay500ms(void) unsigned char i,j,k;for(i=15;i0;i-)for(j=202;j0;j-)for(k=81;k0;k-); 产生的汇编:C:0x08007F0F MOVR7,#0x0FC:0x08027ECA MOVR6,#0xCAC:0x08047D51 MOVR5,#0x51C:0x0806DDFE DJNZ R5,C:0806C:0x0808DEFA DJNZ R6,C:0804C:0x080ADFF6 DJNZ R7,C:0802C:0x080C2
12、2 RET计算分析:程序共有三层循环一层循环n:R5*2 = 81*2 = 162usDJNZ2us二层循环m:R6*(n+3) = 202*165 = 33330usDJNZ2us + R5赋值 1us = 3us三层循环: R7*(m+3) = 15*33333 = 499995usDJNZ2us + R6赋值 1us = 3us循环外: 5us子程序调用 2us + 子程序返回 2us + R7赋值 1us= 5us延时总时间 = 三层循环 + 循环外 = 499995+5 = 500000us =500ms计算公式:延时时间=(2*R5+3)*R6+3*R7+5二. 200ms延时子程
13、序程序:void delay200ms(void) unsigned char i,j,k;for(i=5;i0;i-)for(j=132;j0;j-)for(k=150;k0;k-);产生的汇编C:0x08007F05 MOVR7,#0x05C:0x08027E84 MOVR6,#0x84C:0x08047D96 MOVR5,#0x96C:0x0806DDFE DJNZ R5,C:0806C:0x0808DEFA DJNZ R6,C:0804C:0x080ADFF6 DJNZ R7,C:0802C:0x080C22 RET三. 10ms延时子程序程序:void delay10ms(void)
14、 unsigned char i,j,k;for(i=5;i0;i-)for(j=4;j0;j-)for(k=248;k0;k-);产生的汇编C:0x08007F05 MOVR7,#0x05C:0x08027E04 MOVR6,#0x04C:0x08047DF8 MOVR5,#0xF8C:0x0806DDFE DJNZ R5,C:0806C:0x0808DEFA DJNZ R6,C:0804C:0x080ADFF6 DJNZ R7,C:0802C:0x080C22 RET四. 1s延时子程序程序:void delay1s(void) unsigned char h,i,j,k;for(h=5;
15、h0;h-)for(i=4;i0;i-)for(j=116;j0;j-)for(k=214;k0;k-);产生的汇编C:0x08007F05 MOVR7,#0x05C:0x08027E04 MOVR6,#0x04C:0x08047D74 MOVR5,#0x74C:0x08067CD6 MOVR4,#0xD6C:0x0808DCFE DJNZ R4,C:0808C:0x080ADDFA DJNZ R5,C:0806C:0x080CDEF6 DJNZ R6,C:0804C:0x080EDFF2 DJNZ R7,C:0802C:0x081022 RET在精确延时的计算当中,最容易让人忽略的是计算循环
16、外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响.设晶振频率为12M,单片机为AT89C51。则知一个机器周期为1us。现有延时函数如下: 3: void delay(void) 4: 5: unsigned char i,j,k; 6: for(i=15;i0;i-) 7: for(j=202;j0;j-) 8: for(k=81;k0;k-); 9: 以上就是利用C51语言编写的一个延时函数,下面这段代码则是利用keil软件翻译的一段汇编语言程序段,大家可以对照着看。 机器周期数 C:0x0800 7F0F MOV R7,#0x0F 1 C:0x0802 7ECA MOV
17、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 通过上面的汇编语言程序段(0x0F15,0xCA202,0x5181 ),我们可以计算出该延时子程序的具体延时时间: Td(2811)202+2022+115+152+1+2 (163202+405)15+35 3333115+33 499978us499.978ms 通过与C语言相比较,我们可以
18、得出一个公式(对于C51,这个公式中的格式就跟上面的一样,数据类型必须是unsigned char,而且是三重循环,延时函数不带任何形参): Td2jk+3j+3i+3. 下面是调用了上面的延时函数的C51主函数,紧跟其后的汇编语言是利用keil软件翻译的一段汇编语言程序段。 11: void main(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
19、 120800 LCALL delay(C:0800) 2 C:0x0820 80F9 SJMP C:081B 2 倘若我们需要一个带有形参的延时函数,方便在程序中根据不同的情形调用,延时时间不一致,我们的延时时间又如何计算呢?下面我们来看看这个延时函数,跟上面的一样,只是把延时函数加了个形参,我们用同样的方法来计算它的延时时间。 3: void delay(unsigned char i) 4: 5: unsigned char j,k; 6: for(;i0;i-) 7: for(j=202;j0;j-) 8: for(k=81;k0;k-); 9: 机器周期数 C:0x800 EF MO
20、V 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。通过上面这段汇编程序段,我们用同样的方法来计算一下它的延时时间为(机器周期数请自己查阅书籍): Td220281+3202+815+3 32724+61415+3 3333815+3 500073us500.073ms 由此,我们可以得出一个公式为: Td2jk+3j+8i+3. 下面是调用了void delay(unsigned char i)延时函数的main()函数机器汇编代码。 11: void main(void) 12: 13: p10=1; 14: while(1) 15: 16:
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1