搜档网
当前位置:搜档网 › 单片机的C语言延时程序

单片机的C语言延时程序

单片机的C语言延时程序
单片机的C语言延时程序

单片机C语言环境延时程序设计

东北电力学院(132012)盛文利

摘要:讨论运用C语言的单片机开发中,对于时序要求苛刻的延时的开发过程。实例说明在高级语言中,严格控制时序的可能性和注意事项。

关键词:单片机C语言编译器

现在单片机的开发中,C语言以其方便、灵活、逻辑性强、易于维护和可移植性好等强大优势,占据了单片机开发的主要地位。过去,由于单片机C 语言本身存在的缺陷,加之单片机工程本身都不大,C语言在单片机中的应用没有被重视。在C语言环境下,我们只需关心程序的逻辑关系,实现细节由编译器完成,这给我们快速开发提供了条件,也大大减小了开发工作量。但同时实现细节就不被控制了,这个问题对于经常重视实现细节的单片机,就必须得到解决。好在一般的C语言编译器都提供嵌入汇编、与汇编互调用和汇编级的代码察看等功能。现以KEIL C51[1]下的一线总线器件程序开发为例,说明我的解决方法。

1 一线总线协议器件

一线总线协议(1 wire bus protocol)也叫做单总线协议,是单总线器件数据传输协议。因为数据(也可以包括电源)都由一条线来传输,没有辅助的数据同步信号,这时就只能由时序来解析协议内容。这样就只有依靠对时序的严格要求,来保障数据传输的准确性。

单总线协议的信号主要有:初始化时序,包括复位脉冲和在线脉冲,是主机与器件的联络信号。然后是读、写时序。单总线器件完全依靠对线上的时序来做出响应,这些有效的时序称作时隙,也有称作时间片的,是同一词汇的不同翻译[2]。

初始化时序时间裕度大,容易实现。读写脉冲对时序要求相对严格,尤其在慢速的MCS-51下,指令的运行在微妙级,而读写时序在15微秒的后小部分,大约4微秒,不同批次的芯片会有少许差距。有的会允许你的时序有少许误差,有的则非常严格。

2 C语言编译器

在用汇编语言编写的程序时,很容易控制时间,因为我们知道每条语句的执行时间,每段宏的执行时间,每段子程序加调用语句所消耗的时间。在单片机的C语言开发中,C语言编译器都对标准C 作了针对单片机特点的扩展。但对于不同的单片机,不同的C语言编译器在将源程序翻译成目标机器语言时,会有不同的编译方法,在生成目标代码时,会有所不同。开发人员必须研究它生成的汇编语言代码,来保证时间的准确性。这也许是除了使用嵌入汇编或直接编写汇编函数的唯一方法。其实在单片机的C编译器中,已经有足够的底层操作方面的扩展,所以这里只考虑纯C语言的方法。

3. 延时程序设计

我们以德国Keil公司的MCS-51C语言编译器为例,目前它已被公认为业界的标准。以下讨论均假设单片机时钟晶振为6兆的51芯片,以小模式下编译,这时MCS-51程序指令执行的最小单位是2微秒。如果使用非英特尔且内核优化过的单片机,应切换回普通模式,或仔细研究它的时序。以DS18B20为例,我们临时在程序中需要延时2微秒,那末可以用以下程序

#include

void somefunc(void)

{ ……;

_nop_();

……;

}

如果要延时64微秒甚至640微妙,我们不可能在程序中重复上述_nop_(),虽然这并不会出错。可以利用它来构建延时程序,精确定时的问题就变为延时程序的精度问题。我们先讨论以上的空操作延时。

3.1 空操作延时及延时函数

注意到DS18B20最小时隙除大于一微秒的外,最小的就是15微秒。而数据的读或写也包含在这15微秒中。由于定时器的延时要对定时器进行初始化,不易得到小的延时。只能用于复位脉冲,即在480至960微秒之间的延时,程序实现接近960

微秒的延时

……;

TH0 = -950/256;

TL0 = -950%256;

TMOD= TMOD|0x01

TR0 = 1;

……;

定时器启后,单片机可以作其他事情,如复位看门狗,或LED扫描。

而调用的短延时函数必需精确的受到控制,先作一个几微秒的延时实验程序,以观察它实际的延时时间,又因为我们在汇编时代常用的延时指令为MOV R7, DDLY

DJNZ R7,$

以便产生最小4微秒的延时,仿造的C程序如下void delay1(ndly)

{for(;ndly>0;ndly--)

}

生成的汇编代码却与DJNZ无关,是如下形式,其中无关的编译注释已删除

RSEG ?PR?_delay1?DLY2

_delay1:

USING 0

?C0006:

MOV A,R7 ;2us

SETB C ;2us

SUBB A,#00H ;2us

JC ?C0009 ;2us

DEC R7 ;2us

SJMP ?C0006 ;4us

?C0009:

RET ;4us

; END OF _delay1

