当前位置:文档之家› 单片机延时计算

单片机延时计算

单片机延时计算
单片机延时计算

单片机C51延时时间怎样计算?

C程序中可使用不同类型的变量来进行延时设计。经实验测试,使用unsigned char类型具有比unsigned int 更优化的代码,在使用时应该使用unsigned char作为延时变量。以某晶振为12MHz的单片机为例,晶振为12MHz即一个机器周期为1us。

void delay__ms(void) //x,y,z位固定值,故不能接受参数

{

unsigned char i,j,k;

for(i=x;i>0;i--)

for(j=y;j>0;j--)

for(k=z;k>0;k--);

}

【Delay_Time=[(2z+3)*y+3]*x+5】

一. 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--);

}

四. 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--);

}

应用单片机的时候,经常会遇到需要短时间延时的情况。需要的延时时间很短,一般都是几十到几百微妙(us)。有时候还需要很高的精度,比如用单片机驱动DS18B20的时候,误差容许的范围在十几us以内,不然很容易出错。这种情况下,用计时器往往有点小题大做。而在极端的情况下,计时器甚至已经全部派上了别的用途。这时就需要我们另想别的办法了。以前用汇编语言写单片机程序的时候,这个问题还是相对容易解决的。比如用的是12MHz 晶振的51,打算延时20us,只要用下面的代码,就可以满足一般的需要:

mov r0, #09h

loop: djnz r0, loop

51单片机的指令周期是晶振频率的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 根据这段代码产生的汇编代码找了出来:

; SOURCE LINE # 18

;---- Variable 'i' assigned to Register 'R7' ----

; SOURCE LINE # 19

; SOURCE LINE # 20

0000 ?C0007:

0000 EF MOV A,R7

0001 6003 JZ ?C0010

0003 1F DEC R7

0004 80FA SJMP ?C0007

; SOURCE LINE # 21

0006 ?C0010:

0006 22 RET

; FUNCTION _delay2 (END)

真是不看不知道~~~一看才知道这个延时程序是多么的不准点~~~光看主要的那四条语句,就需要6个机器周期。也就是说,它的精度顶多也就是6us而已,这还没算上一条 lcall 和一条 ret。如果我们把调用函数时赋的i值根延时长度列一个表的话,就是:

i delay time/us

0 6

1 12

2 18

...

因为函数的调用需要2个时钟周期的lcall,所以delay time比从函数代码的执行时间多2。顺便提一下,有的朋友写的是这样的代码:

void delay2(unsigned char i)

{

unsigned char a;

for(a = i; a != 0; a--);

}

可能有人认为这会生成更长的汇编代码来,但是事实证明:

; FUNCTION _delay2 (BEGIN)

; SOURCE LINE # 18

;---- Variable 'i' assigned to Register 'R7' ----

; SOURCE LINE # 19

; SOURCE LINE # 21

;---- Variable 'a' assigned to Register 'R7' ----

0000 ?C0007:

0000 EF MOV A,R7

0001 6003 JZ ?C0010

0003 1F DEC R7

0004 80FA SJMP ?C0007

; SOURCE LINE # 22

0006 ?C0010:

0006 22 RET

其生成的代码是一样的。不过这的确不是什么好的习惯。因为这里实在没有必要再引入多余的变量。我们继续讨论正题。有的朋友为了得当更长的延时,甚至用了这样的代码:

void delay2(unsigned long i)

{

for(; i != 0; i--);

}

这段代码产生的汇编代码是什么样子的?其实不用想也知道它是如何恐怖的$#^%&%$......让我们看一看:

; FUNCTION _delay2 (BEGIN)

; SOURCE LINE # 18

0000 8F00 R MOV i+03H,R7

0002 8E00 R MOV i+02H,R6

0004 8D00 R MOV i+01H,R5

0006 8C00 R MOV i,R4

; SOURCE LINE # 19

; SOURCE LINE # 20

0008 ?C0007:

0008 E4 CLR A

0009 FF MOV R7,A

000A FE MOV R6,A

000B FD MOV R5,A

000C FC MOV R4,A

000D AB00 R MOV R3,i+03H

000F AA00 R MOV R2,i+02H

0011 A900 R MOV R1,i+01H

0013 A800 R MOV R0,i

0015 C3 CLR C

0016 120000 E LCALL ?C?ULCMP

0019 601A JZ ?C0010

001B E500 R MOV A,i+03H

