51单片机精确延时设计

2020-12-11 18:36发布

在我们使用单片机的时候,很多情况下需要用到精确的延时。比如在跟DS18B20进行通讯的时候需要遵循严格的时序,这就需要我们严格把控程序执行的时间。 一般我们都是通过执行空语句的方式来使程序延时。这种方法是用循环嵌套的方式使程序执行空操作,达到延时的目的;除此之外还有使用中断的方式等。 我们可以通过debug来验证我们的函数延时是否准确。本次实验采用12M的晶振进行仿真模拟,应当对软件进行一些设置。在option中将晶振频率改为12M。用12M的晶振是因为这时候一条指令周期恰好是1us,方便我们计算。

首先我们构建一个延时函数void delay_10us( unsigned char tick ),即每次延时的最小单位是10us,通过控制tick的取值来改变延时的长度。在51单片机中有一个内置的指令_nop_( ),其执行一次的时间恰好是一个指令周期。这里我们使用的晶振是12M,那么执行一次_nop_( )就是1us。 在函数内部,通过tick的值来控制循环执行_nop_( )的次数。即:

void delay_10us(unsigned char tick)
 
{
 
    for(; tick>1; tick--)
 
    {
 
        _nop_();
 
        _nop_();
 
    }
 
}

这里本来应该是10个_nop_( ),但是却只写了两个。这是因为在实际调用的时候,进入函数、返回、循环跳转等都是会耗费时间的。这里具体写几个_nop_( )可以通过实际的调试来得到。 在主循环中构建下面的代码: 

void main()
 
{
 
      while(1)
 
      {
 
        P1_0 = 1;
 
              delay_10us(10);
 
        P1_0 = 0;
 
              delay_10us(10);
 
      }
 
}

进入debug模式,然后将P1_0添加到虚拟逻辑分析仪中,通过观察高低电平翻转的时间来检验延时的正确性。  

可以看到时间差非常接近100us,除去执行P1_0 = 1耗费的时间基本满足我们的需求。我们可以通过改变不同的延时值来验证我们的函数是否正确。这里不再赘述。   现在我们利用刚才构建的延时函数来让单片机的引脚输出2KHz的占空比20%的方波;占空比为2KHZ,则周期为500us,简单计算得到方波需要保持100us高电平400us低电平。 需要注意,由于我们的10us延时是存在一些微小的偏差的,如果需要延时的tick数比较大,那么误差将会被放大,导致时间偏差较大。为了解决这个问题,我们可以少写一个tick,剩余的时间再额外用_nop_()或其它方式补上。具体需要补多少根据实际调试的结果去加,这里肯定不能一次性就做到合适,需要多次尝试。


void main()
{
	while(1)
	{
        P1_0 = 1;
		 delay_10us(9);
        delayNOP();
        _nop_();
        _nop_();
        P1_0 = 0;
		 delay_10us(39);	
        delayNOP();
	}
}

这里的delayNOP()是在前面define的

 #define delayNOP(); {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}; 

仿真结果如下,可以看到实验结果是符合我们的预期的。

 

附完整程序:


#include "reg52.h"
#include "intrins.h"
 
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();};
 
sbit P1_0 = P1^0;
 
void delay_10us(unsigned char tick);
void main()
{
	while(1)
	{
        P1_0 = 1;
		 delay_10us(9);
        delayNOP();
        _nop_();
        _nop_();
        P1_0 = 0;
		 delay_10us(39);	
        delayNOP();
	}
}
 
void delay_10us(unsigned char tick)
{
    for(; tick>1; tick--)
    {
        _nop_();
        _nop_();
    }
}