延时至少18微秒,无法用于读写时隙。

改用如下程序

void delay2(unsigned char vd)

{ for (vd=0; vd<10;vd++)

}

生成的汇编代码如下

RSEG ?PR?_delay2?DLY2

_delay2:

USING 0

CLR A ;2us

MOV R7,A ;2us

?C0002:

INC R7 ;2us

CJNE R7,#0AH,?C0002 ;4us

?C0005:

RET ;4us

; END OF _delay2

以上代码调用一次也有14微秒之多,还是无

法使用。也就是说采用for形式的语句,生成的汇

编代码都是先减,再比较,还是与DJNZ无关,也

就不能产生短延时。

3.2短延时程序编写

首先想到的是用汇编语言函数,或嵌入汇编的

方法,例如汇编语言函数实现

NAME DLY

?PR?_delay3?DLY SEGMENT CODE

PUBLIC _delay3

RSEG ?PR?_delay3?DLY

_delay3:

DJNZ R7, $ ;4us

RET ;4us

END

或嵌入汇编的方法,

void delay4(unsigned char vd)

{#pragma asm

djnz R7, $ ;

#pragma endasm

}

编译后的形式

?PR?_delay4?DLY SEGMENT CODE

?DT?_delay4?DLY SEGMENT DATA OVERLAYABLE PUBLIC _delay4

RSEG ?DT?_delay4?DLY2

?_delay4?BYTE:

vd?040: DS 1

RSEG ?PR?_delay4?DLY2

_delay4:

MOV vd?040,R7 ;2us

djnz R7, $ ;

RET

; END OF _delay4

注意这里用于参数传递的R7,被复制保存起

来了,这样在调用时,比汇编书写的函数多出2微秒。

除此之外,我们还是可以编出全C的类似函数void delay5(unsigned char vd)

{ do {vd--;

}

while(vd);

}

编译后

RSEG ?PR?_delay5?DLY3

_delay5:

USING 0

?C0003:

DJNZ R7,?C0003

RET

看来DO__WHILE是和DJNZ相对应编译的。

4 调用时间和某些细节

除了直接插入_nop_()语句的短时间延时,在稍长的时间多变的延时程序中,往往需要调用上节讨论的延时函数来完成延时,其中以汇编语言函数和DO__WHILE最易控制,以为他们只有一条延时指令

DJNZ R7, $

这时,一定注意在函数传递的参数(通过R7付值)要2微秒,调用函数时执行的LCALL语句的4微秒,在函数返回时,RET指令的4微秒,还要至少执行DJNZ指令(R7为0,djnz后R7=256)4微秒,所以至少为14微秒,这应该是函数调用延时的极限状态。另外还要注意对于连续调用的函数例如:

{……;

void delay 1(void);

void delay2 (void);

……;

}

生成的汇编语言代码不是

lcall delay1;

lcall delay2;

他被编译器优化,在调用delay1进入并执行该函数,返回前直接跳入delay2执行,类似于delay1:

……

ljmp delay 2;

由delay 2执行RET返回,这样连续调用两个函数时,就变成一次调用加跳转,结果会省去2微秒。解决了延时问题,再根据参考文献[2],可写出读字节程序

unsigned char read_byte(void)

{ unsigned char i;

unsigned char value = 0;

for (i=0;i<8;i++)

{ value>>=1; //2us

D0 = 0;// pull DQ low to start timeslot

_nop_();//2us

_nop_();//2us

_nop_();//2us

D0 = 1;// then return high

_nop_();//2us

_nop_();//2us

if(D0) value|=0x80;_nop_(); //2us

delay(10);// wait for rest of timeslot

}

return(value);

}

写一字节的程序

void write_byte(unsigned char val)

{ unsigned char i;

for (i=0;i<8;i++) {

D0 = 0;// pull DQ low to start timeslot

delay(1);//can't read when 4

D0 = val&0x01;

val=val>>1;

delay(10);//because the write time must just fit for,

D0 = 1;

}

delay(2);

}

完整的18B20数据采集程序,再附件中,(可放在网上),其中包含了CRC校验。

5 结论

在现代竞争激烈的商品经济环境下,高级语言的运用大大提高了开发效率。在C等高级语言编写这类时间要求严格的程序时,分析最终的目标汇编代码是无法避免的。在现代带有集成开发环境的编译器中,有软件仿真,或是有硬件调试功能,都给精确定时程序的开发带来方便。充分发挥软件工具的潜力,才能真正在各种环境下发挥高级语言带

来的最大好处——效率。

参考文献

1 Keil Software .Keil uVision

2 Reference.Keil Electronik GmbH/Keil Software,Inc.1995

2 DALLAS MAXIM产品资料光盘 6.0版2002年

作者简介:作者我(盛文利)1964年出生1983年考入成都电讯工程学院,1987年毕业。1991年调入东北电力学院应用电子技术实验室。现任职称工程师。

STC12系列单片机C语言的延时程序