001D 24FF ADD A,#0FFH

001F F500 R MOV i+03H,A

0021 E500 R MOV A,i+02H

0023 34FF ADDC A,#0FFH

0025 F500 R MOV i+02H,A

0027 E500 R MOV A,i+01H

0029 34FF ADDC A,#0FFH

002B F500 R MOV i+01H,A

002D E500 R MOV A,i

002F 34FF ADDC A,#0FFH

0031 F500 R MOV i,A

0033 80D3 SJMP ?C0007

; SOURCE LINE # 21

0035 ?C0010:

0035 22 RET

; FUNCTION _delay2 (END)

呵呵,这倒是的确可以延迟很长时间~~~但是毫无精度可言了。

那么,用C到底能不能实现精确的延时呢?我把代码稍微改了一下:

void delay1(unsigned char i)

{

while(i--);

}

因为根据经验,越简洁的C代码往往也能得出越简洁的机器代码。那这样结果如何呢?把它生成的汇编代码拿出来看一看就知道了。满怀希望的我按下了“Build target”键,结果打击是巨大的:

; FUNCTION _delay1 (BEGIN)

; SOURCE LINE # 13

;---- Variable 'i' assigned to Register 'R7' ----

; SOURCE LINE # 14

0000 ?C0004:

; SOURCE LINE # 15

0000 AE07 MOV R6,AR7

0002 1F DEC R7

0003 EE MOV A,R6

0004 70FA JNZ ?C0004

; SOURCE LINE # 16

0006 ?C0006:

0006 22 RET

; FUNCTION _delay1 (END)

虽说生成的代码跟用for语句是不大一样,不过我可以毫无疑问的说,这两种方法的效率是一样的。似乎到此为止了,因为我实在想不出来源程序还有什么简化的余地。看来我就要得出来这个结论了:“如果需要us级的延时精度,需要时用汇编语言。”但是真的是这样吗?我还是不甘心。因为我不相信大名鼎鼎的 Keil C 编译器居然连 djnz 都不会用???因为实际上程序体里只需要一句 loop: djnz r7, loop。近乎绝望之际(往往人在这种情况下确可以爆发出来,哦呵呵呵~~~),我随手改了一下:

void delay1(unsigned char i)

{

while(--i);

}

心不在焉的编译,看源码:

; FUNCTION _delay1 (BEGIN)

; SOURCE LINE # 13

;---- Variable 'i' assigned to Register 'R7' ----

; SOURCE LINE # 14

0000 ?C0004:

; SOURCE LINE # 15

0000 DFFE DJNZ R7,?C0004

; SOURCE LINE # 16

0002 ?C0006:

0002 22 RET

; FUNCTION _delay1 (END)

天~~~奇迹出现了......我想这个程序应该已经可以满足一般情况下的需要了。如果列个表格的话:

i delay time/us

1 5

2 7

3 9

...

计算延时时间时,已经算上了调用函数的lcall语句所花的2个时钟周期的时间。

终于,结果已经明了了。只要合理的运用,C还是可以达到意想不到的效果。很多朋友抱怨C效率比汇编差了很多,其实如果对Keil C的编译原理有一个较深入的理解,是可以通过恰当的语法运用,让生成的C代码达到最优化。即使这看起来不大可能,但还是有一些简单的原则可循的:1.尽量使用unsigned型的数据结构。2.尽量使用char型,实在不够用再用int,然后才是long。3.如果有可能,不要用浮点型。4.使用简洁的代码,因为按照经验,简洁的C代码往往可以生成简洁的目标代码(虽说不是在所有的情况下都成立)。

以下是测试实验:

★其实用for()循环就可以实验C51精确定时没有必要用while()?

? 有时候for()比while()好,但是又有时候相反(*^_^*)

只可惜不能传递参数!!!

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--);

}

▲实验记录:

i=5,j=132,k=150

0.000 389 00

0.200 391 00 0.200 002 00

(1+1)+[(2*150+1+2)*132+1+2]*5+2+1+2=200 002 us

▲产生的汇编代码为:

?

? ; FUNCTION delay (BEGIN)

;---- Variable 'i' assigned to Register 'R7' ---- 0000 7F05 MOV R7,#05H 1'

0002 ?C0001:

;---- Variable 'j' assigned to Register 'R6' ---- 0002 7E84 MOV R6,#084H 1'

0004 ?C0004:

