51单片机延时函数设计
51单片机C程序标准延时函数

51单片机C程序标准延时函数在此,我用的是12M晶振,一个时钟周期是1/12us,一个机器周期为12个时钟周期,则机器周期为1us,而51单片机执行一条语句,为1,2,4个机器周期不等,根据语句的长度来定,一般为1个机器周期。
而_nop_()为一条空语句,执行一次需要一个机器周期。
1us#include<intrins.h>_nop_();执行了一条_nop_();所以延时为1us;10usvoid delay10us(){_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}执行了6条_nop_(),延时6us,主函数调用delay10us 时,先执行了LCALL指令2us,然后执行6条_nop_()语句6us,最后执行一条RET指令2us,所以总共延时10us。
100usvoid delay100us(){delay10us();delay10us();delay10us();delay10us();delay10us();delay10us();delay10us();delay10us();delay10us();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}与上面的一样,主函数调用delay100us();先执行了LCALL语句2us,再调用9个delay10us()函数90us,然后执行了6条_nop_()语句6us,最后执行了一条RET语句2us,总共100us。
1msvoid delay1ms(){f=1;TH0=0xe1;TL0=0X13;TR0=1;while(f);}void T0_3() interrupt 1{TR0=0;f=0;}这里就直接用51单片机内部定时器延时了,如果用_nop_();如果要做到微妙不差,那程序就太长了。
这里我用的是定时器0的方式0,13位定时器,这里为了方便,我就没就EA=1;ET0=1;TM0D=0X00;写在延时函数里。
基于51单片机的精确延时(微秒级)

声明:*此文章是基于51单片机的微秒级延时函数,采用12MHz晶振。
*此文章共包含4个方面,分别是延时1us,5us,10us和任意微秒。
前三个方面是作者学习过程中从书本或网络上面总结的,并非本人所作。
但是延时任意微秒函数乃作者原创且亲测无误。
欢迎转载。
*此篇文章是作者为方便初学者使用而写的,水平有限,有误之处还望大家多多指正。
*作者:Qtel*2012.4.14*QQ:97642651----------------------------------------------------------------------------------------------------------------------序:对于某些对时间精度要求较高的程序,用c写延时显得有些力不从心,故需用到汇编程序。
本人通过测试,总结了51的精确延时函数(在c语言中嵌入汇编)分享给大家。
至于如何在c 中嵌入汇编大家可以去网上查查,这方面的资料很多,且很简单。
以12MHz晶振为例,12MHz 晶振的机器周期为1us,所以,执行一条单周期指令所用时间就是1us,如NOP指令。
下面具体阐述一下。
----------------------------------------------------------------------------------------------------------------------1.若要延时1us,则可以调用_nop_();函数,此函数是一个c函数,其相当于一个NOP指令,使用时必须包含头文件“intrins.h”。
例如:#include<intrins.h>#include<reg52.h>void main(void){P1=0x0;_nop_();//延时1usP1=0xff;}----------------------------------------------------------------------------------------------------------------------2.延时5us,则可以写一个delay_5us()函数:delay_5us(){#pragma asmnop#pragma endasm}这就是一个延时5us的函数,只需要在需要延时5us时调用此函数即可。
51 单片机 定时器 延时1s函数

