51单片机精确延时设计

2019-04-15 12:17发布

在我们使用单片机的时候,很多情况下需要用到精确的延时。比如在跟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_(); } }