;---- Variable 'k' assigned to Register 'R5' ---- 0004 7D96 MOV R5,#096H 1'

0006 ?C0007:

0006 DDFE DJNZ R5,?C0007 2'

0008 ?C0006:

0008 DEFA DJNZ R6,?C0004 2'

000A ?C0003:

000A DFF6 DJNZ R7,?C0001 2'

000C ?C0010:

000C 22 RET 2'

? ; FUNCTION delay (END)

▲延迟时间计算:

?Delay_Time=[(2k+1+2)*j+1+2]*i+2+1+2

?即:?

?【Delay_Time=[(2k+3)*j+3]*i+5】

▲程序代码:

void delay__ms(void) //x,y,z位固定值,故不能接受参数{

?unsigned char i,j,k;

?for(i=x;i>0;i--)

?for(j=y;j>0;j--)

?for(k=z;k>0;k--);

}

【Delay_Time=[(2z+3)*y+3]*x+5】

——————————————————————————-

〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒File: 关于while(--n)的优化设计〒

Time: 12:25-9:56 8:02- 〒

Date: 08-7-26-27 〒

Note: MOV Direct,#Data 为两个机器周期〒

?MOV Rn,Direct 为两个机器周期〒

〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒

▲测试代码:

void delay_us(unsigned char)

{

?while(--n);

}

▲测试记录:

n=1

0.000 398 00

0.000 407 00

0.000 415 00 0.000 008 00

1+2+2*1+2+1=8

n=2

0.000 400 00

0.000 411 00

0.000 421 00 0.000 010 00

1+2+2*2+2+1=10

n=3

0.000 402 00

0.000 415 00

0.000 427 00 0.000 012 00

1+2+2*3+2+1=12

n=255

0.000 906 00

0.001 423 00

0.001 939 00 0.000 516 00

1+2+2*255+2+1=516

▲Keil C编译成汇编代码:

MOV 0x80,#00H

MOV R7,#255 1'

LCALL delay_us 2'

delay_us:

?DJNZ R7,$ 2' 2*255

?RET 2'

▲测试结论:

延迟时间=1+2+2*n+2

即Time=2*n+5 误差:-1us

当n=255时,Time_Max=2*255+5=515 us

▲软件改进:

延迟时间Time=2*n+6 误差:0us

void delay_us(unsigned char)

{

?while(--n);

?_nop_();

}

Delay_Max=516 us

Delay_Min=8 us

?

▲改进后测试:

n=100

0.000 907 00

0.001 425 00

0.001 942 00 0.000 517 00

2*255+6+1=517 零误差

▲双重循环▲

void delay(unsigned char n)

{

?do{unsigned char m="47";while(--m);}while(--n); }

n=10

0.001 376 00

0.002 363 00

0.003 349 00 0.000 986 00

1+1+2+(47*2+2)*10+10*2+2=986

n=5

0.000 886 00

0.001 383 00

0.001 879 00 0.000 496 00

1+1+2+(47*2+2)*5+5*2+2=496

▲得出结论:

延迟时间: Time="1"+1+2+(m*2+2)*n+n*2+2

? 由于MOV P1,A 1'

? 【即: Time=(m*2+2)*n+n*2+5】

▲结论验证:

n=20,则Time=(47*2+2)*20+20*2+6=1966

n=20

0.002 356 00

0.004 323 00

0.006 289 00 0.001 966 00 OK!验证通过

▲双重循环程序C51代码:

void delay(unsigned char m, unsigned char n)

{

?unsigned char tempm,tempn=n;

?do{tempm=m;while(--tempm);}while(--tempn);

}

▲生成的A51代码为:

? ; FUNCTION _delay (BEGIN)

;---- Variable 'n' assigned to Register 'R5' ----

;---- Variable 'm' assigned to Register 'R7' ----

;---- Variable 'tempn' assigned to Register 'R5' ---- 0000 ?C0003:

;---- Variable 'tempm' assigned to Register 'R6' ---- 0000 AE07 MOV R6,0x07 1'

0002 ?C0004:

0002 DEFE DJNZ R6,?C0004 2'

0004 ?C0001:

0004 DDFA DJNZ R5,?C0003 2'

0006 22 RET 2'

? ; FUNCTION _delay (END)

? ; FUNCTION main (BEGIN)

0000 E4 CLR A 1'

0001 F580 MOV P0,A 1'

0003 7D0A MOV R5,#0AH 1'