STC12系列单片机C语言的延时程序 本举例所用CPU 为STC12C5412 系列12 倍速的单片机,只要修改一下参数值其它系例单片机也通用,适用范围宽。共有三条延时函数说明如下:函数调用 分两级:一级是小于10US 的延时,二级是大于10US 的延时 //====================小于10US 的【用1US 级延时】 ====================//----------微秒级延时---------for(i=X;i>X;i--) 延时时间 =(3+5*X)/12 提示(单位us, X 不能大于255)//================大于10US0;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 中断:void timer0(void) interrupt 1{ TL0=(0xffff-1000+2)% 0x100;TH0=(0xffff-1000+2)/0x100; //每毫秒执行一次if(DelayMs_1>0) DelayMs_1--;//大于20Ms 延时程序}函数调用void DelayMs(uint a)//延时 a 乘以1(ms)的时间。{ DelayMs_1=a; while(DelayMs_1);}如果延时50Ms 则函数值为DelayMs(50)tips:感谢大家的阅读,本文由我司收集整编。仅供参阅!

单片机C延时时间怎样计算

C程序中可使用不同类型的变量来进行延时设计。经实验测试,使用unsigned char类型具有比unsigned int更优化的代码,在使用时 应该使用unsigned char作为延时变量。以某晶振为12MHz的单片 机为例,晶振为12M H z即一个机器周期为1u s。一. 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--); } 计算分析: 程序共有三层循环 一层循环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--);

C语言延时程序

C51精确延时程序 一、、看了网上延时程序的帖子挺多,我也说点。 用keil调试, void yanshi( uint n ) { uchar data i="0"; for(i=0;i<N;I++); return; } 延时时间=12*(n*12+17)/fosc 用keil测时功能很容易得到这个关系,很精确,偏差不过几us. 可以自己编一些延时程序,也可以很方便的得到关系式,只是系数不同. 二、、//我看到的地方也是从别的地方转贴,所以我不知道原作者是谁,但相信这么成熟的东西转一下他也不会见意。看到了个好帖,我在此在它得基础上再抛抛砖! 有个好帖,从精度考虑,它得研究结果是: void delay2(unsigned char i) { while(--i); } 为最佳方法。 分析:假设外挂12M(之后都是在这基础上讨论) 我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据: delay2(0):延时518us 518-2*256=6 delay2(1):延时7us(原帖写“5us”是错的,^_^) delay2(10):延时25us 25-20=5 delay2(20):延时45us 45-40=5 delay2(100):延时205us 205-200=5 delay2(200):延时405us 405-400=5 见上可得可调度为2us,而最大误差为6us。 精度是很高了! 但这个程序的最大延时是为518us 显然不 能满足实际需要,因为很多时候需要延迟比较长的时间。 那么,接下来讨论将t分配为两个字节,即uint型的时候,会出现什么情况。

用单片机实现延时(自己经验及网上搜集).

标准的C语言中没有空语句。但在单片机的C语言编程中,经常需要用几个空指令产生短延时的效果。这在汇编语言中很容易实现,写几个nop就行了。 在keil C51中,直接调用库函数: #include // 声明了void _nop_(void; _nop_(; // 产生一条NOP指令 作用:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒。NOP指令为单周期指令,可由晶振频率算出延时时间,对于12M晶振,延时1uS。对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。 在选择C51中循环语句时,要注意以下几个问题 第一、定义的C51中循环变量,尽量采用无符号字符型变量。 第二、在FOR循环语句中,尽量采用变量减减来做循环。 第三、在do…while,while语句中,循环体内变量也采用减减方法。 这因为在C51编译器中,对不同的循环方法,采用不同的指令来完成的。 下面举例说明: unsigned char i; for(i=0;i<255;i++; unsigned char i; for(i=255;i>0;i--;

其中,第二个循环语句C51编译后,就用DJNZ指令来完成,相当于如下指令: MOV 09H,#0FFH LOOP: DJNZ 09H,LOOP 指令相当简洁,也很好计算精确的延时时间。 同样对do…while,while循环语句中,也是如此 例: unsigned char n; n=255; do{n--} while(n; 或 n=255; while(n {n--}; 这两个循环语句经过C51编译之后,形成DJNZ来完成的方法, 故其精确时间的计算也很方便。 其三:对于要求精确延时时间更长,这时就要采用循环嵌套的方法来实现,因此,循环嵌套的方法常用于达到ms级的延时。对于循环语句同样可以采用for,do…while,while结构来完成,每个循环体内的变量仍然采用无符号字符变量。 unsigned char i,j for(i=255;i>0;i--

基于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 #include void main(void){ P1=0x0; _nop_();//延时1us P1=0xff; } ----------------------------------------------------------------------------------------------------------------------2.延时5us,则可以写一个delay_5us()函数: delay_5us(){ #pragma asm nop #pragma endasm } 这就是一个延时5us的函数,只需要在需要延时5us时调用此函数即可。或许有人会问,只有一个NOP指令,怎么是延时5us呢? 答案是:在调用此函数时,需要一个调用指令,此指令消耗2个周期(即2us);函数执行完毕时要返回主调函数,需要一个返回指令,此指令消耗2个周期(2us)。调用和返回消耗了2us+2us=4us。然后再加上一个NOP指令消耗1us,不就是5us吗。

