zoukankan      html  css  js  c++  java
  • 51单片机(STC89C52RC) keil软件精确定时 浅析

      这里主要是对使用keil环境下,提高51单片机软件精度的问题给出自己的一点小看法,参阅了文章利用 Keil Cx51实现T0的精确定时,使用文章中的方法的确是可以提高软件精度,可是一碰到中断函数中语句较多,且main函数其它任务的时候,总是觉得力不从心,因为要计算中断执行时间就够我受的了。我可是很懒的,研究之下发现了一些东西。

    51误差主要是来自两个方面:晶振和单片机中断系统的误差
    1. 晶振:我们的晶振一般误差都是20PPM的,百万分之二十。想提高精度,只能选择误差更小的晶振,但它毕竟不是为精确定时设计的,很难达到时钟芯片晶振的精度。
    2. 中断系统的误差:定时器产生中断请求以后,并不一定能马上响应这个中断。单片机至少要把当前的指令执行完。51的指令是1到4个周期。如果赶上两周期指令,就会延误一个指令周期。最慢的情况会延误3个周期响应中断。这里是没有办法预测的,因为中断是随机的。如果使用上述文章中的方法,这里就比较难计算了。如果单片机正处理其他的中断(同级或更高级)。要等其执行完其他中断,再执行一条主程序指令,才会响应定时器0中断。因为程序千差万别,所以其他中断占用的时间,就没准儿了。这类影响是随机的,一般会提升相应的优先级别。那么如何解决呢?我们知道定时器只要开着,TH0和 TL0就会不断的增一,增到FF FF,再增一就溢出(不自动重载),这时TF0被硬件置1(也就是中断请求)。我们要注意的就是不管定时器中断是否被响应,TH0和 TL0仍然会不断增一,FF FF增一00 00 再增一 00 01 再增一 00 02 。定时器在溢出产生中断以后,不论响应还是不响应,TL0并不停止计数。虽然中断响应有可能被延 迟,但是延迟的时间仍然被计算。那么我们就完全有可能将下一次中断“补上”。
     
    测试代码
     1 #include "reg51.h"
     2 #include "delay.h"
     3 #include "stdlib.h"
     4 #define uchar unsigned char
     5 uchar  MScond=0
     6 uchar  Seond=0;  
     7 uchar  Minute=0;  
     8 uchar  Hour=0
     9 sbit P1_0 = P1^0;
    10 
    11 bit is_arrive_time(void)
    12 {
    13 
    14      int a = rand()%10;
    15      if(a>5)
    16      {
    17          return 1;
    18      }
    19      return 0;
    20 }
    21 void main(void){
    22 EA=1
    23 ET0=1;
    24 TMOD &= 0xf0 ;
    25 TMOD |= 0x01 ;  
    26 TH0=0xb1;
    27 TL0=0xdf;
    28 TR0=1;
    29 P1_0 = 0;
    30 while(1)
    31 {
    32     if( is_arrive_time() == 1)
    33       {
    34           P1_0 = ~P1_0;
    35       }
    36 }
    37 }
    38 void Time0Isr(void) interrupt 1 
    39 {
    40        TH0  =  0xb1 ;            //定时器重新赋初值
    41    // TL0  =  0xeb;
    42    TL0 +=  0xe1;           //测试点
    43 MScond=MScond+1;
    44 if(MScond==50)
    45 {
    46 MScond=0;
    47 Seond=Seond+1 ;
    48     if(Seond==60)
    49     {Seond=0;
    50         Minute=Minute+1;
    51         if(Minute==60)
    52         {
    53             Minute=0;
    54             Hour=Hour+1
    55             if(Hour==24)
    56             {Hour=0;
    57             }
    58         }
    59     }
    60 }
    61 

     我们可以看到我将TL0的赋值累加了,其结果将等待和初始化的时间也给算上了,解决了上面预测计算的问题,那么为什么负的初值不是0xdf而是0xe1呢?我们查看一下这个语句的汇编就知道了 TL0 +=  0xe1

    1 C:0x0179    74E1     MOV      A,#0xE1
    2 C:0x017B    258A     ADD      A,TL0(0x8A)
    3 C:0x017D    F58A     MOV      TL0(0x8A),A

     可以看出,TL0(0x8A)的值在C:0x017B 这里就记录到了A寄存器当中去了。也就是C:0x017B语句本身和C:0x017D两条语句没有记录进去,这两条都是一个周期的指令,故要加上2。这条语句后面的代码执行也就算下一次中断执行的了。从理论上说,真正是一个微秒都不差。中断中代码的执行时间可以扩展到中断周期那么大,比如我这里是50ms,12MHZ的话就是约50000行代码。哟,要计算执行周期不得累死。这个用法的好处显而易见了吧。

    作者:xiaoxia

    出处:http://cnblogs.com/xiaoxia

    本文遵从GNU 的自由文档许可证(Free Document License)的条款,欢迎转载、修改、散布。 

  • 相关阅读:
    MySQL查看表占用空间大小(转)
    Nginx搭建flv视频点播服务器
    Nginx SPDY Pagespeed模块编译——加速网站载入
    RAD,V模型
    java运算符的优先级
    union和union all的区别
    java数据的5种存储位置(转)
    java对象的初始化过程和创建对象的几种方式
    java异常的原理以及应用
    缓冲与缓存的概念(转)
  • 原文地址:https://www.cnblogs.com/xiaoxia/p/2151460.html
Copyright © 2011-2022 走看看