0005 7F2F MOV R7,#02FH 1'

0007 120000 R LCALL _delay 2'

000A 7580FF MOV P0,#0FFH 2'

000D 22 RET 2'

? ; FUNCTION main (END)

▲测试记录:

m=47,n=10

0.000 389 00

0.001 377 00 0.000 988 00

1+1+1+1+2+(2*47+2)*10+10*2+2=988

▲实验结论:

延迟时间:

?Time=1+1+2+(2*m+2)*n+n*2+2

?即:【Time=(2*m+2)*n+n*2+6】

▲结论验证:

C51代码:

void delay(unsigned char m, unsigned char n) {

?unsigned char tempm,tempn=n;

?do{tempm=m;while(--tempm);}while(--tempn); }

令m=10,n=10

则Time=(20+2)*10+10*2+6=246

m=10,n=10

0.000 389 00

0.000 637 00 0.000 248 00

? 246+1+1=248 OK!测试通过

〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒

★最后,实验总结:

【C51代码:】

void delay(unsigned char m, unsigned char n)

{

?unsigned char tempm,tempn=n;

?do{tempm=m;while(--tempm);}while(--tempn);

}

延迟时间为:【Time=(2*m+2)*n+n*2+6】

〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒

——————————————————————————————————-

〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒

File: 关于while(--n)的优化设计〒

Time: 10:02 〒

Date: 08-7-29 〒

Note: MOV Direct,#Data 为两个机器周期〒

?MOV Rn,Direct 为两个机器周期〒

〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒

◆实验目的:

?用while实现三层循环,以达到更长的延迟时间.

◆测试代码:?

void delay(unsigned char x, unsigned char y, unsigned char z)

{

? unsigned char tempx,tempy,tempz=z;

?

do{tempy=y;do{tempx=x;while(--tempx);}while(--tempy);}while(--tempz);

}

◆汇编代码:

ASSEMBLY LISTING OF GENERATED OBJECT CODE

? ; FUNCTION _delay (BEGIN)

;---- Variable 'x' assigned to Register 'R7' ----

;---- Variable 'z' assigned to Register 'R3' ----

;---- Variable 'y' assigned to Register 'R5' ----

;---- Variable 'tempz' assigned to Register 'R3' ---- 0000 ?C0003:

;---- Variable 'tempy' assigned to Register 'R6' ---- 0000 AE05 MOV R6,AR5(y) 2'

0002 ?C0006:

;---- Variable 'tempx' assigned to Register 'R4' ---- 0002 AC07 MOV R4,AR7(x) 2'

0004 ?C0007:

0004 DCFE DJNZ R4,?C0007 2'

0006 ?C0004:

0006 DEFA DJNZ R6,?C0006 2'

0008 DBF6 DJNZ R3,?C0003 2'

000A 22 RET 2'

? ; FUNCTION _delay (END)

? ; FUNCTION main (BEGIN)

0000 E4 CLR A 1'

0001 F580 MOV P0,A 1'

0003 7B01 MOV R3,#01H 1'

0005 7D47 MOV R5,#047H 1'

0007 7F05 MOV R7,#05H 1'

0009 120000 R LCALL _delay 2'

000C 7580FF MOV P0,#0FFH 2'

000F 22 RET 2'

? ; FUNCTION main (END)

◆实验记录:

x=5,y=71,z=1

0.000 389 00

0.001 396 00 0.001 007 00

◆推论公式:

[(2*x+2)*y+2*y+2]*z+2*z+2+5+2=0.001 007 00

◆结论验证:

x=10,y=60,z=5

0.000 389 00

0.007 618 00 0.007 229 00

[(2*x+2)*y+2*y+2]*z+2*z+2+5+2=0.007 229 00 OK!测试通过

◆实验结论:

延迟时间为:Delay_Time=[(2*x+2)*y+2*y+2]*z+2*z+2+5+2 ? =[(2*x+2)*y+2*y+2]*z+2*z+7

? =2*x*y*z+4*y*z+4*z+7

?即:【Delay_Time=2xyz+4yz+4z+7】

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:感谢大家的阅读,本文由我司收集整编。仅供参阅!

单片机延时

