标题: 编写延时函数的简单方法 [打印本页]

作者: admin    时间: 2010-6-21 00:26
标题: 编写延时函数的简单方法

   在本站51hei-5板子上做315兆无线解码和红外解码试验的时候,延时函数的精度很重要,要做到相当精确才可以成功,所以大家一定要掌握.

 

  这也是大家最常在QQ里问我的一个问题,如果从keil里看了c语言的反汇编代码然后根据晶振和指令计算延时的时间这样虽然非常的准确但是相当的麻烦而且容易搞错,我这里介绍一个最简单的方法.可以验证你的延时函数

 

   这里用一个例程详细介绍一下。
    过程参考如下:
    在编译器下建立一个新项目,也可以利用已有项目。此过程中需要注意,单片机晶振的选择,因为for循环里指令的执行时间和晶振有直接关系,本例中晶振使用11.0592M。



 
    编写一段关于延时的函数,主要利用for循环,代码如下:
void delay_ms(unsigned int ms)
{
unsigned int i;
unsigned char j;
    for(i=0;i<ms;i++)
    {
        for(j=0;j<200;j++);
        for(j=0;j<102;j++);
    }

}
其中ms是输入参数,如果输入1,就是要求程序延时1ms。
j变量是调整程序运行的时间参数。调整j的数值,使1次循环的时间在1ms。
    将此程序编译通过,然后利用软件仿真,调整时间。

 

下面这个sec就是程序运行到现在的这一行所用的时间。


 

  两次时间差就是延时函数使用的时间,如果与1ms相差比较多,用户可以调整j参数的值,使延时时间尽量接近1ms。如增大j的值for(j=0;j<105;j++);
    此方法得出延时函数,在晶振不同的情况下,延时时间会不准。软件调试结果,这个程序的延时时间为:1.01779ms,一般的单片机系统中都可以应用。

 

下面来说说汇编的传统计算方法:

 

指令周期、机器周期与时钟周期
       指令周期:CPU执行一条指令所需要的时间称为指令周期,它是以机器周期为单位的,指令不同,所需的机器周期也不同。
       时钟周期:也称为振荡周期,一个时钟周期 =晶振的倒数。
       MCS-51单片机的一个机器周期=6个状态周期=12个时钟周期。
       MCS-单片机的指令有单字节、双字节和三字节的,它们的指令周期不尽相同,一个单周期指令包含一个机器周期,即12个时钟周期,所以一条单周期指令被执行所占时间为12*(1/12000000)=1us。

了解了上面这些我们来看一个例子

;============延时1秒子程序========================
DELAY_1S:         ;延时子程序,12M晶振延时1.002035秒
MOV R4,#10
L3: MOV R2 ,#200  ;1指令周期
L1: MOV R3 ,#249  ;1指令周期
L2: DJNZ R3 ,L2     ;2指令周期
DJNZ R2 ,L1          ;2指令周期
DJNZ R4 ,L3          ;2指令周期
RET                      ;2指令周期
;循环体延时时间: [(249*2+1+2)*200+1+2]*10*12/12000000=1.002030s
;加上ACALL指令和第一条mov指令以及最后一条RET指令算出来整个函数的时间为1.002035s
;===================================================
通常选用的是11.0592MHZ的晶振:
[(249*2+1+2)*200+1+2]*10*12/11059200=1.08727213541666666...S

汇编延时子程序的延时计算问题
对于程序
DELAY:  MOV R0,#00H
DELAY1: MOV R1,#0B3H
DJNZ R1,$
DJNZ R0,DELAY1

  查指令表可知 MOV一个机器周期,DJNZ 指令需用两个机器周期,而一个机器周期时间长度为12/11.0592MHz,所以该段程序执行时间为:

((0B3×2+1+2)×256+1)×12÷11059200=100.2789mS

  第一层:DJNZ R1,$:执行了B3H次,一次两个周期,所以为0B3×2;

  第二层:MOV R1,#0B3H为一个周期,DJNZ R0,DELAY1为两个周期,这样循环一次就是0B3×2+1+2个周期;第二层的执行次数本来是255次,但因为赋首值为0,而DJNZ是先减1,再比较的,所以就应该是256次。

  这样的话,整个循环执行完应该是(0B3×2+1+2)×256+1次。再加上开始赋值这一句,就是((0B3×2+1+2)×256+1)了

 

 

