[讨论] 这个时钟走时误差太大了

youshx   2009-9-19 19:57 楼主
刚做好一个时钟(还要谢谢帮我修改程序的那些热心的朋友们),装好运行后发现每走一小时就要快5分钟左右,一天下来就差不多快了两小时。12MHz的石英晶振换过了,那两只电容也由原来的33P换到22P,再换到12P还是不行。我把电路原理图和工程文件一起传上来大家看看是哪里有问题了。
  • AT89C2051电子钟.jpg

回复评论 (29)

这个误差,主要来自软件

软件上修改,你可以尝试在中断服务程序里把定时器计数器的值弄小点(在原来的基础上减小1/12)

另外,断电以后,你的时钟不能保存的哦,实际上,真正的时钟都不是这么做的,向你 推荐DS12C877A
点赞  2009-9-19 20:23

LZ的图画的不错,但地线的符号不对

点赞  2009-9-19 20:25

一般来说,硬件和软件都可能存在一定的限制

但修改软件是万能的.对于1S,相差个几个毫秒是正常的,如果1S变成2S或者0.5S,那么问题出在你的 程序里了
点赞  2009-9-19 22:43

那个22P的 电容是微调的

一般不要修改那2个电容值
点赞  2009-9-19 22:44

还有一点

就是尽可能把定时间隔弄大点,比如,12M的CPU最大可以定时65.536MS.可以取50MS作为整数,比10MS单位的定时要准确些
点赞  2009-9-19 22:47
改用时钟芯片吧,国产的DS1302日误差2S,应该还可以接受的
QQ:1289535315 淘宝:http://feixue008.taobao.com 论坛:http://dianzhen.5d6d.com/bbs.php
点赞  2009-9-20 07:16

LS正解

LZ的电路图和软件,都来自大学里教学的资料,其实是不能用来做产品的,DS1302需要备份电池,精度还可以,但晶体是外挂的,电容最好22P,
点赞  2009-9-20 09:53
 先算下,水晶振动子是什么概念?随便找个样本看看,恐怕很难找到误差大过10的-5次方的产品的。即使再把温漂什么的一股脑儿全都考虑进去,也难误差出万分之几来。1天=24×3600秒=86400秒,亦即,硬软件若没错的话,一天能走偏10秒就相当值得怀疑了,反正俺是没见过。

 既然误差这么大,软件问题的可能性最大。把程序下载来浏览了下,俺没玩过51,无法过多评论,建议首先检查对定时器设置的4800有没算错?还有一处小的误差源(不是顶楼上讲的那么大误差)就是定时器应设成auto-reload方式的,不应在中断服务里软件重置计数器,否则会积累误差。
点赞  2009-9-20 23:55

回复 11楼 仙猫 的帖子

auto-reload 方式不可取,TIMER是8BIT的,中断定时太小.程序还没跑出中断,又要发生第二次中断,这样会把CPU累死的.
如果出现误差很稳定,还可以软件来休整,比如每12分钟自动加一分钟来调整.这个也是可以的.
点赞  2009-9-21 09:05
引用: 原帖由 jxb01033016 于 2009-9-21 09:05 发表
auto-reload 方式不可取,TIMER是8BIT的,中断定时太小.程序还没跑出中断,又要发生第二次中断,这样会把CPU累死的.
如果出现误差很稳定,还可以软件来休整,比如每12分钟自动加一分钟来调整.这个也是可以的.


 俺对51不熟,莫非没有16-bit timer?(硬件手册里看到timer2是16位,可auto-reload的。)
 假如只有8-bit,用一个timer的输出给另一个做capture源,就可以嵌套成16-bit。
 或者再退一步说,嵌套的16-bit分辨率不够,仍非用8-bit不可的话,因中断服务只有对一个8位变量的+1操作,再加上push/pop满打满算一次中断也不至于用掉80步timer(80μs)吧?即使在此系统开销被花掉1/3的情况下,相当于拿12MHz的CPU当8MHz的使,也是值得的,因为时钟是这个CPU的主业,它没干其他什么需要有实时性的事。
点赞  2009-9-21 09:34

回复 13楼 仙猫 的帖子

有16BIT ,但不是auto-reload
点赞  2009-9-21 13:31

回复 13楼 仙猫 的帖子

如果中断服务程序的长度>80步长,那么程序还没来的及退出,又进入了第二次中断,或者第二次中断被阻塞了.会造成定时计数误差.

51的自动装载和其他MCU不一样,一个16BIT 的TIMER被分为2个8BIT 的TIMERS, 2个TIMER的计数要求是一样的,但一个TIMER益出时,会
AUTO-RELOAD另一个TIMER的内容.
我的意思不是中断需要80个步长,我说的中断服务程式.中断本身只需要3-8个指令周期.

也就是说,2个中断之间的间隔必须大于中断服务程式所执行的时间.SO YOU UNDERSTAND???
点赞  2009-9-21 13:40
没弄过,帮不上忙了。
点赞  2009-9-21 15:56
 用足一个8-bit时钟是256个步长。刚才查了下51手册,timer是1/12主频驱动的,也就是说,可能设置的最长中断间隔时间是12×256=3072个CPU时钟(机器周期),那么在只有单个中断的情况下,3~8个指令周期应该只占3072个机器周期的几十分之一,绝无出现中断重叠的可能。
 80个时钟步长是满打满算放足裕量了的预算,这里想说的是,即便中断服务真需要那么长,也不过才花销了1/3的时间资源。

 不过话说回来,在AT89C2051手册里连“reload”字样都没能检索到,莫非这块芯片还真用不了硬件重载?而在Atmel产的另一款芯片AT89C52里,则是明明白白地写着可以使用16-bit auto-reload mode的。