如果用软件延时的话,那么在执行延时程序的时候就不能作其它事了,如LED、按键扫描等。 用中断则可以实现多任务。 所以中断是个很好的资源,要充分利用 秒=1000毫秒(ms) 1毫秒=1/1,000秒(s) 1秒=1,000,000 微秒(μs) 1微秒=1/1,000,000秒(s) 1秒=1,000,000,000 纳秒(ns) 1纳秒=1/1,000,000,000秒(s) 1秒=1,000,000,000,000 皮秒(ps) 1皮秒=1/1,000,000,000,000秒(s) 参考资料:资料 用定时器延时,有时候显得有点麻烦,我们不如考虑软件精确延时,软件延时无非就是利用for或while多重循环。以前用到延时函数时,都是从网上下载别人写好的延时子程序。延时5ms,400ms,1s,……,这些延时函数的函数名中都清清楚楚地标明了延时的时间,可我一直不知道这些函数是如何编写的,确切地说,是如果根据延时时间来确定循环次数的。如果是纳秒级的延时,可以通过示波器来观察波形,或者反汇编一下,计算一下指令执行时间,但如果延时时间相对较长,示波器便无能为力了。这几天好好看了一下Keil调试,发现Keil的功能实在是太强大了。利用Keil uVersion的调试就可以写出精确的软件延时程序。以下是我的简单小结,文中所有程序都是在Xtal=11.0592MHZ下测试。 比如我需要一个400ms的延时,随便写了个两重循环,外层循环5次,内层循环暂且设为5000: void Delay400Ms(void){ uchar i=5; unint j; while(i--){ j=5000; //通过keil调试来确定循环次数 while(j--); } } 在main函数中调用Delay400Ms(): void main() { while(1){ P1=0; Delay400ms(); P1=1; } }

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。可以把这一函数

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

单片机一些常用的延时与中断问题及解决方法 延时与中断出错,是单片机新手在单片机开发应用过程中,经常会遇到的问题,本文汇总整理了包含了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 答: 不可能的,是不是你的编译有错的啊

单片机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--);

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.doczj.com/doc/7e19124367.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语言编程中,经常需要用几个空指令产生短延时的效果。这在汇编语言中很容易实现,写几个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--

单片机延迟函数

单片机延迟函数 /*************************************************************** *************** 12M 延时计算公式= 4.17+(n-1)*0.5 us 8M 延时计算公式= 6.25+(n-1)*0.75 us 7.3728M 延时计算公式= 6.78+(n-1)*0.81 us 或者6.51+(n-1)*0.82 us 4M 延时计算公式= 12.5+(n-1)*1.5 us 3.6864M 延时计算公式= 13.56+(n-1)*1.63 us 2M 延时计算公式= 25.00+(n-1)*3.0 us 1M 延时计算公式= 50.00+(n-1)*6.0 us **************************************************************** ***************/ void delay (unsigned int n) { unsigned int i; i = n; while (i--)

; } } /*************************************************************** *************** 12M 延时计算公式= 4.0+(n-1)*0.5 us 8M 延时计算公式= 6.0+(n-1)*0.75 us 7.3728M 延时计算公式= 6.51+(n-1)*0.81 us 或者6.51+(n-1)*0.82 us 4M 延时计算公式= 12.0+(n-1)*1.5 us 3.6864M 延时计算公式= 13.02+(n-1)*1.63 us 2M 延时计算公式= 24.00+(n-1)*3.0 us 1M 延时计算公式= 48.00+(n-1)*6.0 us **************************************************************** ***************/ void delay (unsigned int n) { unsigned int i; for (i=n;i>0;i--) { ;

单片机几个典型延时函数

软件延时:(asm) 晶振12MHZ,延时1秒 程序如下: DELAY:MOV 72H,#100 LOOP3:MOV 71H,#100 LOOP1:MOV 70H,#47 LOOP0:DJNZ 70H,LOOP0 NOP DJNZ 71H,LOOP1 MOV 70H,#46 LOOP2:DJNZ 70H,LOOP2 NOP DJNZ 72H,LOOP3 MOV 70H,#48 LOOP4:DJNZ 70H,LOOP4 定时器延时: 晶振12MHZ,延时1s,定时器0工作方式为方式1 DELAY1:MOV R7,#0AH ;;晶振12MHZ,延时0.5秒 AJMP DELAY DELAY2:MOV R7,#14H ;;晶振12MHZ,延时1秒DELAY:CLR EX0 MOV TMOD,#01H ;设置定时器的工作方式为方式1 MOV TL0,#0B0H ;给定时器设置计数初始值 MOV TH0,#3CH SETB TR0 ;开启定时器 HERE:JBC TF0,NEXT1 SJMP HERE NEXT1:MOV TL0,#0B0H MOV TH0,#3CH DJNZ R7,HERE CLR TR0 ;定时器要软件清零 SETB EX0 RET

C语言延时程序: 10ms延时子程序(12MHZ)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延时子程序(12MHZ)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--); }

