51单片机精确延时源程序
- 格式:doc
- 大小:4.69 KB
- 文档页数:12
Keil C51程序设计中几种精确延时方法实际的单片机应用系统开发过程中,由于程序功能的需要,经常编写各种延时程序,延时时间从数微秒到数秒不等,对于许多C51开发者特别是初学者编制非常精确的延时程序有一定难度。
本文从实际应用出发,讨论几种实用的编制精确延时程序和计算程序执行时间的方法,并给出各种方法使用的详细步骤,以便读者能够很好地掌握理解。
关键词Keil C51 精确延时程序执行时间引言单片机因具有体积小、功能强、成本低以及便于实现分布式控制而有非常广泛的应用领域[1]。
单片机开发者在编制各种应用程序时经常会遇到实现精确延时的问题,比如按键去抖、数据传输等操作都要在程序中插入一段或几段延时,时间从几十微秒到几秒。
有时还要求有很高的精度,如使用单总线芯片DS18B 20时,允许误差范围在十几微秒以内[2],否则,芯片无法工作。
用51汇编语言写程序时,这种问题很容易得到解决,而目前开发嵌入式系统软件的主流工具为C语言,用C51写延时程序时需要一些技巧[3]。
因此,在多年单片机开发经验的基础上,介绍几种实用的编制精确延时程序和计算程序执行时间的方法。
实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;另一种是软件延时,这种方法主要采用循环体进行。
1 使用定时器/计数器实现精确延时单片机系统一般常选用11.059 2 MHz、12 MHz或6 MHz晶振。
第一种更容易产生各种标准的波特率,后两种的一个机器周期分别为1 μs和2 μs,便于精确延时。
本程序中假设使用频率为12 MHz的晶振。
最长的延时时间可达216=65 536 μs。
若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。
在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。
在用C语言写程序时,初学者遇到的一个难题时精确延时程序的设计。
我刚开始用C语言写程序时同样遇到了这个问题,后来参考了一些文章和实际设计后才知道了精确延时程序的设计。
我现在就用两种方法来实现,一种是while()语句,另一种是for()语句,这两种语句均可产生汇编语句中的DJNZ语句,以12MHZ晶振为例(说明:在编写C程序时,变量尽量使用unsigned char,如满足不了才使用unsigned int):1.delay=99;while(--delay);产生的汇编代码为:000FH MOV 08H,#63H0012H DJNZ 08H,0012H这样产生的延时时间为:(99+1)×2us。
最小延时时间为2us,若加上对delay赋值语句,则最小为4us。
2.for(i=delay;i>0;i--);产生的汇编代码同while()语句。
下面来举例几个延时函数:一. 500ms延时子程序void delay500ms(void){unsigned char i,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,#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*(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;i>0;i--)for(j=132;j>0;j--)for(k=150;k>0;k--);}三. 10ms延时子程序void delay10ms(void){unsigned char i,j,k;for(i=5;i>0;i--)for(j=4;j>0;j--)for(k=248;k>0;k--);}四. 1s延时子程序void delay1s(void){unsigned char h,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语言的精确延时,网上很多都是大约给出延时值没有准确那值是多少,也就没有达到精确高的要求,而51hei给出的本函数克服了以上缺点,能够精确计数出要延时值且精确达到1us,本举例所用CPU为STC12C5412系列12倍速的单片机,只要修改一下参数值其它系例单片机也通用,适用范围宽。
Keil C51精确延时程序程序说明如下:振荡频率:12MHz机器周期=12/振荡频率=12/12000000=1us#include <reg52.h>void delay1(unsigned char i){ while(--i);}说明:delay1程序为:延时时间=(2*i+2)*机器周期。
i=1~255。
void delay2(unsigned char i){ while(i--);}说明:delay2程序为:延时时间=(6*i+2)*机器周期。
i=1~255。
void main (void){unsigned char m;delay1(10); //赋值并调延时程序delay1说明:本句为赋值并调用Delayus1:延时时间=(1+2)*机器周期。
全部延时时间为:延时时间=(1+2+2*i+2)*机器周期。
i=1~255。
本例:延时时间=(1+2+2*10+2)*1us=25usdelay2(10); //赋值并调延时程序delay2说明:本句为赋值并调用Delayus2:延时时间=(1+2)*机器周期。
全部延时时间为:延时时间=(1+2+6*i+2)*机器周期。
i=1~255。
本例:延时时间=(1+2+6*10+2)*1us=65usm=10; //赋值,m=1~255while(--m) ; //计算,延时时间=2*m*机器周期说明:本两句为赋值并计算。
全部延时时间为:延时时间=(1+2*m)*机器周期。
m=1~255。
本例:延时时间=(1+2*10)*1us=25uswhile(1);}。
51单片机c语言延时51单片机(8051微控制器)是一种广泛使用的嵌入式系统芯片,其编程语言包括C语言和汇编语言等。
在C语言中,实现51单片机延时的方法有多种,下面介绍其中一种常用的方法。
首先,我们需要了解51单片机的指令周期和机器周期。
指令周期是指单片机执行一条指令所需的时间,而机器周期是指单片机执行一个操作所需的时间,通常以微秒为单位。
在C语言中,我们可以使用循环结构来实现延时。
#include <reg51.h> // 包含51单片机的寄存器定义void delay(unsigned int time) // 延时函数,参数为需要延时的微秒数{unsigned int i, j;for (i = 0; i < time; i++)for (j = 0; j < 1275; j++); // 1275个机器周期,约等于1ms}void main() // 主函数{while (1) // 无限循环{// 在这里添加需要延时的代码P1 = 0x00; // 例如将P1口清零delay(1000); // 延时1秒P1 = 0xFF; // 将P1口清零delay(1000); // 延时1秒}}在上面的代码中,我们定义了一个名为delay的函数,用于实现延时操作。
该函数接受一个无符号整数参数time,表示需要延时的微秒数。
在函数内部,我们使用两个嵌套的循环来计算延时时间,其中外层循环控制需要延时的次数,内层循环控制每个机器周期的时间(约为1微秒)。
具体来说,内层循环执行了约1275次操作(具体数值取决于编译器和单片机的型号),以实现约1毫秒的延时时间。
需要注意的是,由于单片机的指令周期和机器周期不同,因此我们需要根据具体的单片机型号和编译器进行调整。
在主函数中,我们使用一个无限循环来不断执行需要延时的操作。
例如,我们将P1口的所有引脚清零,然后调用delay函数进行1秒钟的延时,再将P1口清零并再次调用delay函数进行1秒钟的延时。
51单片机延时程序51单片机延时程序应用单片机的时候,经常会遇到需要短时间延时的情况。
需要的延时时间很短,一般都是几十到几百微妙(us)。
有时候还需要很高的精度,比如用单片机驱动DS18B20的时候,误差容许的范围在十几us 以内,不然很容易出错。
这种情况下,用计时器往往有点小题大做。
而在极端的情况下,计时器甚至已经全部派上了别的用途。
这时就需要我们另想别的办法了。
以前用汇编语言写单片机程序的时候,这个问题还是相对容易解决的。
比如用的是12MHz晶振的51,打算延时20us,只要用下面的代码,就可以满足一般的需要:mov r0, #09hloop: djnz r0, loop51 单片机的指令周期是晶振频率的1/12,也就是1us一个周期。
mov r0, #09h需要2个极其周期,djnz也需要2个极其周期。
那么存在r0里的数就是(20-2)/2。
用这种方法,可以非常方便的实现256us 以下时间的延时。
如果需要更长时间,可以使用两层嵌套。
而且精度可以达到2us,一般来说,这已经足够了。
现在,应用更广泛的毫无疑问是Keil的C编译器。
相对汇编来说,C固然有很多优点,比如程序易维护,便于理解,适合大的项目。
但缺点(我觉得这是C的唯一一个缺点了)就是实时性没有保证,无法预测代码执行的指令周期。
因而在实时性要求高的场合,还需要汇编和C的联合应用。
但是是不是这样一个延时程序,也需要用汇编来实现呢?为了找到这个答案,我做了一个实验。
用C语言实现延时程序,首先想到的就是C常用的循环语句。
下面这段代码是我经常在网上看到的:void delay2(unsigned char i){for(; i != 0; i--);到底这段代码能达到多高的精度呢?为了直接衡量这段代码的效果,我把 Keil C 根据这段代码产生的汇编代码找了出来:; FUNCTION _delay2 (BEGIN); SOURCE LINE # 18;---- Variable i assigned to Register R7 ----; SOURCE LINE # 19; SOURCE LINE # 200000 ?C0007:0000 EF MOV A,R70001 6003 JZ ?C00100003 1F DEC R70004 80FA SJMP ?C0007; SOURCE LINE # 210006 ?C0010:0006 22 RET; FUNCTION _delay2 (END)真是不看不知道~~~一看才知道这个延时程序是多么的不准点~~~光看主要的那四条语句,就需要6个机器周期。
51单片机精确延时源程序一、晶振为11.0592MHz,12T1、延时1ms:(1)汇编语言:代码如下:DELAY1MS: ;误差 -0.651041666667usMOV R6,#04HDL0:MOV R5,#71HDJNZ R5,$DJNZ R6,DL0RET(2)C语言:void delay1ms(void) //误差 -0.651041666667us{unsigned char a,b;for(b=4;b>0;b--)for(a=113;a>0;a--);}2、延时10MS:(1)汇编语言:DELAY10MS: ;误差 -0.000000000002usMOV R6,#97HDL0:MOV R5,#1DHDJNZ R5,$DJNZ R6,DL0RET(2)C语言:void delay10ms(void) //误差 -0.000000000002us{unsigned char a,b;for(b=151;b>0;b--)for(a=29;a>0;a--);}3、延时100MS:(1)汇编语言:DELAY100MS: ;误差 -0.000000000021usMOV R7,#23HDL1:MOV R6,#0AHI棋影淘宝店:QQ:149034219DL0:MOV R5,#82HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1RET(2)C语言:void delay100ms(void) //误差 -0.000000000021us{unsigned char a,b,c;for(c=35;c>0;c--)for(b=10;b>0;b--)for(a=130;a>0;a--);}4、延时1S:(1)汇编语言:DELAY1S: ;误差 -0.00000000024usMOV R7,#5FHDL1:MOV R6,#1AHDL0:MOV R5,#0B9HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1RET(2)C语言:void delay1s(void) //误差 -0.00000000024us{unsigned char a,b,c;for(c=95;c>0;c--)for(b=26;b>0;b--)for(a=185;a>0;a--);}5、延时10S:(1)汇编语言:DELAY10S: ;误差 -0.000000003101us MOV R7,#0B3HDL1:MOV R6,#83HDL0:MOV R5,#0C3HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1NOPII棋影淘宝店:QQ:149034219 RET(2)C语言:void delay10s(void) //误差 -0.000000003101us{unsigned char a,b,c;for(c=179;c>0;c--)for(b=131;b>0;b--)for(a=195;a>0;a--);_nop_(); //if Keil,require use intrins.h}6、延时100S:(1)汇编语言:DELAY100S: ;误差 -0.000000016117usMOV R4,#72HDL2:MOV R7,#0DDHDL1:MOV R6,#0D7HDL0:MOV R5,#07HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1DJNZ R4,DL2NOPRET(2)C语言:void delay100s(void) //误差 -0.000000016117us{unsigned char a,b,c,d;for(d=114;d>0;d--)for(c=221;c>0;c--)for(b=215;b>0;b--)for(a=7;a>0;a--);_nop_(); //if Keil,require use intrins.h }二、晶振为12MHz,12T1、延时1MS:(1)汇编语言:DELAY1MS: ;误差 0usMOV R6,#0C7HDL0:MOV R5,#01HDJNZ R5,$DJNZ R6,DL0III棋影淘宝店:QQ:149034219 RET(2)C语言:void delay1ms(void) //误差 0us{unsigned char a,b;for(b=199;b>0;b--)for(a=1;a>0;a--);}2、延时10MS:(1)汇编语言:DELAY10MS: ;误差 0usMOV R7,#05HDL1:MOV R6,#04HDL0:MOV R5,#0F8HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1RET(2)C语言:void delay10ms(void) //误差 0us{unsigned char a,b,c;for(c=5;c>0;c--)for(b=4;b>0;b--)for(a=248;a>0;a--);}3、延时100MS:(1)汇编语言:DELAY100MS: ;误差 0usMOV R7,#07HDL1:MOV R6,#4AHDL0:MOV R5,#5FHDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1RET(2)C语言:void delay100ms(void) //误差 0us{unsigned char a,b,c;for(c=7;c>0;c--)for(b=74;b>0;b--)IV棋影淘宝店:QQ:149034219for(a=95;a>0;a--);}4、延时1S:(1)汇编语言:DELAY1S: ;误差 0usMOV R7,#2EHDL1:MOV R6,#98HDL0:MOV R5,#46HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1NOPRET(2)C语言:void delay1s(void) //误差 0us{unsigned char a,b,c;for(c=46;c>0;c--)for(b=152;b>0;b--)for(a=70;a>0;a--);_nop_(); //if Keil,require use intrins.h}5、延时10S:(1)汇编语言:DELAY10S: ;误差 0usMOV R7,#0BDHDL1:MOV R6,#0BFHDL0:MOV R5,#89HDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1MOV R5,#02HDJNZ R5,$RET(2)C语言:void delay10s(void) //误差 0us{unsigned char a,b,c,n;for(c=189;c>0;c--)for(b=191;b>0;b--)for(a=137;a>0;a--);for(n=2;n>0;n--);V棋影淘宝店:QQ:149034219 }6、延时100S:(1)汇编语言:DELAY100S: ;误差 0usMOV R4,#1EHDL2:MOV R7,#0E7HDL1:MOV R6,#0E5HDL0:MOV R5,#1EHDJNZ R5,$DJNZ R6,DL0DJNZ R7,DL1DJNZ R4,DL2MOV R5,#02HDJNZ R5,$RET(2)C语言:void delay100s(void) //误差 0us{unsigned char a,b,c,d,n;for(d=30;d>0;d--)for(c=231;c>0;c--)for(b=229;b>0;b--)for(a=30;a>0;a--);for(n=2;n>0;n--);}VI。