极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 23213|回复: 7

关于定时器中断MsTimer2

[复制链接]
发表于 2015-3-18 09:23:37 | 显示全部楼层 |阅读模式
小弟刚接触arduino没多久,用定时器中断MsTimer2做了一个时钟实验。每1s调用一次定时器中断,中断里的内容就是second++,最后通过数码管显示。实验发现这个MsTimer2特别不准..每隔1小时就与正常时间差了将近20s!
是我程序的问题,还是定时器中断本身的缺陷?求大神解答{:soso_e118:}

#include&#160;<MsTimer2.h>
int dataPin = 7;       
int clockPin  = 12;       
int latchPin = 13;       

unsigned char led_segbit[]={0x08,0x04,0x02,0x01};
unsigned char led_table[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xFF,0xBF};
unsigned char led_disbuf[4]={0,0,0,0};
unsigned char led_date[4];


unsigned char second=00;
unsigned char minute=15;
unsigned char hour=16;

void change(){
&#160;&#160;&#160;second++;
&#160;&#160;&#160;&#160;if(second > 59)
&#160;&#160;&#160;&#160;{
&#160;&#160;&#160;&#160;&#160;&#160;second&#160;=&#160;0;
&#160;&#160;&#160;&#160;&#160;&#160;minute&#160;++;
&#160;&#160;&#160;&#160;&#160;&#160;if(minute > 59)
&#160;&#160;&#160;&#160;&#160;&#160;{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;minute&#160;=&#160;0;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;hour&#160;++;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if(hour > 23)   hour = 0;
&#160;&#160;&#160;&#160;&#160;&#160;}
&#160;&#160;&#160;&#160;}
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
}
void setup()
{
&#160;&#160;&#160;&#160;pinMode(dataPin,OUTPUT);
&#160;&#160;&#160;&#160;pinMode(clockPin,OUTPUT);
&#160;&#160;&#160;&#160;pinMode(latchPin,OUTPUT);
&#160;&#160;&#160;&#160;MsTimer2::set(1000, change);
&#160;&#160;&#160;&#160;MsTimer2::start();
&#160;&#160;&#160;&#160;Serial.begin(9600);
}
void loop()
{
&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;LED4_Display&#160;();
&#160;&#160;&#160;&#160;&#160;
}&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;
void LED4_Display (void)
{
&#160;&#160;&#160;&#160;&#160;led_date[0]=hour/10;
&#160;&#160;&#160;&#160;&#160;led_date[1]=hour%10;
&#160;&#160;&#160;&#160;&#160;led_date[2]=minute/10;
&#160;&#160;&#160;&#160;&#160;led_date[3]=minute%10;
&#160;&#160;&#160;
&#160;&#160;&#160;&#160;unsigned char* led_address;  
&#160;&#160;&#160;&#160;for(int i=0;i<4;i++)
&#160;&#160;&#160;&#160;{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;led_address=led_table+led_date;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;led_disbuf=*led_address;
        digitalWrite(latchPin,LOW);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;shiftOut(dataPin,clockPin,MSBFIRST,led_disbuf);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;shiftOut(dataPin,clockPin,MSBFIRST,led_segbit);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;digitalWrite(latchPin,HIGH);
&#160;&#160;&#160;&#160;}&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
}
回复

使用道具 举报

发表于 2015-3-18 10:59:22 | 显示全部楼层
时间定时通常使用内部计数器,也即是是一种机械式,如果工作时钟不怎么准确,计数后除出来的数可能产生误差,因此,如果是一个固定误差的话,可以在程序中进行修正。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-18 11:42:56 | 显示全部楼层
林定祥 发表于 2015-3-18 10:59
时间定时通常使用内部计数器,也即是是一种机械式,如果工作时钟不怎么准确,计数后除出来的数可能产生误差 ...