51单片机的几种精确延时

51单片机的几种精确延时实现延时 51单片机的几种精确延时实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高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的延时函数可编写如下: void Delay10us( ) { _NOP_( ); _NOP_( ); _NOP_( ); _NOP_( ); _NOP_( ); _NOP_( ); } Delay10us( )函数中共用了6个_NOP_( )语句,每个语句执行时间为1 μs。主函数调用Delay10us( )时,先执行一个LCALL指令(2 μs),然后执行6个_NOP_( )语句(6 μs),最后执行了一个RET指令(2 μs),所以执行上述函数时共需要10 μs。可以把这一函数

51单片机精确延时源程序

51单片机精确延时源程序 一、晶振为 11.0592MHz,12T 1、延时 1ms: (1)汇编语言: 代码如下: DELAY1MS: ;误差 -0.651041666667us MOV R6,#04H DL0: MOV R5,#71H DJNZ R5,$ DJNZ R6,DL0 RET (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.000000000002us MOV R6,#97H DL0: MOV R5,#1DH DJNZ R5,$ DJNZ R6,DL0

RET (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.000000000021us MOV R7,#23H DL1: MOV R6,#0AH I

棋影淘宝店:https://www.sodocs.net/doc/874419496.html,QQ:149034219 DL0: MOV R5,#82H DJNZ R5,$ DJNZ R6,DL0 DJNZ R7,DL1 RET (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.00000000024us MOV R7,#5FH DL1: MOV R6,#1AH DL0: MOV R5,#0B9H DJNZ R5,$ DJNZ R6,DL0 DJNZ R7,DL1 RET (2)C语言: void delay1s(void) //误差 -0.00000000024us { unsigned char a,b,c; for(c=95;c>0;c--) for(b=26;b>0;b--)

用C语言实现精确的延时

怎么用C语言做单片机的精确延时在单片机应用中,经常会遇到需要短时间延时的情况,一般都是几十到几百μs,并且需要很高的精度(比如用单片机驱动DS18B20时,误差容许的范围在十几μs以内,不然很容易出错);而某些情况下延时时间较长,用计时器往往有点小题大做。另外在特殊情况下,计时器甚至已经全部用于其他方面的定时处理,此时就只能使用软件定时了[1]。 1C语言程序延时 Keil C51的编程语言常用的有2种:一种是汇编语言;另一种是C语言。用汇编语言写单片机程序时,精确时间延时是相对容易解决的。比如,用的是晶振频率为12MHz 的AT89C51,打算延时20μs,51单片机的指令周期是晶振频率的1/12,即一个机器周期为1μs;“MOV R0,#X”需要2个机器周期,DJNZ也需要2个机器周期,单循环延时时间t=2X+3(X为装入寄存器R0的时间常数)[2]。这样,存入R0里的数初始化为8即可,其精度可以达到1μs。用这种方法,可以非常方便地实现512μs以下时间的延时。如果需要更长时间,可以使用两层或更多层的嵌套,当然其精度误差会随着嵌套层的增加而成倍增加。 虽然汇编语言的机器代码生成效率很高,但可读性却并不强,复杂一点的程序就更难读懂;而C语言在大多数情况下,其机器代码生成效率和汇编语言相当,但可读性和可移植性却远远超过汇编语言,且C语言还可以嵌入汇编程序来解决高时效性的代码编写问题。就开发周期而言,中大型软件的编写使用C语言的开发周期通常要比汇编语言短很多,因此研究C语言程序的精确延时性能具有重要的意义。 C程序中可使用不同类型的变量来进行延时设计。经实验测试,使用unsigned char类型具有比unsigned int更优化的代码,在使用时应该使用unsigned char作为延时变量。 2单层循环延时精度分析 下面是进行μs级延时的while程序代码。 延时函数: void delay1(unsigned char i){ while(i);} 主函数: void main(){ while(1){ delay1(i); } } 使用Keil C51的反汇编功能,延时函数的汇编代码如下: C:0x00E6AE07MOVR6,0x07 C:0x00E81FDECR7 C:0x00E9EEMOVA,R6 C:0x00EA70FAJNZC:00E6 C:0x00EC22RET 图1断点设置位置图 通过对i赋值为10,在主程序中图1所示的位置设置断点。经过测试,第1次执行到断点处的时间为457μs,再次执行到该处的时间为531μs,第3次执行到断点处的时间为605μs,10次while循环的时间为74μs,整个测试结果如图2所示。 图2使用i--方式测试仿真结果图 通过对汇编代码分析,时间延迟t=7X+4(其中X为i的取值)。测试表明,for循环方式虽然生成的代码与用while语句不大一样,但是这两种方法的效率几乎相同。C语言中的自减方式有两种,前面都使用的是i--的方式,能不能使用--i方式来获得不同的效果呢?将前面的主函数保持不变,delay1函数修改为下面的方式:void delay1(unsigned char i){ while(--i);} 同样进行反汇编,得到如下结果: C:0x00E3DFFEDJNZR7, C:00E3C:0x00E522RET 比较发现,--i的汇编代码效率明显高于i--方式。由于只有1条语句DJNZ,执行只需要2个时钟周期,1个时钟周期按1μs计算,其延时精度为2μs;另外,RET

51单片机延时模块程序

51单片机独立模块 一、延时模块 1、for循环延时 void delayms(UINT8 ms) { UINT8 x,y; for(x=ms;x>0;x--) for(y=112;y>0;y--); } 2、while循环延时 void delayms(UINT8 ms) { UINT8 x; while(ms--) for(x=112;x>0;x--); } 3、精确的单片机常用延时函数:(c代码误差0us 12M)(1)、延时0.5ms void delay0.5ms(void) //误差 0us { unsigned char a,b; for(b=71;b>0;b--) for(a=2;a>0;a--); } (2)、延时1ms void delay1ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c>0;c--) for(b=142;b>0;b--) for(a=2;a>0;a--); } (3)、延时2ms void delay2ms(void) //误差 0us { unsigned char a,b; for(b=4;b>0;b--) for(a=248;a>0;a--); _nop_; //if Keil,require use intrins.h } (4)、延时3ms void delay3ms(void) //误差 0us

{ unsigned char a,b; for(b=111;b>0;b--) for(a=12;a>0;a--); } (5)、延时4ms void delay4ms(void) //误差 0us { unsigned char a,b,c; for(c=7;c>0;c--) for(b=8;b>0;b--) for(a=34;a>0;a--); } (6)、延时5ms void delay5ms(void) //误差 0us { unsigned char a,b; for(b=19;b>0;b--) for(a=130;a>0;a--); } (7)、延时10ms void delay10ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c>0;c--) for(b=38;b>0;b--) for(a=130;a>0;a--); } (8)、延时15ms void delay15ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c>0;c--) for(b=238;b>0;b--) for(a=30;a>0;a--); } (9)、延时20ms void delay20ms(void) //误差 0us { unsigned char a,b; for(b=215;b>0;b--) for(a=45;a>0;a--); _nop_; //if Keil,require use intrins.h

单片机一些常用的延时与中断问题及解决方法

延时与中断出错,是单片机新手在单片机开发应用过程中,经常会遇到的问题,本文汇总整理了包含了MCS-51系列单片机、MSP430单片机、C51单片机、8051F的单片机、avr单片机、STC89C52、PIC单片机…..在内的各种单片机常见的延时与中断问题及解决方法,希望对单片机新手们,有所帮助! 一、单片机延时问题20问 1、单片机延时程序的延时时间怎么算的? 答:如果用循环语句实现的循环,没法计算,但是可以通过软件仿真看到具体时间,但是一般精精确延时是没法用循环语句实现的。 如果想精确延时,一般需要用到定时器,延时时间与晶振有关系,单片机系统一般常选用11.059 2 MHz、12 MHz或6 MHz晶振。第一种更容易产生各种标准的波特率,后两种的一个机器周期分别为1 μs和2 μs,便于精确延时。本程序中假设使用频率为12 MHz的晶振。最长的延时时间可达216=65 536 μs。若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。 2、求个单片机89S51 12M晶振用定时器延时10分钟,控制1个灯就可以 答:可以设50ms中断一次,定时初值,TH0=0x3c、TL0=0xb0。中断20次为1S,10分钟的话,需中断12000次。计12000次后,给一IO口一个低电平(如功率不够,可再加扩展),就可控制灯了。 而且还要看你用什么语言计算了,汇编延时准确,知道单片机工作周期和循环次数即可算出,但不具有可移植性,在不同种类单片机中,汇编不通用。用c的话,由于各种软件执行效率不一样,不会太准,通常用定时器做延时或做一个不准确的延时,延时短的话,在c中使用汇编的nop做延时 3、51单片机C语言for循环延时程序时间计算,设晶振12MHz,即一个机器周期是1us。for(i=0,i<100;i++) for(j=0,j<100;j++) 我觉得时间是100*100*1us=10ms,怎么会是100ms 答: 不可能的,是不是你的编译有错的啊 我改的晶振12M,在KEIL 4.0 里面编译的,为你得出的结果最大也就是40ms,这是软件的原因, 不可能出现100ms那么大的差距,是你的软件的原因。 不信你实际编写一个秒钟,利用原理计算编写一个烧进单片机和利用软件测试的秒程序烧进单片机,你会发现原理计算的程序是正确的

51单片机延时时间计算和延时程序设计

一、关于单片机周期的几个概念 ●时钟周期 时钟周期也称为振荡周期,定义为时钟脉冲的倒数(可以这样来理解,时钟周期就是单片机外接晶振的倒数,例如12MHz的晶振,它的时间周期就是1/12 us),是计算机中最基本的、最小的时间单位。 在一个时钟周期内,CPU仅完成一个最基本的动作。 ●机器周期 完成一个基本操作所需要的时间称为机器周期。 以51为例,晶振12M,时钟周期(晶振周期)就是(1/12)μs,一个机器周期包 执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期也不同。 对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。 1.指令含义 DJNZ:减1条件转移指令 这是一组把减1与条件转移两种功能结合在一起的指令,共2条。 DJNZ Rn,rel ;Rn←(Rn)-1 ;若(Rn)=0,则PC←(PC)+2 ;顺序执行 ;若(Rn)≠0,则PC←(PC)+2+rel,转移到rel所在位置DJNZ direct,rel ;direct←(direct)-1 ;若(direct)= 0,则PC←(PC)+3;顺序执行 ;若(direct)≠0,则PC←(PC)+3+rel,转移到rel 所在位置 2.DJNZ Rn,rel指令详解 例:

MOV R7,#5 DEL:DJNZ R7,DEL; rel在本例中指标号DEL 1.单层循环 由上例可知,当Rn赋值为几,循环就执行几次,上例执行5次,因此本例执行的机器周期个数=1(MOV R7,#5)+2(DJNZ R7,DEL)×5=11,以12MHz的晶振为例,执行时间(延时时间)=机器周期个数×1μs=11μs,当设定立即数为0时,循环程序最多执行256次,即延时时间最多256μs。 2.双层循环 1)格式: DELL:MOV R7,#bb DELL1:MOV R6,#aa DELL2:DJNZ R6,DELL2; rel在本句中指标号DELL2 DJNZ R7,DELL1; rel在本句中指标号DELL1 注意:循环的格式,写错很容易变成死循环,格式中的Rn和标号可随意指定。 2)执行过程

单片机精确毫秒延时函数

单片机精确毫秒延时函数 实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;另一种是软件延时,这种方法主要采用循环体进行。今天主要介绍软件延时以及单片机精确毫秒延时函数。 单片机的周期介绍在电子技术中,脉冲信号是一个按一定电压幅度,一定时间间隔连续发出的脉冲信号。脉冲信号之间的时间间隔称为周期;而将在单位时间(如1秒)内所产生的脉冲个数称为频率。频率是描述周期性循环信号(包括脉冲信号)在单位时间内所出现的脉冲数量多少的计量名称;频率的标准计量单位是Hz(赫)。电脑中的系统时钟就是一个典型的频率相当精确和稳定的脉冲信号发生器。 指令周期:CPU执行一条指令所需要的时间称为指令周期,它是以机器周期为单位的,指令不同,所需的机器周期也不同。对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。 时钟周期:也称为振荡周期,一个时钟周期= 晶振的倒数。对于单片机时钟周期,时钟周期是单片机的基本时间单位,两个振荡周期(时钟周期)组成一个状态周期。 机器周期:单片机的基本操作周期,在一个操作周期内,单片机完成一项基本操作,如取指令、存储器读/写等。 机器周期=6个状态周期=12个时钟周期。 51单片机的指令有单字节、双字节和三字节的,它们的指令周期不尽相同,一个单周期指令包含一个机器周期,即12个时钟周期,所以一条单周期指令被执行所占时间为12*(1/ 晶振频率)= x s。常用单片机的晶振为11.0592MHz,12MHz,24MHz。其中11.0592MHz 的晶振更容易产生各种标准的波特率,后两种的一个机器周期分别为1 s和2 s,便于精确延时。 单片机精确毫秒延时函数对于需要精确延时的应用场合,需要精确知道延时函数的具体延

c语言中的精确延时程序举例

c语言中的精确延时程序举例 我在网上到看了一些关于延时的讨论,其中有篇文章51单片机Keil C 延时程序的简单研究,作者:InfiniteSpace Studio/isjfk 写得不错,他是用while(--i);产生DJNZ 来实现精确延时,后来有人说如果while里面不能放其它语句,否则也不行,用do-while就可以,具体怎样我没有去试.所有这些都没有给出具体的实例程序来.还看到一些延时的例子多多少少总有点 延时差.为此我用for循环写了几个延时的子程序贴上来,希望能对初学者有所帮助.(晶振12 MHz,一个机器周期1us.) 一. 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,#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延时子程序程序: 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--); } 产生的汇编

单片机写延时程序的几种方法

单片机写延时程序的几种方法 1)空操作延時(12MHz) void delay10us() { _NOP_(); _NOP_(); _NOP_(); _NOP_(); _NOP_(); _NOP_(); } 2)循環延時 (12MHz) Void delay500ms() { unsigned char i,j,k; for(i=15;i>;0;i--) for(j=202;j>;0;j--) for(k=81;k>;0;k--); }