单片机精确毫秒延时函数

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

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

延时与中断出错,是单片机新手在单片机开发应用过程中,经常会遇到的问题,本文汇总整理了包含了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那么大的差距,是你的软件的原因。 不信你实际编写一个秒钟,利用原理计算编写一个烧进单片机和利用软件测试的秒程序烧进单片机,你会发现原理计算的程序是正确的

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

单片机写延时程序的几种方法 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個機器周期,故

AVR单片机常用的延时函数

AVR单片机常用的延时函数 /******************************************************************** *******/ //C header files:Delay function for AVR //MCU:ATmega8 or 16 or 32 //Version: 1.0beta //The author: /******************************************************************** *******/ #include void delay8RC_us(unsigned int time) //8Mhz内部RC震荡延时Xus { do { time--; } while(time>1); } void delay8RC_ms(unsigned int time) //8Mhz内部RC震荡延时Xms { while(time!=0) { delay8RC_us(1000); time--; } } /******************************************************************** **********/ void delay1M_1ms(void) //1Mhz延时1ms { unsigned char a,b,c; for(c=1;c>0;c--) for(b=142;b>0;b--) for(a=2;a>0;a--); } void delay1M_xms(unsigned int x) //1Mhz延时xms { unsigned int i; for(i=0;i

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

上一次课中,我们已经知道,程序中的符号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指令;如果延 时时间长(几个毫秒级别),则可以使用双嵌套循环的 方法来实现。 实验的流程图:

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

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

谈谈单片机里延时子程序

谈谈51单片机里的延时子程序 延时程序在单片机编程中使用非常广泛,但一些读者在学习中不知道延时程序怎么编程,不知道机器 周期和指令周期的区别,不知道延时程序指令的用法, ,本文就此问题从延时程序的基本概念、机器周期和指 令周期的区别和联系、相关指令的用法等用图解法的形式详尽的回答读者 我们知道程序设计是单片机开发最重要的工作,而程序在执行过程中常常需要完成延时的功能。例如 在交通灯的控制程序中,需要控制红灯亮的时间持续30秒,就可以通过延时程序来完成。延时程序是如何 实现的呢?下面让我们先来了解一些相关的概念。 一、机器周期和指令周期 1.机器周期是指单片机完成一个基本操作所花费的时间,一般使用微秒来计量单片机的运行速度, 51 单片机的一个机器周期包括12 个时钟振荡周期,也就是说如果51 单片机采用12MHz 晶振,那么执行 一个机器周期就只需要1μs;如果采用的是6MHz 的晶振,那么执行一个机器周期就需要2μs。 2 .指令周期是指单片机执行一条指令所需要的时间,一般利用单片机的机器周期来计量指令周期。 在51 单片机里有单周期指令(执行这条指令只需一个机器周期),双周期指令(执行这条指令只需要两个 机器周期),四周期指令(执行这条指令需要四个机器周期)。除了乘、除两条指令是四周期指令,其余均 为单周期或双周期指令。也就是说,如果51 单片机采用的是12MHz 晶振,那么它执行一条指令一般只需 1~2 微秒的时间;如果采用的是6MH 晶振,执行一条指令一般就需2~4 微秒的时间。现在的单片机有很多种型号,但在每个型号的单片机器件手册中都会详细说明执行各种指令所需的机 器周期,了解以上概念后,那么可以依据单片机器件手册中的指令执行周期和单片机所用晶振频率来完成 需要精确延时时间的延时程序。 二、延时指令 在单片机编程里面并没有真正的延时指令,从上面的概念中我们知道单片机每执行一条指令都需要一 定的时间,所以要达到延时的效果,只须让单片机不断地执行没有具体实际意义的指令,从而达到了延时 的效果。 1.数据传送指令MOV 数据传送指令功能是将数据从一个地方复制、拷贝到另一个地方。 如:MOV R7,#80H ;将数据80H 送到寄存器R7,这时寄存器R7 里面存放着

51单片机精确延时程序大集合

