单片机延时函数简单分析

初识延时程序

1
2
3
4
5
6
void delay(unsigned int xms)
{
unsigned int i,j;
for(i=xms;i>0;i--)
for(j=112;j>0;j--);
}
  • 使用此函数可以达到我们想要的延时,使二极管发生闪烁。这也是我刚学单片机的小白,遇到的第一个问题。为什么要用延时,难道仅仅是为了闪烁,它还有啥作用;当我们调用delay(500)时,它到底延时了多长时间。。。。
  • 由于是自学所以只能靠自己了,通过查阅参考书和在网上搜索,自己也算理解一点。现在就将自己理解的记录一下,就当做笔记了

术语解释

  1. 振荡周期:也称时钟周期,是指为单片机提供时钟脉冲信号的振荡源的周期,两个相邻同方向峰值之间的时间,一个时钟周期 =晶振的倒数。。

  2. 状态周期:每个状态周期为时钟周期的2倍,是振荡周期经二分频后得到的。

  3. 机器周期:指单片机完成一个基本操作所花费的时间,一般使用微秒来计量单片机的运行速度,12Mhz的频率,其周期为1/12乘10的-6方s,那么1个机器周期就是它的12倍,即1us。51 单片机的一个机器周期包括12个时钟振荡周期,也就是说如果51 单片机采用12MHz 晶振,那么执行一个机器周期就只需要1μs;如果采用的是6MHz 的晶振,那么执行一个机器周期就需要2 μs。

  4. 指令周期:指单片机执行一条指令所需要的时间,一般利用单片机的机器周期来计量指令周期。在51 单片机里有单周期指令(执行这条指令只需一个机器周期),双周期指令(执行这条指令只需要两个机器周期),四周期指令(执行这条指令需要四个机器周期)。除了乘、除两条指令是四周期指令,其余均为单周期或双周期指令。也就是说,如果51 单片机采用的是12MHz 晶振,那么它执行一条指令一般只需
    12 微秒的时间;如果采用的是6MH 晶振,执行一条指令一般就需24 微秒的时间。

  • MCS-51单片机的一个机器周期=6个状态周期=12个时钟周期

为什么要用延时

  1. 时序是描述对象之间发送消息的时间顺序显示多个对象之间的动态协作,这样就需要等待,等待就可以通过延时子程序实现。
  2. 为了单纯的等待,比如交通信号灯的控制,红灯绿灯黄灯都有时间控制,亮的长短就要由延时程序控制,或者CPU速度快于外设速度时,就需要用延时程序做等待!
  3. 为了某些时序逻辑器件的时序要求。

延时的具体实现方法

  • 在单片机编程里面并没有真正的延时指令,从上面的概念中我们知道单片机每执行一条指令都需要一定的时间,所以要达到延时的效果,只须让单片机不断地执行没有具体实际意义的指令,从而达到了延时。

汇编语言中基本的延时语句

  1. 数据传送指令 MOV
    数据传送指令功能是将数据从一个地方复制、拷贝到另一个地方。

     MOV R7,#80H   ;将数据80H
    

    送到寄存器R7,这时寄存器R7里面存放着80H,就单这条指令而言并没有任何实际意义,而执行该指令则需要一个机器周期。

  2. 空操作指令 NOP
    空操作指令功能只是让单片机执行没有意义的操作,消耗一个机器周期。

  3. 循环转移指令 DJNZ
    循环转移指令功能是将第一个数进行减1 并判断是否为0,不为0 则转移到指定地点;为0 则往下执行。

     DJNZ R7,KK ;
    

    将寄存器R7 的内容减1 并判断寄存器R7 里的内容减完1 后是否为0
    如果不为0 则转移到地址标号为KK 的地方;
    如果为0 则执行下一条指令。这条指令需要2个机器周期。
    利用以上三条指令的组合就可以比较精确地编写出所需要的延时程序。

C51下的延时语句

  1. 使用空的for循环,可以多层嵌套,比如上面给出的例子
  2. 使用while()语句
  • 注意:在C51中定义循环变量时,尽量采用无符号整型;for,while循环变量体变量采用减减方法。

延时程序时间的计算

汇编指令的时间计算

1
2
3
4
	MOV R6,   #20
D1:MOV R7, #248
DNJZ R7,$ //循环248次,共用248X2us,
DNJZ R6,D1
  • 我觉得应该从最后往前算,在MOR R7,#248执行一次前提下,用时1X2us,然后循环减248,用时248X2us,故共用时[2+2X248]us。
    然而又由于DNJZ R6,D1,上面的过程又循环20次,故上面的循环最终用时20X[2+2X248]us。
    接着注意到单纯DNJZ R6,D1会用时20X2=40us
    最后注意过执行唯一一次的 MOV R6,#20会用时1X2us。

C51延时函数的计算

  • 500ms延时子程序程序:.(晶振12MHz,一个机器周期1us.一条指令周期2us)
1
2
3
4
5
6
7
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--);
}
  • 计算分析:程序共有三层循环
  1. 一层循环n:R52 = 812 = 162us DJNZ 2us
  2. 二层循环m:R6*(n+3) = 202*165 = 33330us DJNZ 2us + R5赋值 1us = 3us
  3. 三层循环: R7*(m+3) = 15*33333 = 499995us DJNZ 2us + R6赋值 1us = 3us
  • 循环外: 5us [子程序调用 2us + 子程序返回 2us + R7赋值 1us = 5us ]

  • 延时总时间 = 三层循环 + 循环外 = 499995+5 = 500 000us =500ms

  • 所以开头给出来得延时函数delay(500)时间,循环外+ 两层循环 = 5+1122500us = 56005us = 56.005ms =0.056005s

  • 1s延时子程序 :

1
2
3
4
5
6
7
8
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--);
}
  • 时间计算

time = 221411645 + 5 = 992 965us = 0.992965s ≈ 1s

51单片机数据类型大小测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<reg52.h>

sbit led1=P1^0;
sbit led2=P1^1;
sbit led4=P1^3;

void main()
{
int char_size, int_size;

char_size = sizeof(char);
int_size = sizeof(int);

if(char_size==1) //测试char占一个字节
led1 = 0;
if(int_size==2) //测试int 占两个字节
led2 = 0;
while(1);
}