延時總時間=[(k*2+3)*j+3]*i+5 k*2+3=165 us 165*j+3=33333 us 33333*i+5=500000 us=500 ms 3)計時器中斷延時(工作方式2) (12MHz) #include; sbit led=P1^0; unsigned int num=0; void main() { TMOD=0x02; TH0=6; TL0=6; EA=1; ET0=1; TR0=1; while(1) { if(num==4000) { num=0;

led=~led; } } } void T0_time() interrupt 1 { num++; } 4)C程序嵌入組合語言延時 #pragma asm …… 組合語言程序段 …… #pragma endasm KEIL軟件仿真測量延時程序延時時間

這是前段事件總結之延時程序、由於不懂組合語言,故NO.4無程序。希望對你有幫助!!! 對於12MHz晶振,機器周期為1uS,在執行該for循環延時程式的時候 Void delay500ms() { unsigned char i,j,k; for(i=15;i>;0;i--) for(j=202;j>;0;j--) for(k=81;k>;0;k--); } 賦值需要1個機器周期,跳轉需要2個機器周期,執行一次for循環的空操作需要2個機器周期,那么,對於第三階循環 for(k=81;k>;0;k--);,從第二階跳轉到第三階需要2機器周期,賦值需要1個機器周期,執行81次則需要2*81個機器周期,執行一次二階for循環的事件為81*2+1+2;執行了220次,則(81*2+3)*220+3,執行15次一階循環,則 [(81*2+3)*220+3]*15,由於不需要從上階跳往下階,則只加賦值的一個機器周期,另外進入該延時子函數和跳出該函數均需要2個機器周期,故