51单片机精确延时程序大集合 以下程序说是精确延时,实际上都不对。调用一次差个几微秒、几百微秒,一天下来差好几分钟。加我的QQ群有精确版本哦,不同频率的晶振都适用。群:38397759 2008-04-24 12:10:26, 在论坛上看到不少不错的延时程序,整理如下共同分享: 精确延时计算公式: 延时时间=[(2*第一层循环+3)*第二层循环+3]*第三层循环+5 ;延时5秒左右 DELAY5S:PUSH 04H;2个机器周期 PUSH 05H;2个机器周期 PUSH 06H;2个机器周期 MOV R4,#50;1个机器周期 DELAY5S_0:MOV R5,#200;1个机器周期 DELAY5S_1:MOV R6,#245;1个机器周期 DJNZ R6,$;2×245=490个机器周期 DJNZ R5,DELAY5S_1;这条2个机器周期,这层循环包含R5×(490+1) +2×R5=98600个机器周期 DJNZ R4,DELAY5S_0;这条2个机器周期,这层循环包含R4×(98600+1) +2×R4=4930150个机器周期 POP 06H;2个机器周期 POP 05H;2个机器周期 POP 04H;2个机器周期 RET;2个机器周期 ;(共2+2+2+1+4930150+2+2+2+2=4930165个机器周期) ;513微秒延时程序 DELAY: MOV R2,#0FEH;1个机器周期 JUZINAIYOU: DJNZ R2,JUZINAIYOU;2×R21即2×245 RET;2个机器周期 ;(实际上是493个机器周期)

;10毫秒延时程序 DL10MS: MOV R3,#14H DL10MS1:LCALL DELAY DJNZ R3,DL10MS1 RET ;(缺DELAY) ;0.1s延时程序12MHz DELAY: MOV R6,#250 DL1: MOV R7,#200 DL2: DJNZ R6,DL2 DJNZ R7,DL1 RET ;延时1046549微秒(12MHz) ;具体的计算公式是: ;((((r7*2+1)+2)*r6+1)+2)*r5+1+4 = ((r7*2+3)*r6+3)*r5+5 DEL : MOV R5,#08H DEL1: MOV R6,#0FFH DEL2: MOV R7,#0FFH DJNZ R7,$ DJNZ R6,DEL2 DJNZ R5,DEL1 RET ;1秒延时子程序是以12MHz晶振 DELAY:MOV R1,#50 del0: mov r2,#91 del1: mov r3,#100 djnz r3,$ djnz r2,del1 djnz r1,del0 Ret ;1秒延时子程序是以12MHz晶振为例算指令周期耗时 KK: MOV R5,#10 ;1指令周期×1 K1: MOV R6,#0FFH ;1指令周期×10

单片机延时程序分析

任何单片机在工作之前都要有个复位的过程,复位是什么意思呢?它就象是我们上课之前打的预备铃。预备铃一响,大家就自动地从操场、其它地方进入教室了,在这一段时间里,是没有老师干预的,对单片机来说,是程序还没有开始执行,是在做准备工作。显然,准备工作不需要太长的时间,复位只需要5ms的时间就能了。如何进行复位呢?只要在单片机的RST管脚上加上高电平,就能了,按上面所说,时间不少于5ms。为了达到这个要求,能用很多种办法,这里供给一种供参考,见图1。实际上,我们在上一次实验的图中已见到过了。 这种复位电路的工作原理是:通电时,电容两端相当于是短路,于是RST管脚上为高电平,然后电源通过电阻对电容充电,RST端电压慢慢下降,降到一定程序,即为低电平,单片机开始正常工作。 摘要实际的单片机应用系统开发过程中,由于程序功能的需要,经常编写各种延时程序,延时时间从数微秒到数秒不等,对于许多C51 开发者特别是初学者编制非常精确的延时程序有一定难度。本文从实际应用出发,讨论几种实用的编制精确延时程序和计算程序执行时间 的方法,并给出各种方法使用的详细步骤,以便读者能够很好地掌握理解。关键词 Keil 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的延时函数可编写如下: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。可以把这 一函数当作基本延时函数,在其他函数中调用,即嵌套调用\[4\],以实现较长时间的延时;但需要注意,如在Delay40us( )中直接调用4次Delay10us( )函数,得到的延时时间将是42 μs,而不是40 μs。这是因为执行Delay40us( )时,先执行了一次LCALL指令(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为低电平。在主程序中循环调用该

相关主题
文本预览
相关文档 最新文档