Keil C51程序设计中几种精确延时方法
51单片机C语言延时程序几种方式对比

51单片机C语言延时程序几种方式对比51单片机C语言延时程序的简单研究应用单片机的时候,经常会遇到需要短时间延时的情况。
需要的延时时间很短,一般都是几十到几百微妙(us)。
有时候还需要很高的精度,比如用单片机驱动DS18B20(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 6003JZ?C00100003 1F DEC R70004 80FA SJMP?C0007; SOURCE LINE # 210006?C0010:0006 22RET; FUNCTION _delay2 (END)真是不看不知道~~~一看才知道这个延时程序是多么的不准点~~~光看主要的那四条语句,就需要6个机器周期。
利用Keil调试精确实现软件延时

图2 值得注意的是,用性能分析窗口来观察延时函数的执行时间要求被观察的延时函数中不能再调用其他任何子函数,被测函数只能由C的 基本语句组成,否则观测到的时候并不是整个函数的运行时间。 采用上述方法,得到了以下几个延时程序: /* * 延时400毫秒 */ void Delay400Ms(void){
while(s--){ delay_1_s();
} }
但是我碰到一个比较奇怪的问题:在实现N秒延时函数中,我不调用delay_1_s()这个延时1秒函数,而用delay_1_s()中的语句来替 换delay_1_s(),改后的函数如下:
void delayNs(uchar s) {
uchar loop=10; unint j; while(s--){
比如我需要一个400ms的延时,随便写了个两重循环,外层循环5次,内层循环暂且设为5000: void Delay400Ms(void){
uchar i=5;
unint j; while(i--){
j=5000; while(j--);
//通过keil调试来确定循环次数
} }
在main函数中调用Delay400Ms(): void main()
loop=10; //注意,不能忘了此句 while(loop--){
j=8375; while(j--); } }
}
{ while(1){ P1=0; Delay400ms();
P1=1; }
} 进入uVersion的调试状态,按F10进行单步,当黄色箭头指向Delay400ms()这条语句时记下左边窗中Sys->sec的值,如图, 是0.00042426。
51单片机的几种精确延时

在很多情况下,定时器/计数器经常被用作其他用途,这时候就只能用软件方法延时。下面介绍几种软件延时的方法。
2.1短暂延时
可以在C文件中通过使用带_NOP_( )语句的函数实现,定义一系列不同的延时函数,如Delay10us( )、Delay25us( )、Delay40us( )等存放在一个自定义的C文件中,需要时在主程序中直接调用。如延时10μs的延时函数可编写如下:
同样对do…while,while循环语句中,也是如此
例:
unsigned char n;
n=255;
do{n--}
while(n);
或
n=255;
while(n)
{n--};
这两个循环语句经过C51编译之后,形成DJNZ来完成的方法,
故其精确时间的计算也很方便。
其三:对于要求精确延时时间更长,这时就要采用循环嵌套
i=255;
do{j=255;
do{j--}
while(j);
i--;
}
while(i);
或
unsigned char i,j
i=255;
while(i)
{j=255;
while(j)
{j--};
i--;
}
这三种方法都是用DJNZ指令嵌套实现循环的,由C51编
译器用下面的指令组合来完成的
MOVR7,#0FFH
当采用while (DlyT--)循环体时,DlyT的值存放在R7中。相对应的汇编代码如下:
C:0x000FAE07MOVR6, R7//1T
C:0x00111F DECR7//1T
C:0x0012EE MOVA,R6//1T
C:0x001370FAJNZC:000F//2T
51单片机C语言精确延时程序(超级准)