51 单片机定时器延时1s函数1.引言1.1 概述本文介绍了51单片机中的定时器功能以及如何通过定时器实现延时1秒的函数。
在单片机应用中,定时器是一种非常重要且常用的功能模块之一。
它能够精确计时,并可用于实现周期性的任务触发、计时、脉冲输出等功能。
本文首先将对51单片机进行简要介绍,包括其基本概念、结构和特点。
随后,重点讲解了定时器的基本原理和功能。
定时器通常由一个计数器和一组控制寄存器组成,通过预设计数器的初值和控制寄存器的配置来实现不同的计时功能。
接着,本文详细介绍了如何通过编程实现一个延时1秒的函数。
延时函数是单片机开发中常用的功能,通过定时器的计时功能可以实现精确的延时控制。
本文将以C语言为例,介绍延时函数的编写步骤和原理,并给出示例代码和详细的说明。
最后,本文对所述内容进行了总结,并展望了定时器在单片机应用中的广泛应用前景。
通过学习定时器的相关知识和掌握延时函数的编写方法,我们可以更好地应用定时器功能,提高单片机应用的效率和精确性。
综上所述,通过本文的学习,读者可全面了解51单片机中定时器的功能和应用,并能够掌握延时函数的编写方法,为单片机应用开发提供一定的参考和指导。
1.2 文章结构本文以51单片机定时器功能为主题,旨在介绍如何使用定时器进行延时操作。
文章分为引言、正文和结论三个主要部分。
在引言部分,首先会对文章的背景进行概述,介绍单片机的基本概念和应用领域。
然后,给出本文的整体结构,并阐述文章的目的和意义。
正文部分将分为两个小节。
在2.1节中,将对单片机进行详细介绍,包括其构造与工作原理。
这部分的内容将帮助读者全面了解单片机的基本知识,为后续的定时器功能介绍打下基础。
2.2节将重点介绍定时器的功能和特点。
这部分将涵盖定时器的基本原理、工作模式以及在实际应用中的使用方法。
同时,还将详细讲解如何使用定时器进行1秒钟的延时操作,包括具体的代码实现和注意事项。
结论部分将对全文进行总结,并强调定时器的重要性和应用前景。
转用C51编写单片机延时函数

转用C51编写单片机延时函数这里假定单片机是时钟频率为12MHz,则一个机器周期为:1us.参考了51单片机Keil C延时程序的简单研究后,我们可知道,在Keil C 中获得最为准确的延时函数将是void delay(unsigned char t){while(--t);}反汇编代码如下:执行DJNZ指令需要2个机器周期,RET指令同样需要2个机器周期,根据输入t,在不计算调用delay()所需时间的情况下,具体时间延时如下:t Delay Time(us)1 2×1+2=4 22×2+2=6 N2×N+2=2(N+1)当在main函数中调用delay(1)时,进行反汇编如下:调用delay()时,多执行了两条指令,其中MOV R,#data需要1个机器周期,LJMP需要2个机器周期,即调用delay()需要3us.Keil C仿真截图与计算过程:加上调用时间,准确的计算时间延时与Keil C仿真对比如下:(可见,仿真结果和计算结果是很接近的)t Delay Time(us)仿真11.0592 Mhz时钟(us)1 3+2×1+2=7|7.7(实际)7.60 23+2×2+2=9|9.9 9.76 N3+2×N+2=2N+5|(2N+5)*1.1/3 11|12.1 11.94 15 35|38.5 37.98 100 205|225.5 222.44 255515|566.5 558.81也就是说,这个延时函数的精度为2us,最小的时间延时为7us,最大的时间延时为3+255×2+2=515us.实际中使用11.0592 MHz的时钟,这个延时函数的精度将为2.2us,最小时间延时为7.7us,最大时间延时为566.5us.这个时间延时函数,对于与DS18B20进行单总线通信,已经足够准确了。
现在,我们将时钟换成11.0592 MHz这个实际用到的频率,每个机器周期约为1.1us.现在让我们来分析一下这个之前用过的延时函数://延时函数,对于11.0592 MHz时钟,例i=10,则大概延时10ms.void delayMs(unsigned int i){unsigned int j;while(i--){for(j=0;j 125;j++);}}它的反汇编代码如下:分析:T表示一个机器周期(调用时间相对于这个ms级的延时来说,可忽略不计)1 C:0000 MOV A,R7;1T 2DEC R7;1T低8位字节减1 3MOV R2,0x06;2T 4JNZ C:0007;2T若低8位字节不为0,则跳到C:0007 5DEC R6;1T低8位字节为0,则高8位字节减1 6C:0007 ORL A,R2;1T 7JZ C:001D;2T若高8位也减为0,则RET 8CLR A;1T A清零9 MOV R4,A;1T R4放高位10 MOV R5,A;1T R5放低位11 C:000D CLR C;1T C清零12 MOV A,R5;1T 13 SUBB A,#0x7d;1T A=A-125 14 MOV A,R4;1T 15 SUBB A,#0x00;1T A16 JNC C:0000;2T A为零则跳到C:0000 17 INC R5;1T R5增1 18 CJNE R5,#0x00,C:001B;2T R5 0,跳转到C:000D 19 INC R4;1T 20 C:001B SJMP C:000D;2T 21 C:001D RET对于delayMs(1),执行到第7行就跳到21行,共需时12T,即13.2us对于delayMs(2),需时9T+13T+124×10T+7T+12T=9T+13T+1240T+7T+12T=1281T=1409.1 us.对于delayMs(3),需时9T×(3-1)+(13T+124×10T+7T)×(3-1)+12T=1269T×(3-1)+12T=2550T=2805us.对于delayMs(N),N 1,需时1269T×(N-1)+12T=1269NT-1257T=(1395.9 N-1382.7)us.利用Keil C仿真delayMs(1)=0.00166558 s=1.67ms截图如下:由分析可知具体的计算延时时间与Keil C仿真延时对比如下:i Time Delay仿真延时1 13.2us 1.67ms 21409.1 us 3.31ms 32805us 4.96ms N(1395.9 N-1382.7)us 10 12.6ms 16.50ms 20 26.5ms 32.98ms 30 40.5ms 49.46ms 50 68.4ms 82.43ms 100 138.2ms 164.84 ms 200 277.8ms 329.56 ms 500696.6ms 824.13 ms 1000 1394.5 ms 1648.54 ms 1500 2092.5 ms 2472.34 ms 2000 2790.4 ms 3296.47 ms 55.6ms 8.26ms 73 100.5ms 120.34 ms 720 1003.7 ms=1s 1186.74 ms计算delayMs(10)得到延时时间为:12576.3 us约等于12.6ms,接近我们认为的10ms。
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单片机延时函数设计

