51单片机精确延时设计

2020-08-23 17:34发布

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

   111

 首先我们构建一个延时函数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添加到虚拟逻辑分析仪中,通过观察高低电平翻转的时间来检验延时的正确性。  

333

可以看到时间差非常接近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_();}; 仿真结果如下,可以看到实验结果是符合我们的预期的。

   444555

 附完整程序: 

#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_(); 
} 
}