Keil C51精确延时程序(C语言)

Keil C51精确延时程序 程序说明如下: 振荡频率:12MHz 机器周期=12/振荡频率=12/12000000=1us #include 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=25us delay2(10); //赋值并调延时程序delay2 说明:本句为赋值并调用Delayus2:延时时间=(1+2)*机器周期。 全部延时时间为:延时时间=(1+2+6*i+2)*机器周期。 i=1~255。 本例:延时时间=(1+2+6*10+2)*1us=65us m=10; //赋值,m=1~255 while(--m) ; //计算,延时时间=2*m*机器周期 说明:本两句为赋值并计算。 全部延时时间为:延时时间=(1+2*m)*机器周期。 m=1~255。 本例:延时时间=(1+2*10)*1us=25us while(1); }

单片机一些常用的延时与中断问题及解决方法

单片机一些常用的延时与中断问题及解决方法 延时与中断出错,是单片机新手在单片机开发应用过程中,经常会遇到的问题,本文汇总整理了包含了MCS-51系列单片机、MSP430单片机、C51单片机、8051F的单片机、avr单片机、STC89C52、PIC单片机…..在内的各种单片机常见的延时与中断问题及解决方法,希望对单片机新手们,有所帮助! 一、单片机延时问题20问 1、单片机延时程序的延时时间怎么算的? 答:如果用循环语句实现的循环,没法计算,但是可以通过软件仿真看到具体时间,但是一般精精确延时是没法用循环语句实现的。 如果想精确延时,一般需要用到定时器,延时时间与晶振有关系,单片机系统一般常选用 2 MHz、12 MHz或6 MHz晶振。第一种更容易产生各种标准的波特率,后两种的一个机器周期分别为1 μs和2 μs,便于精确延时。本程序中假设使用频率为12 MHz的晶振。最长的延时时间可达216=65 536 μs。若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。 2、求个单片机89S51 12M晶振用定时器延时10分钟,控制1个灯就可以 答:可以设50ms中断一次,定时初值,TH0=0x3c、TL0=0xb0。中断20次为1S,10分钟的话,需中断12000次。计12000次后,给一IO口一个低电平(如功率不够,可再加扩展),就可控制灯了。 而且还要看你用什么语言计算了,汇编延时准确,知道单片机工作周期和循环次数即可算出,但不具有可移植性,在不同种类单片机中,汇编不通用。用c的话,由于各种软件执行效率不一样,不会太准,通常用定时器做延时或做一个不准确的延时,延时短的话,在c中使用汇编的nop做延时 3、51单片机C语言for循环延时程序时间计算,设晶振12MHz,即一个机器周期是1us。for(i=0,i<100;i++) for(j=0,j<100;j++) 我觉得时间是100*100*1us=10ms,怎么会是100ms 答: 不可能的,是不是你的编译有错的啊 我改的晶振12M,在KEIL 里面编译的,为你得出的结果最大也就是40ms,这是软件的原因, 不可能出现100ms那么大的差距,是你的软件的原因。 不信你实际编写一个秒钟,利用原理计算编写一个烧进单片机和利用软件测试的秒程序烧进单片机,你会发现原理计算的程序是正确的 4 、51单片机c语言 _nop_()是一个空指令短时间延时的空几个机器周期?

