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并不停止计数。虽然中断响应有可能被延 迟,但是延迟的时间仍然被计算。那么我们就完全有可能将下一次中断“补上”。
 
ExpandedBlockStart.gif 测试代码
 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行代码。哟,要计算执行周期不得累死。这个用法的好处显而易见了吧。

 

转载于:https://www.cnblogs.com/xiaoxia/archive/2011/08/24/2151460.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值