51单片机 C语言精确延时程序(超级准)
51单片机C语言精密延时程序 程序如下: void delayms(unsigned char t) { unsigned char j; unsigned char i; do { j=3; do { i=165; do { --i; } while(i!=0); --j; } while(j!=0); --t; } while(t!=0); } 该程序延时时基为1ms,所以最大延时时间是255ms 下面是反编译的汇编程序 C:0x0031 7E03 MOV R6,#0x03 C:0x0033 7DA5 MOV R5,#0xA5 C:0x0035 DDFE DJNZ R5,C:0035 C:0x0037 DEFA DJNZ R6,C:0033 C:0x0039 DFF6 DJNZ R7,delayms(C:0031) C:0x003B 22 RET 延时时间计算公式如下: ((R5*2 + 2+1)*R6+2+1)R7
假设R7=1,上式为(165*2+3)*3+2+1 =1002us!!!!! 以上程序使用的晶振是12MHz,如果使用的是其他频率的晶振只需计算出1ms的机器周期 数,代入5*2 + 2+1)*R6+2+1,选择合适的R
C51单片机的几种常用延时程序设计2024

引言概述:C51单片机是一种广泛应用于嵌入式系统中的微控制器,它具有高度集成化、易于编程和灵活性强等特点。
在C51单片机的软件开发过程中,延时程序设计是非常重要的一部分。
本文将介绍C51单片机中几种常用的延时程序设计方法,包括循环延时、定时器延时、外部中断延时等。
这些方法不仅可以满足在实际应用中对延时的需求,而且可以提高程序的稳定性和可靠性。
正文内容:一、循环延时1. 使用循环控制语句实现延时功能,例如使用for循环、while循环等。
2. 根据需要设置延时的时间,通过循环次数来控制延时的时长。
3. 循环延时的精度受到指令执行时间的影响,可能存在一定的误差。
4. 循环延时的优点是简单易用,适用于较短的延时时间。
5. 注意在循环延时时要考虑其他任务的处理,避免长时间的等待造成程序卡死或响应延迟。
二、定时器延时1. 使用C51单片机内置的定时器模块来实现延时。
2. 配置定时器的工作模式,如工作方式、定时器精度等。
3. 设置定时器的初值和重装值,控制定时器中断的触发时间。
4. 在定时器中断服务函数中进行延时计数和延时结束标志的设置。
5. 定时器延时的优点是精确可控,适用于需要较高精度的延时要求。
三、外部中断延时1. 在C51单片机上配置一个外部中断引脚。
2. 设置外部中断中断触发条件,如上升沿触发、下降沿触发等。
3. 在外部中断中断服务函数中进行延时计数和延时结束标志的设置。
4. 外部中断延时的优点是能够快速响应外部信号,适用于实时性要求较高的场景。
5. 注意在外部中断延时时要处理好外部中断的抖动问题,确保延时的准确性。
四、内部计时器延时1. 使用C51单片机内部的计时器模块来实现延时。
2. 配置计时器的工作模式,如工作方式、计时器精度等。
3. 设置计时器的初值和重装值,使计时器按照一定的频率进行计数。
4. 根据计时器的计数值进行延时的判断和计数。
5. 内部计时器延时的优点是能够利用单片机内部的硬件资源,提高延时的准确性和稳定性。
51单片机延时函数

51单片机延时函数在嵌入式系统开发中,51单片机因其易于学习和使用、成本低廉等优点被广泛使用。
在51单片机的程序设计中,延时函数是一个常见的需求。
通过延时函数,我们可以控制程序的执行速度,实现定时器功能,或者在需要的时候进行延时操作。
本文将介绍51单片机中常见的延时函数及其实现方法。
一、使用for循环延时这种方法不精确,但是对于要求不高的场合,可以用来估算延时。
cvoid delay(unsigned int time){unsigned int i,j;for(i=0;i<time;i++)for(j=0;j<1275;j++);}这个延时函数的原理是:在第一个for循环中,我们循环了指定的时间次数(time次),然后在每一次循环中,我们又循环了1275次。
这样,整个函数的执行时间就是time乘以1275,大致上形成了一个延时效果。
但是需要注意的是,这种方法因为硬件和编译器的不同,延时时间会有很大差异,所以只适用于对延时时间要求不精确的场合。
二、使用while循环延时这种方法比使用for循环延时更精确一些,但是同样因为硬件和编译器的不同,延时时间会有差异。
cvoid delay(unsigned int time){unsigned int i;while(time--)for(i=0;i<1275;i++);}这个延时函数的原理是:我们先进入一个while循环,在这个循环中,我们循环指定的时间次数(time次)。
然后在每一次循环中,我们又循环了1275次。
这样,整个函数的执行时间就是time乘以1275,大致上形成了一个延时效果。
但是需要注意的是,这种方法因为硬件和编译器的不同,延时时间会有差异,所以只适用于对延时时间要求不精确的场合。
三、使用定时器0实现精确延时这种方法需要在单片机中开启定时器0,并设置定时器中断。
在中断服务程序中,我们进行相应的操作来实现精确的延时。
这种方法需要使用到单片机的定时器中断功能,相对复杂一些,但是可以实现精确的延时。
Keil C51精确延时程序(C语言)

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);}。
Keil C51精确延时程序设计