单片机延时程序分析#(优选.)

上一次课中,我们已经知道,程序中的符号R7、R6是代表了一个个的RAM单元,是用来放一些数据的,下面我们再来看一下其它符号的含义。 DELAY:MOV R7,#250;(6) D1:MOV R6,#250 ;(7) D2:DJNZ R6,D2 ;(8) DJNZ R7,D1;(9) RET ;(10) 〈单片机延时程序〉 MOV:这是一条指令,意思是传递数据。说到传递,我们都很清楚,传东西要从一本人的手上传到另一本人的手上,也就是说要有一个接受者,一个传递者和一样东西。从指令M OV R7,#250中来分析,R7是一个接受者,250是被传递的数,传递者在这条指令中被省略了(注意:并不是每一条传递指令都会省的,事实上大部份数据传递指令都会有传递者)。它的意义也很明显:将数据250送到R7中去,因此执行完这条指令后,R7单元中的值就应当是250。在250前面有个#号,这又是什么意思呢?这个#就是用来说明250就是一个被传递的东西本身,而不是传递者。那么MOV R6,#250是什么意思,应当不用分析了吧。 DJNZ:这是另一条指令,我们来看一下这条指令后面跟着的两个东西,一个是R6,一个是D2,R6我们当然已知是什么了,查一下D2是什么。D2在本行的前面,我们已学过,这称之为标号。标号的用途是什么呢?就是给本行起一个名字。DJNZ指令的执行过程是这样的,它将其后面的第一个参数中的值减1,然后看一下,这个值是否等于0,如果等于0,就往下执行,如果不等于0,就转移,转到什么地方去呢?可能大家已猜到了,转到第二个参数所指定的地方去(请大家用自已的话讲一下这条语句是怎样执行的)。本条指令的最终执行结果就是,在原地转圈250次。