感觉像是一个固定误差,平均每小时就会慢20s左右。不过在程序中具体应该如何修正? 我在网上也看过其他人写的关于用数码管做时钟的程序,大部分都会有一个对时钟进行微调的常量,不过不太明白这个微调是如何对时间误差进行修正的
回复 支持 反对

使用道具 举报

发表于 2015-3-18 20:31:49 | 显示全部楼层
中断程序中另加一个变量,没中断一次+1,满180,清零,同时时间计数器额外+1,这样每一小时加多了20妙秒,补偿那20秒。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-19 08:51:13 | 显示全部楼层
林定祥 发表于 2015-3-18 20:31
中断程序中另加一个变量,没中断一次+1,满180,清零,同时时间计数器额外+1,这样每一小时加多了20妙秒,补 ...

谢谢大神
回复 支持 反对

使用道具 举报

发表于 2015-4-7 01:01:55 | 显示全部楼层
女神去哪了 发表于 2015-3-19 08:51
谢谢大神


这是因为 MsTimer2 使用 timer2 (废话 :-)
且 prescaler  用 64
timer2 是 8 bit counter
于是, 这使得它所谓的 1ms 其实是 1.024ms,
阿就是说其实其内部是每隔 1.024ms 产生一次中断, 不是每 1ms,
跟 timer0 帮忙 millis( ) 计算 millis 类似,
但是在 millis( ) 函数内有个调整机制,
里面偷用一个变数 unsigned char  gg=0; 每次1.024中断做 gg+=3;
然后 if(gg >=125) { gg-=125;  millis++; }
就是说大约 125/3 = 41.6 次中断会偷偷调整 1 millis;
但在 MsTimer2 库内没针对这做调整 !
所以, 如果你要用 MsTimer2 做出时钟, 那可以如下做法(仿照 millis( )):

(1)在 void setup( ) {
那句
MsTimer2::set(1000, change);
改为
MsTimer2::set(1, change);  // 1.024 ms; 注意, 不是 1ms
(2)把你的 change 函数改如下

  void change(){  // 每 1ms (1.024 ms) 来一次中断
    static unsigned char gg = 0;
    static int ms = 0;
    ++ms; gg += 3;
    if(gg >= 125) { ++ms; gg -= 125; }
    if(ms < 1000) return;
    ms -= 1000;
   //////
    second++;
    if(second > 59)
    {
      second = 0;
      minute ++;
      if(minute > 59)
      {
        minute = 0;
        hour ++;
        if(hour > 23)   hour = 0;
      }
    }
这样就会很准 !
不信你测试看看 :-)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-8 15:08:16 | 显示全部楼层
tsaiwn 发表于 2015-4-7 01:01
这是因为 MsTimer2 使用 timer2 (废话 :-)
且 prescaler  用 64
timer2 是 8 bit counter

原来是这样. 解释的太详细了 谢谢
回复 支持 反对

使用道具 举报

发表于 2015-4-8 23:13:24 | 显示全部楼层
女神去哪了 发表于 2015-4-8 15:08
原来是这样. 解释的太详细了 谢谢

【补充】
   这个调整 millis 的动作跟闰年(Leap year)原理类似,
   因为地球绕太阳一圈的回归年其实是365.2421990741天,
   不是 365天也不是 366天, 所以每四年要闰年一次多一天,
   可是四年多一天等於算做一年是 365.25 天, 又不准了,
   因此每一百年又把多算的一天取消(公元年/100整除不是闰年)做修正 !!

       这里的算法是因每次误差 0.024 ms, 用 3 代表,
   然后 125 就是代表 0.024ms * 125 = 1.000ms,
   因此如果 (gg >= 125) 就要把 millis 加 1,
   並且要做 gg -= 125;
   注意不是设为 0 喔, 是减去 125,
   因这时可能是125, 126, 127 这三个之一个,  
   多出来的误差要累计到下次的计算內。


回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-17 20:15 , Processed in 0.055070 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表