Keil C51精确延时程序设计时间:2013-05-16 10:45:33 来源:电子设计工程作者:吴挺运,林成何摘要针对C语言代码的执行时间的可预见性差,结合Keil C51开发工具,分析了在Keil C51开发工具中利用C语言实现精确的延时程序的设计,指出了常用延时方法优缺点。
并通过一些实例分析了延时时间的计算方法,使C语言代码的延时时间可以被预见。
C语言中嵌套汇编语言是一种有效的方法,可以充分发挥出各语言的优势特点、提高开发效率。
关键词 Keil C51;C语言;软件延时;单片机C语言具有较强的数据处理能力、语言功能齐全、使用灵活方便、开发效率高,被广泛应用于在单片机系统开发应用中。
在单片机幕统开发的过程中,经常需要使用到延时程序,但C语言代码执行时间。
的可预见性和实时性较差,在开发一些具有严格通信时序要求的系统时,往往需要反复调试延时代码,给开发者带来了较大困难。
比如使用DS18B20进行温度测控时,必须按照其单总线通信协议,否则无法读取温度数据。
针对上述问题,结合Keil C51开发工具和Proteus仿真软件,介绍在Keil C51开发系统中,利用C语言编写的延时程序设计及其运行的时间的计算方法。
1 常用延时程序的设计方法1.1 利用定时器/计数器延时利用C51单片机内部2个16位定时器/计数器实现精确的程序,由于定时器/计数器不占用CPU的运行时间,可以提高CPU的使用效率。
但假设使用12 MHz晶振,定时器工作在方式1模式下,其最长定时时间也只能达到65.53 ms,由此,可以采用中断方式进行溢出次数累加的方法进行长时间的延时程序设计。
但在开发过程中要考虑C51自动对断点的保护和重装初值所带来的延时误差,也可以使用定时器工作在方式2模式下,减少重装初值所带来的误差。
1.2 利用空操作实现延时当所需的延时非常短,可以利用Keil C51自带intrins.h头文件中的_nop_()函数实现函数延时。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Keil C51程序设计中几种精确延时方法来源:嵌入式技术网作者:河南师范大学段向东毋茂盛时间:2007-12-19发布人:摘要实际的单片机应用系统开发过程中,由于程序功能的需要,经常编写各种延时程序,延时时间从数微秒到数秒不等,对于许多C51开发者特别是初学者编制非常精确的延时程序有一定难度。
本文从实际应用出发,讨论几种实用的编制精确延时程序和计算程序执行时间的方法,并给出各种方法使用的详细步骤,以便读者能够很好地掌握理解。
引言单片机因具有体积小、功能强、成本低以及便于实现分布式控制而有非常广泛的应用领域[1]。
单片机开发者在编制各种应用程序时经常会遇到实现精确延时的问题,比如按键去抖、数据传输等操作都要在程序中插入一段或几段延时,时间从几十微秒到几秒。
有时还要求有很高的精度,如使用单总线芯片DS18B20时,允许误差范围在十几微秒以内[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个机器周期)。
在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。
使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。
但应该注意,C51编写的中断服务程序编译后会自动加上PUSH ACC、PUSH PSW、POP PSW 和POP ACC语句,执行时占用了4个机器周期;如程序中还有计数值加1语句,则又会占用1个机器周期。
这些语句所消耗的时间在计算定时初值时要考虑进去,从初值中减去以达到最小误差的目的。
2 软件延时与时间计算在很多情况下,定时器/计数器经常被用作其他用途,这时候就只能用软件方法延时。
下面介绍几种软件延时的方法。
2.1 短暂延时可以在C文件中通过使用带_NOP_( )语句的函数实现,定义一系列不同的延时函数,如Delay10us( )、Delay25us( )、Delay40us( )等存放在一个自定义的C文件中,需要时在主程序中直接调用。
如延时10 μs的延时函数可编写如下:Delay10us( )函数中共用了6个_NOP_( )语句,每个语句执行时间为1 μs。
主函数调用Delay10us( )时,先执行一个LCALL指令(2 μs),然后执行6个_NOP_( )语句(6μs),最后执行了一个RET指令(2 μs),所以执行上述函数时共需要10 μs。
可以把这一函数当作基本延时函数,在其他函数中调用,即嵌套调用\[4\],以实现较长时间的延时;但需要注意,如在Delay40us( )中直接调用4次Delay10us( )函数,得到的延时时间将是42 μs,而不是40 μs。
这是因为执行Delay40us( )时,先执行了一次L CALL指令(2 μs),然后开始执行第一个Delay10us( ),执行完最后一个Delay10us( )时,直接返回到主程序。
依此类推,如果是两层嵌套调用,如在Delay80us( )中两次调用Delay40us( ),则也要先执行一次LCALL指令(2 μs),然后执行两次Delay40us( )函数(84 μs),所以,实际延时时间为86 μs。
简言之,只有最内层的函数执行RET指令。
该指令直接返回到上级函数或主函数。
如在Delay80μs( )中直接调用8次Delay10us( ),此时的延时时间为82 μs。
通过修改基本延时函数和适当的组合调用,上述方法可以实现不同时间的延时。
2.2 在C51中嵌套汇编程序段实现延时在C51中通过预处理指令#pragma asm和#pragma endasm可以嵌套汇编语言语句。
用户编写的汇编语言紧跟在#pragma asm之后,在#pragma endasm之前结束。
如:#pragma asm…汇编语言程序段…#pragma endasm延时函数可设置入口参数,可将参数定义为unsigned char、int或long型。
根据参数与返回值的传递规则,这时参数和函数返回值位于R7、R7R6、R7R6R5中。
在应用时应注意以下几点:◆#pragma asm、#pragma endasm不允许嵌套使用;◆在程序的开头应加上预处理指令#pragma asm,在该指令之前只能有注释或其他预处理指令;◆当使用asm语句时,编译系统并不输出目标模块,而只输出汇编源文件;◆asm只能用小写字母,如果把asm写成大写,编译系统就把它作为普通变量;◆#pragma asm、#pragma endasm和asm只能在函数内使用。
将汇编语言与C51结合起来,充分发挥各自的优势,无疑是单片机开发人员的最佳选择。
2.3 使用示波器确定延时时间熟悉硬件的开发人员,也可以利用示波器来测定延时程序执行时间。
方法如下:编写一个实现延时的函数,在该函数的开始置某个I/O口线如P1.0为高电平,在函数的最后清P1. 0为低电平。
在主程序中循环调用该延时函数,通过示波器测量P1.0引脚上的高电平时间即可确定延时函数的执行时间。
方法如下:把P1.0接入示波器,运行上面的程序,可以看到P1.0输出的波形为周期是3 ms的方波。
其中,高电平为2 ms,低电平为1 ms,即for循环结构“for(j=0;j<124;j++) {;}”的执行时间为 1 ms。
通过改变循环次数,可得到不同时间的延时。
当然,也可以不用for 循环而用别的语句实现延时。
这里讨论的只是确定延时的方法。
2.4 使用反汇编工具计算延时时间对于不熟悉示波器的开发人员可用Keil C51中的反汇编工具计算延时时间,在反汇编窗口中可用源程序和汇编程序的混合代码或汇编代码显示目标应用程序。
为了说明这种方法,还使用“for (i=0;i<DlyT;i++) {;}”。
在程序中加入这一循环结构,首先选择build taget,然后单击start/stop debug session按钮进入程序调试窗口,最后打开Disassembly wi ndow,找出与这部分循环结构相对应的汇编代码,具体如下:可以看出,0x000F~0x0017一共8条语句,分析语句可以发现并不是每条语句都执行DlyT次。
核心循环只有0x0011~0x0017共6条语句,总共8个机器周期,第1次循环先执行“CLR A”和“MOV R6,A”两条语句,需要2个机器周期,每循环1次需要8个机器周期,但最后1次循环需要5个机器周期。
DlyT次核心循环语句消耗(2+DlyT×8+5)个机器周期,当系统采用12 MHz时,精度为7 μs。
当采用while (DlyT--)循环体时,DlyT的值存放在R7中。
相对应的汇编代码如下:循环语句执行的时间为(DlyT+1)×5个机器周期,即这种循环结构的延时精度为5 μs。
通过实验发现,如将while (DlyT--)改为while (--DlyT),经过反汇编后得到如下代码:C:0x0014DFFE DJNZR7,C:0014//2T可以看出,这时代码只有1句,共占用2个机器周期,精度达到2 μs,循环体耗时Dl yT×2个机器周期;但这时应该注意,DlyT初始值不能为0。
这3种循环结构的延时与循环次数的关系如表1所列。
表1 循环次数与延时时间关系单位:μs注意:计算时间时还应加上函数调用和函数返回各2个机器周期时间。
2.5 使用性能分析器计算延时时间很多C程序员可能对汇编语言不太熟悉,特别是每个指令执行的时间是很难记忆的,因此,再给出一种使用Keil C51的性能分析器计算延时时间的方法。
这里还以前面介绍的for (i=0;i<124;i++)结构为例。
使用这种方法时,必须先设置系统所用的晶振频率,选择O ptions for target中的target选项,在Xtal(MHz)中填入所用晶振的频率。
将程序编译后,分别在_point = 1和T_point = 0处设置两个运行断点。
选择start/stop debug sessi on按钮进入程序调试窗口,分别打开Performance Analyzer window和Disassemblywindow。
运行程序前,要首先将程序复位,计时器清零;然后按F5键运行程序,从程序效率评估窗口的下部分可以看到程序到了第一个断点,也就是所要算的程序段的开始处,用了389 μs;再按F5键,程序到了第2个断点处也就是所要算的程序段的结束处,此时时间为1 386 μs。
最后用结束处的时间减去开始处时间,就得到循环程序段所占用的时间为997 μs。
当然也可以不用打开Performance Analyzer window,这时观察左边工具栏秒(SE C)项。
全速运行时,时间不变,只有当程序运行到断点处,才显示运行所用的时间。
3 总结本文介绍了多种实现并计算延时程序执行时间的方法。
使用定时器进行延时是最佳的选择,可以提高MCU工作效率,在无法使用定时器而又需要实现比较精确的延时时,后面介绍的几种方法可以实现不等时间的延时:使用自定义头文件的优点是,可实现任意时间长短的延时,并减少主程序的代码长度,便于对程序的阅读理解和维护。
编写延时程序是一项很麻烦的任务,可能需要多次修改才能满足要求。
掌握延时程序的编写,能够使程序准确得以执行,这对项目开发有着重要的意义。
本文所讨论的几种方法,都是来源于实际项目的开发经验,有着很好的实用性和适应性。