58:
unsigned char i = 0;
C:0x11A5 E4
CLR
A
C:0x11A6 FF
MOV
R7,A
59:
unsigned char j = 0;
60:
for(i = 0;i < 29;i++)
61:
{
62:
for(j = 0;j < 62;j++)
C:0x11A7 E4
CLR
A
C:0x11A8 FE
djnz r6,DELAY2 ;2 个机器周期
djnz r5,DELAY1 ;2 个机器周期
ret
;2 个机器周期
假定已经给 delayr5、delayr6 和 delayr7 赋予了正确的数值,当调用 DELAY
函数的时候就会进行一段时间的空循环,然后返回,达到延时的目的。必须遵守
如下的调用顺序:
我找到了解决之道,参看示例代码 7。
示例代码 7
unsigned char i = 0;
unsigned char j = 0;
for(i = 0;i < 29;i++)
{
for(j = 0;j < 62;j++)
{
_nop_();
}
_nop_();
}
编译器做出来的代码类似如下格式(蓝色行是汇编代码,红色行是 C 源代码):
再次强调,延时时间是包括“DelayConstantInner = XXX”这些语句在内 的总时间。当然,在 C 程序中嵌入汇编,还要设置一些编译器选项,这个你自己 找书看。这一步很重要哦,要不编译根本通不过。
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,并设置定时器中断。
在中断服务程序中,我们进行相应的操作来实现精确的延时。
这种方法需要使用到单片机的定时器中断功能,相对复杂一些,但是可以实现精确的延时。
51单片机延时程序

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--);
//
void delay(uint t)
{
for (;t>0;t--);
}
1ms延时子程序(12MHZ)
void delay1ms(uint p)//12mhz
{ uchar i,j;
for(i=0;i<p;i++)
{
for(j=0;j<124;j++)
{;}
在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。但应该注意,C51编写的中断服务程序编译后会自动加上PUSH ACC、PUSH PSW、POP PSW和POP ACC语句,执行时占用了4个机器周期;如程序中还有计数值加1语句,则又会占用1个机器周期。这些语句所消耗的时间在计算定时初值时要考虑进去,从初值中减去以达到最小误差的目的。
}
void Delay10us( ) //12mhz
{
_NOP_( );
_NOP_( );
_NOP_( );
_NOP_( );
_NOP_( );
_NOP_( );
}
/*****************11us延时函数*************************/
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
际算出来的量是延时 0.105664 秒,差 2 个机器周期。这个差值被表示为以 机器周期为单位的量显示出来,在本例是-2。那么,先按照延时 0.105664 秒编写程序,然后紧跟着添加 2 个 nop 就 OK 了。 3. 如果差值是正的怎么办?例如延时 0.123458 秒,实际算出来的量是延时 0.123460 微秒,总不能减去 2 个 nop 吧。答:你可以这样,把延时迟数值稍 微改小一些,比如 0.123456,那样的话,计算出来的差值是零,然后再添加 2 个 nop 就 OK 了。 4. 太大或者太小的延时时间怎么办?答:如果延时 6 个时钟周期,直接写 6 个 nop 不是更好吗。如果延时 100 秒,可以每次延时 10 秒,分 10 次调用。貌 似延时 100 秒也没有必要精确到一个机器周期的样子。 5. 如果差值是-10000,岂不是要写 10000 个 nop 吗?答:你好笨,汉字一写一 划,二写二划,三写三划,这么说万字要写 10000 划了吗?如果差 10000 个, 可以再搞一个延时 10000 的延时调用不就行了,大鱼吃小鱼,小鱼吃虾米, 虾米喝海水吗,大事化小,小事化无。再说了,貌似这个计算程序不会弄出 10000 来的。 第三步:嵌入 C 程序中。 上面是汇编的代码,用 C 直接写精确的延时程序是不可能的,只能混合编程, 把汇编程序嵌入进去。当然代码要做一些变化以适应 C 语言的语法。程序需要 6 个内存空间用来保存变量,像下面这样,在 main 之前:
#pragma asm DELAY000: mov DelayCounterOuter, DelayConstantOuter DELAY001: mov DelayCounterMiddle,DelayConstantMiddle DELAY002: mov DelayCounterInner, DelayConstantInner DELAY003: djnz DelayCounterInner, DELAY003 djnz DelayCounterMiddle,DELAY002 djnz DelayCounterOuter, DELAY001 #pragma endasm }
天津工业大学信息与通信工程学院 宋培林
延时函数是最经常被使用的一种子程序,可以用空循环来做,也可以使用基
于定时器的中断来做。两种方法各有优缺点,各自适用不同的应用场合。以下讲
解我设计的可以精确到一个时钟周期的延时函数。注意,时间单位是机器周期,
不是具体的多少多少毫秒、微秒等。
第一步:汇编实现。资源开销:需要 3 个寄存器和 3 个内存单元。
58:
unsigned char = 0;
C:0x11A5 E4
CLR
A
C:0x11A6 FF
MOV
R7,A
59:
unsigned char j = 0;
60:
for(i = 0;i < 29;i++)
61:
{
62:
for(j = 0;j < 62;j++)
C:0x11A7 E4
CLR
A
C:0x11A8 FE
代码太复杂我搞不定它的规律;一层的话又嫌延时时间短,两层折中一下。
2. 变量 i 和 j 必须从 0 开始,到小于某个数值为止(即 for 语句里面的那个数
字,不能带上等于号)。
那么变量 i 和 j 的初始值如何得到呢?参看图 2,选择纯 C 模式,接下来的 使用方法与前面类似,只不过这次只有两层循环变量:中间层计数初始值旁边的 那个数字对应 i,最内层计数初始值旁边的那个数字对应 j,最外层计数初始值 旁边的那个数字无意义。
注意#0XXh 不能是#0h。那么前 3 行就是赋值,第 4 行是调用,包括前 3 行代码
在内,上面的程序一共运行了多少个机器周期呢?
[(2*delayr7+2+2)*delayr6+2+2]*delayr5+2+2+2+2+2+2 假定要延时 100 个机器周期的时间,那么把循环初始数值算好,给 delayr5、
汇编程序最开始,应该定义好 delayr5、delayr6 和 delayr7,比如像下面这样:
示例代码 1:
delayr5 data 30h
delayr6 data 31h
delayr7 data 32h
也就是说,delayr5 占据了地址编号为 30h 的内部 RAM,delayr6 占据了地址编
寄存器用来循环计数,内存单元用来保存循环次数的初始数值。假定寄存器
使用 r5、r6 和 r7,对应的内存单元用标号记为 delayr5、delayr6 和 delayr7。
使用 3 个寄存器和 3 个内存单元就是要做 3 层嵌套循环,r5 和 delayr5 用于控
制最外层,r6 和 delayr6 用于控制中间层,r7 和 delayr7 用于控制最内层。在
还要提醒你:以上只是 C 代码转变为汇编代码的典型实现。如果你的程序比 较复杂,那么可能内部 BANK0 的 8 个寄存器不够用,会切换 BANK,那么代码就 不一定是上面那个。这样的话,这个程序就不精确了。
图2
delayr6 和 delayr7 赋值就行了,也就是替换示例代码 3 中的那些#0XXh。计算 初始值可不是一个简单的活,我设计了一个 VC 程序,用于计算这些数值。 第二步:如何得到循环次数的初始数值。
程序界面如图 1 所示。
图1 1. 以 Hz 为单位设定晶振频率(只能输入数字,不要输入“Hz” )。 2. 像 AT89C51 这种芯片,一个机器周期包括 12 个时钟周期,那么要把 12 这个
如下的调用顺序:
示例代码 3:
;之前的其它代码
mov delayr5,#0XXh ;赋初始值,2 个机器周期
mov delayr6,#0XXh ;赋初始值,2 个机器周期
mov delayr7,#0XXh ;赋初始值,2 个机器周期
lcall DELAY
;2 个机器周期
;之后的其它代码
在示例代码 3 中,#0XXh 代表某些合适的数值(后面再说如何得到这些数值),
示例代码 4: unsigned char data DelayConstantInner = 0;//内层延时时间初值 unsigned char data DelayConstantMiddle = 0;//中层延时时间初值 unsigned char data DelayConstantOuter = 0;//外层延时时间初值 unsigned char data DelayCounterInner = 0;//内层延时计数器 unsigned char data DelayCounterMiddle = 0;//中层延时计数器 unsigned char data DelayCounterOuter = 0;//外层延时计数器 声明延时子程序,如果这个子程序被放在 main 之前,那么声明与实现可以 一起做,如下: 示例代码 5: void TimeDelay() {
96:
}
97:
C:0x11B3 22
RET
示例代码 7 一共运行多长时间呢?[4×j + 6] ×i + 6(单位机器周期)。
这个时间包括示例代码 7 全部代码行运行时间(从隐含的 LCALL 算起,直到隐含
的 RET 指令。)。注意:
1. 只能有两层嵌套循环,变量 i(外层)和 j(内层)。三层以上,编译出来的
为了保持精确性和与汇编的兼容性,应该这样调用: 示例代码 6 DelayConstantInner = XXX; DelayConstantMiddle = XXX; DelayConstantOuter = XXX; TimeDelay()
这样,生成的汇编代码与上面的纯汇编写法完全一致。XXX 的最小值是 1, 最大值是 255,切记不能为 0。
我找到了解决之道,参看示例代码 7。
示例代码 7
unsigned char i = 0;
unsigned char j = 0;
for(i = 0;i < 29;i++)
{
for(j = 0;j < 62;j++)
{
_nop_();
}
_nop_();
}
编译器做出来的代码类似如下格式(蓝色行是汇编代码,红色行是 C 源代码):
号为 31h 的内部 RAM,delayr7 占据了地址编号为 32h 的内部 RAM。具体的地址
根据程序需要自行修改。好了,现在看一下延时函数的设计:
示例代码 2:
DELAY:
mov r5,delayr5 ;2 个机器周期
DELAY1:
mov r6,delayr6 ;2 个机器周期
DELAY2:
mov r7,delayr7 ;2 个机器周期
MOV
R6,A
63:
{
64:
_nop_();
C:0x11A9 00
NOP
65:
}
C:0x11AA 0E
INC
R6
C:0x11AB BE3EFB CJNE R6,#0x3E,C:11A9
66:
_nop_();
C:0x11AE 00
NOP
67:
}
C:0x11AF 0F
INC
R7
C:0x11B0 BF1DF4 CJNE R7,#0x1D,C:11A7
DELAY3:
djnz r7,DELAY3 ;2 个机器周期
djnz r6,DELAY2 ;2 个机器周期
djnz r5,DELAY1 ;2 个机器周期
ret
;2 个机器周期