还说明一下:

nop指令或者_nop_(); 函数占一个机器周期,
   在stc单片机的12T模式下一个机器周期是一个振荡周期的12分频,如果你的晶振是12MHZ,那你的一个机器周期就是1微秒.一个nop指令的执行时间也就是1US

   当在6T模式(下载的时候可选择模式)下12M晶振的时候,一个nop就是0.5US了.

[此贴子已经被作者于2010-7-4 2:51:54编辑过]

作者: brantyou    时间: 2010-6-21 14:34

支持。。。。。


作者: sanchun    时间: 2010-6-21 15:23

admin 斑竹 不愧为强人一个,受教了


作者: mainv    时间: 2010-6-22 22:58

要求精确的话用汇编


作者: RF射频电路    时间: 2010-6-23 12:31
嗯,确实要延时就是循环做空操作。具体循环次数要在实践中修正,不同器件的振荡周期可能不同
作者: chenwu343689806    时间: 2010-6-27 13:37

是可以 比较直观

 


作者: 亮    时间: 2010-6-28 23:51

学习了。。。。。。。。。。。。。。


作者: 示波器    时间: 2010-7-14 10:14
空操作是延时常采用的手段
作者: 木寸子    时间: 2010-8-29 10:09
讨教了
作者: 烟草者    时间: 2010-9-23 11:02
才学多指教
作者: crtnawwan9623    时间: 2010-10-16 18:59
在图书馆一边打瞌睡一边把您的帖子看完了,真是不错,让我又明白了很多,我QQ是451647703,能加我吗,还有问题讨教
作者: lushanxi    时间: 2010-10-25 10:11
不错哦,值得借鉴
作者: 优游鸢123    时间: 2010-10-27 09:43
现在有单片机小精灵了,可以直接获取延时程序
作者: lushanxi    时间: 2010-10-27 20:05

哎 我学得是 一塌糊涂啊

学着学着都不知道从何学起了


作者: motey    时间: 2010-11-6 23:24

用C编程的方法解释不够祥细


作者: 白开水    时间: 2010-11-8 14:08

延时的初值可以借助软件工具那样比较快


作者: cliu1    时间: 2010-11-16 13:32
学习!
作者: 2SK134    时间: 2010-12-12 20:44

好!有用,实用!


作者: dongmaowan    时间: 2010-12-13 21:15
可以用定时器来做延时!
作者: hbyiwen    时间: 2011-1-11 11:48

延时时间精确计算哦 ,看看。。。。


作者: 麦子的梦想    时间: 2011-1-13 11:31
牛人就是牛!!
作者: zh3188    时间: 2011-1-20 12:37
小弟刚来,学习了
作者: li332015563    时间: 2011-1-27 22:15
绝对支持
作者: hanjunjie518    时间: 2011-2-24 13:29

学习学习,谢谢楼主!


作者: 梦幻科学    时间: 2011-3-6 02:33

其实用汇编写也蛮简单的   1S的延时: 

   DELAY:MOV R4,,#50

        D1:MOV R5,,#50

        D2:MOV R6,,#50

              DJNZ R6,,$

              DJNZ R5,D2

              DJNZ R4,D1

               RET


作者: 电子猫    时间: 2011-3-6 15:53

楼主这个方法真是太好了,这样的话即使是c语言延时也能精确到us级别了

 


作者: 麦子的梦想    时间: 2011-3-10 09:05
就是来学习的,呵呵
作者: 亚伦    时间: 2011-3-25 10:51

版主啊,这个程序中,j的循环次数是(200+102)*ms 还是200*102*ms啊 如果是第一种的话,为什么不直接写成j=302 呢 然后最里面的括号直接一个循环语句呢?求回答~


作者: 转转    时间: 2011-6-8 17:29
恩,老师教的也是这样
作者: lzai    时间: 2011-6-10 14:50

谢谢,很不错的,很经典


作者: solo中的陆逊    时间: 2011-8-15 14:51
其实用定时器更准啊
作者: 周义超    时间: 2011-9-11 09:36
仁兄,请问红色标志的那些字怎么弄上去的,讨教了
作者: zxw    时间: 2011-9-17 16:41

学习了,谢谢


作者: zxw    时间: 2011-9-21 07:20

学习了,谢谢