单片机延时程序

实验一单片机延时程序实验 一、实验目的与要求: 在使用4MH在外部晶体振荡器的PIC16F877A上用软件设计一个20ms的软件延时子程序。另外,还要求用MPLAB的软件模拟器及其附带的软件工具窗口stopwatch观测延时程序执行的时间。 二、实验内容: 1.硬件电路设计: 本实验中用的是软件延时,利用循环来实现延时功能。电路就用了单片机的原本电路。没有用到其他的功能模块,单片机与ICD3相连接。 2.软件设计思路: 单片机软件延时的前提和基础是每条指令的执行时间是固定的,且大部分指令的执行时间是相同的。这要求对每条指令所花费的指令周期(Tcy)做到心中有数。指令集中5条无条件跳转指令GOTO,CALL.RETURN,RETLW和RETFIE,由于它们必然引起程序跳转,造成流水线中断,因此肯定将占用2个指令周期。而其他4条有可能引起程序跳转的条件跳转指令DECFSZ,INCFSZ,BTFSC和,BTFSS的执行时间,需要占用2个指令周期,当条件为假不发生跳转时,仅占用1个指令周期。其余所有指令都只用1个指令周期。

每个指令周期Tcy的时间长度,计算方法:如果采用4MHz 的外部晶体(fosc=4 MHz),则PIC中档单片机的指令周期Tcy 为1us,这是一个整数。而采用其他频率的外部晶体时,指令周期时间将反比于外部晶体频率。 至于软件延时的结构和实现方法,其实可以采用任何指令和结构,因为只是通过执行指令耗费时间。但通常情况下有两个选择延时程序结构的原则: (1)执行指令周期数计算方便。如果含有太多复杂的条件跳转循环等结构势必会造成指令周期的计算困难,甚至可 能造成执行所造成的软件延时时间不等。 (2)不能占用太多的程序空间。试想用20000个NOP指令来实现20ms的延时,显然是可以的,但是这样做浪费了 整整一个页的程序存储器,得不偿失,而通过适当的循 环结构,重复执行某些相同的程序是比较合理的方法。 因此,软件延时程序一般采用下列方法:如果延时时间 短(微妙级别),可以连续插入几条NOP指令;如果延 时时间长(几个毫秒级别),则可以使用双嵌套循环的 方法来实现。 实验的流程图:

for循环实现C语言精确延时

for实现C语言精确延时C语言最大的缺点就是实时性差,我在网上到看了一些关于延时的讨论,其中有篇文章 51单片机Keil C 延时程序的简单研究, 写得不错,他是用while(--i);产生DJNZ 来实现精确延时,后来有人说如果while里面不能放其它语句,否则也不行,用do-while就可以,具体怎样我没有去试.所有这些都没有给出具体的实例程序来.还看到一些延时的例子多多少少总有点延时差.为此我用for循环写了几个延时的子程序贴上来,希望能对初学者有所帮助.(晶振12MHz,一个机器周期1us.) 一. 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,#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延时子程序 程序: 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--); } 产生的汇编 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延时子程序: 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--); } 产生的汇编 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延时子程序: 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: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 在精确延时的计算当中,最容易让人忽略的是计算循环外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响.

相关主题