点赞  2009-9-21 16:22

回复 16楼 angelatyizhong 的帖子

我也没弄过,我提出的只是一个理论计算方法,具有预见性的,其实这个程序的主程序不需要做多少事情,我说的并不是主程序占用CPU的多少资源,只是怕中断服务程序占的多.程序会死在中断服务程序里,情况是这样:通常主程序是一个死循环,一开始主程序在运行,到了一定时间(在这里是80US)一个中断来了,程序开始跳进中断,进行处理,还没处理完,(运算,逻辑处理)过了80US,另一个中断又来了,程序又跳进中断,又开始从头执行中断服务程序,如此循环,有2个后果,一,进入中断后主程序一直得不到运行.二,程序在中断服务程序里陷入死循环.
所以在设计程序时要特别小心,假设主时钟是12MHZ,那么时钟周期为1US.中断以后要进行现场保护,再跳转到中断服务程式,在处理完后再恢复现场,在用RETI指令返回到主程式. 也就是说,这个过程不能超过80US,具体做法是:尽可能缩短中断服务程式,把主要任务交给主程序去处理.

  叙述不完整或欠妥的地方,请指点!
 

             

[ 本帖最后由 jxb01033016 于 2009-9-21 16:43 编辑 ]
点赞  2009-9-21 16:40

回复 17楼 仙猫 的帖子

在80C51里,12MHZ时钟,机器周期也是主频12分频,是1US.所以时钟频率最大是机器周期的256倍,而不是256*12倍,3-8倍是中断延迟时间,不是中断服务时间.这2个概念你弄错了;P

[ 本帖最后由 jxb01033016 于 2009-9-21 16:58 编辑 ]
点赞  2009-9-21 16:54
引用: 原帖由 jxb01033016 于 2009-9-21 16:54 发表
在80C51里,12MHZ时钟,机器周期也是主频12分频,是1US.所以时钟频率最大是机器周期的256倍,而不是256*12倍,3-8倍是中断延迟时间,不是中断服务时间.这2个概念你弄错了;P

 大失所望,51真是这么慢吗?没做过51,以后也绝不做了!
 那么好吧,即便如此,8位timer转一圈是256步,中断服务只对一个byte变量+1,在80步之内不会做不到吧?还有一招就是在C编译时将那个byte变量设置成寄存器型的,连PUSH/POP
都省了。
点赞  2009-9-21 17:13

回复 20楼 仙猫 的帖子

51和其他MCU相比,是很慢的,12分频,

那具体看中断服务程序怎么写了?
PUSH ACC
PUSH PSW
PUSH   ...
PUSH ...
SERVR:

POP ...
POP ...
POP PSW
POP ACC

所以要力求压缩中断服务程序,如果是中断服务只对一个byte变量+1,在80步之内不会做不到吧?  

   RE  ::OK

来看看我公司的做的一个项目吧,也是RTC的中断.


void        TimeBase()                                        /* 0.5s interrupt timer program */
{        
  uchar Temp,i;
  _intc1 &= ~D6;                                           // clear this interrupt flag ,deny come back again .        
  _mfic &= ~D5;
        
  if(rWakeTimer) rWakeTimer--;
  

  if(rTimerAutoTran) rTimerAutoTran--;                //2s

  if(rFlag3&0x0f){
    if(rRetryCountCur) rRetryCountCur--;
  }


   
  if(++rMinCount >= 120) {
          rMinCount = 0;
          if(rTimeTranTest) rTimeTranTest--;

  }


  if(rFlag3&0xF0)
    {
      if(rRetryCountLight) rRetryCountLight--;        
    }
  //--------------------------            
  rFlag2 ^= cFlashdot;
                        
  if(rKeyValue == cKey_SetID || rKeyValue == cKey_SetRetry || rKeyValue == cKey_DEL || rKeyValue == cKey_TranManu)
    {        
      rTimeHold++;
    }               
               
  if( rKeyValue ==cKey_DevA || rKeyValue ==cKey_DevB || rKeyValue ==cKey_DevC || rKeyValue ==cKey_DevD   )
    {
      rTimeHold++;
    }
        

        
  if(rScreenReturnCnt<60)                        //0.5s*60=30s return timer
    {
      rScreenReturnCnt++;
    }
               
               
  rCurrentSecond++;
  if(rCurrentSecond==120){                //1 minute
    rZeroTransFlag=1;
    rCurrentMinute++;
    rCurrentSecond=0;
    if(rCurrentMinute==60){                //1 hour
      rCurrentHour++;
      rCurrentMinute=0;                        
      if(rCurrentHour==24){
        rCurrentSecond=0;
        rCurrentMinute=0;
        rCurrentHour=0;                                       
      }
    }
  }        
                        
}


这样就可能达不到了.必须把SECOND++,MINUTE++,HOUR++等搬到主程序去处理,也就是我前面说过的压缩中断服务程序.




[ 本帖最后由 jxb01033016 于 2009-9-21 18:06 编辑 ]
点赞  2009-9-21 17:58
是不是定时器设置有问题啊?
点赞  2009-9-21 18:09
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复