作者: 宁娟    时间: 2011-10-22 19:24
非常有用哈。学习了。
作者: LINGON    时间: 2011-11-10 16:50
温习了一遍  汇编精准定时
作者: ahshmj    时间: 2012-1-27 13:21

其实要想真正的准确,还要加上调用和结束语句的耗时。

要求严格时,还要禁止其他中断,否则一旦被中断,延时时间准确就无从谈起了。


作者: niejieli    时间: 2012-2-6 22:50
正学习中,刚好有这种疑惑,受教了!
作者: dongmaowan    时间: 2012-2-11 14:59
使用前后台系统,可以使用定时器精确延时!
作者: letternew    时间: 2012-2-13 19:35

好东西!!


作者: saiyi2373826912    时间: 2012-2-27 10:37
daydayup
作者: dba119    时间: 2012-2-28 16:21
 支持。。。。。。。
作者: whkltdd    时间: 2012-2-28 19:04
这个支持了 以后慢慢学 呵呵
作者: 神龛之首    时间: 2012-3-4 10:55
学习了
作者: goo12369    时间: 2012-3-8 09:49
谢谢楼主 学习了
作者: wu36369    时间: 2012-3-17 19:04

#include<reg52.h>
//#include<math.h>
#define uint unsigned int
#define uchar unsigned char
sbit DUAN=P2^6;
sbit WEI=P2^7;
uchar i,tt;
uchar code Temp[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
/*void delay(uint z)
{
 uint x,y;
 for(x=z;x>0;x--)
  for(y=110;y>0;y--);
}*/
void main()
{
 i=0;
 tt=0;
 TMOD=0x01;//设置定时器0为工作方式1
 TH0=(65536-50000)/256;//高8位
 TL0=(65536-50000)%256;//低8位
 EA=1;//开总中断
 ET0=1;//开定时器0中断
 TR0=1;//起动定时器0
 WEI=1;
 P0=0;
 WEI=0;
 DUAN=1;
 P0=0x3f;
 DUAN=0;
 while(1)
 {
  tt=0;
  i++; 
  if(tt==20)
  { 
   if(i=16)
   i=0;
   DUAN=1;
   P0=Temp;
   DUAN=0;
   //delay(1000);
  }
 }

}
void exter0() interrupt 1
{
 TH0=(65536-50000)/256;
 TL0=(65536-50000)%256;
 tt++;
}


 

大师们帮忙看一下,我这个让数码管隔一秒钟显示一个数的程序那点出了错。

输出HEX文件时提示Program Size: data=11.0 xdata=0 code=106
creating hex file from "wu13-1"...
"wu13-1" - 0 Error(s), 1 Warning(s).


作者: wuleiwen    时间: 2012-3-21 20:00
定 啊 
作者: windwithgone    时间: 2012-3-26 21:18
支持支持,就是不知道原理,不知谁能讲讲?
作者: wuliurui_123    时间: 2012-3-27 11:12

YS:    NOP                        ;延时 1 ~ 65535 毫秒的延时子程序(晶振为6MHz)

YS1:  MOV A, #163           ; 调用前须将需要延时的毫秒数放入 DPTR 中
YS2:  DEC A                     ;绝对精准
        JNZ YS2                   ;
        MOV A, DPL              ;

        CLR C                      ;

        SUBB A, #1              ;

        MOV DPL, A              ;

        MOV A, DPH              ;

        SUBB A, #0              ;

        MOV DPH, A              ;

        ORL A, DPL               ;

        JNZ YS1                   ;

        RET                      ;


作者: wuliurui_123    时间: 2012-3-27 11:22

LZ 写的很细很到位.

 


作者: qiaoranshanren    时间: 2012-3-28 22:53
正在学习......
作者: 天昊奇缘    时间: 2012-3-31 17:08
为保准确性,还是用中断好些!
作者: 河山    时间: 2012-4-1 16:28
感谢楼主
作者: vlp30028    时间: 2012-4-5 16:31
支持支持
作者: qq945584280    时间: 2012-4-9 17:26
模糊 
作者: 星空爱情    时间: 2012-4-10 12:59
学习受教了 ,真的是醍醐灌顶啊
作者: zza00000    时间: 2012-4-14 18:10
楼主能给分动态数码管0-99的动态循环显示c语言码吗?? 感谢了~~~
作者: feixiang_07    时间: 2012-4-18 08:38
精确延时在汇编中很容易实现,而在C语言中怎么这么难呢。
作者: 星语心愿    时间: 2012-4-22 16:30

受教了!谢谢!

 


作者: code_inconnu    时间: 2012-5-8 23:37
正在学习,谢谢了
作者: lap2008    时间: 2012-5-11 17:55
这里牛人不手。学习了
作者: 月夜CX    时间: 2012-5-12 13:36

我觉得用计时器延迟也不错啊

是不是因为计时器比较占用资源?


作者: 秋海    时间: 2012-5-13 10:17
不错
作者: gzf2009fpf    时间: 2012-5-16 16:47

如果我想做一个报警器不知道晶振频率该怎么算?


作者: ruson    时间: 2012-5-18 17:28
学习了 。。。。。
作者: zhb1510172127    时间: 2012-5-19 11:18
值得学习
作者: 潮州GG    时间: 2012-5-31 10:37
研究中。。。。。。。
作者: kingzhiyun    时间: 2012-6-1 16:51
不错,但是C语言里面的延时是怎么算到1MS的呢?有点不明白
作者: chendonghao55    时间: 2012-6-2 02:06
for(i=20;i>0;i--) for(j=248;j>0;j--); 是10ms,这个也挺好用的;
作者: yeqirang    时间: 2012-6-7 17:41

很好,受教了


作者: 单片机执着者    时间: 2012-7-11 21:37
想请教各位一个问题,在文中仿真过程中,能够随时把时间清零吗?如果可以的话仿真就不用计算什么时间差了,运行那个函数或语句用了多少时间就一目了然,还可以减少人为计算时间差时出错。
作者: 新城风雨    时间: 2012-7-21 16:19
呵呵,受教了哦
作者: 池中麟    时间: 2012-8-5 12:19
不错!!!!!!!!!!!!!!!!!!!!!!!!!
作者: 汪建平    时间: 2012-8-8 17:26
楼主很强大!!
作者: yaake    时间: 2012-9-12 14:08
汇编语言实现起来应该更有优势。
作者: 晨风    时间: 2012-9-25 21:56
汇编不好记
作者: tzzhang2018    时间: 2012-10-2 11:12
不错哦,值得借鉴

作者: Lucifer    时间: 2012-10-7 17:20
学习学习,谢谢楼主~~
作者: franklee    时间: 2012-10-31 19:38

好得很,谢谢了。


作者: 独自等待    时间: 2012-11-4 21:20
学习了,汇编有待于继续!
作者: goldlg    时间: 2012-11-6 15:29

好东西,一直在考虑这个问题,今天解决了啊!呵呵~


作者: 雨台枫叶    时间: 2012-11-30 17:24


作者: hodenshi    时间: 2012-12-14 14:25
看上去好难啊

作者: ligun    时间: 2012-12-19 13:13
支持
作者: 沙漠海滩    时间: 2012-12-19 21:41
支持一下
作者: hodenshi    时间: 2012-12-21 14:40
 受教了,谢谢
作者: cgc503    时间: 2013-1-16 10:30

支持,支持啊


作者: cgc503    时间: 2013-1-16 10:31
牛逼的方法
作者: cgc503    时间: 2013-1-16 10:31
给楼主牛逼了
作者: BG4GKL    时间: 2013-3-10 07:12
看看,好,还有更准的吗?
作者: a3520815    时间: 2013-3-24 17:28
愧为强人一个,受教
作者: 八戒总调皮    时间: 2013-4-15 09:52
我c语言学的不是很好
作者: wgl2013    时间: 2013-4-23 18:11
写的不错
作者: zaizhanzhengtu    时间: 2013-5-1 18:00

楼主 我想问个问题   为什么把a<=200就不行呢


作者: 我为机狂    时间: 2013-5-13 13:04
支持一下...
作者: zuixin2005    时间: 2013-5-20 10:57
好东西,有单片机QQ群嘛,我想加
作者: HectorJohn    时间: 2013-5-28 22:18
必须赞一个!!!!
作者: HectorJohn    时间: 2013-6-2 12:29
必须赞
作者: 魔術師の學徒    时间: 2013-6-20 07:56
多谢楼主分享~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!!!!!!!很好很强大
作者: 芯华科技    时间: 2013-6-24 17:37
的确,很多时候,精确延时很重要的,谢谢分享,学习了




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1