查看: 22301|回复: 7
关于定时器中断MsTimer2
[复制链接]
女神去哪了
女神去哪了
当前离线
积分152
电梯直达
1#
发表于 2015-3-18 09:23:37
|
只看该作者
|倒序浏览
|阅读模式
小弟刚接触arduino没多久,用定时器中断MsTimer2做了一个时钟实验。每1s调用一次定时器中断,中断里的内容就是second++,最后通过数码管显示。实验发现这个MsTimer2特别不准..每隔1小时就与正常时间差了将近20s!
是我程序的问题,还是定时器中断本身的缺陷?求大神解答{:soso_e118:}
#include
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(){
second++;
if(second > 59)
{
second = 0;
minute ++;
if(minute > 59)
{
minute = 0;
hour ++;
if(hour > 23) hour = 0;
}
}
}
void setup()
{
pinMode(dataPin,OUTPUT);
pinMode(clockPin,OUTPUT);
pinMode(latchPin,OUTPUT);
MsTimer2::set(1000, change);
MsTimer2::start();
Serial.begin(9600);
}
void loop()
{
LED4_Display ();
}
void LED4_Display (void)
{
led_date[0]=hour/10;
led_date[1]=hour%10;
led_date[2]=minute/10;
led_date[3]=minute%10;
unsigned char* led_address;
for(int i=0;i<4;i++)
{
led_address=led_table+led_date;
led_disbuf=*led_address;
digitalWrite(latchPin,LOW);
shiftOut(dataPin,clockPin,MSBFIRST,led_disbuf);
shiftOut(dataPin,clockPin,MSBFIRST,led_segbit);
digitalWrite(latchPin,HIGH);
}
}
分享到:
QQ好友和群
收藏1
回复
使用道具
举报
林定祥
林定祥
当前离线
积分4152
2#
发表于 2015-3-18 10:59:22
|
只看该作者
时间定时通常使用内部计数器,也即是是一种机械式,如果工作时钟不怎么准确,计数后除出来的数可能产生误差,因此,如果是一个固定误差的话,可以在程序中进行修正。
回复
支持
反对
使用道具
举报
女神去哪了
女神去哪了
当前离线
积分152
3#
楼主|
发表于 2015-3-18 11:42:56
|
只看该作者
林定祥 发表于 2015-3-18 10:59
时间定时通常使用内部计数器,也即是是一种机械式,如果工作时钟不怎么准确,计数后除出来的数可能产生误差 ...
感觉像是一个固定误差,平均每小时就会慢20s左右。不过在程序中具体应该如何修正? 我在网上也看过其他人写的关于用数码管做时钟的程序,大部分都会有一个对时钟进行微调的常量,不过不太明白这个微调是如何对时间误差进行修正的
回复
支持
反对
使用道具
举报
林定祥
林定祥
当前离线
积分4152
4#
发表于 2015-3-18 20:31:49
|
只看该作者
中断程序中另加一个变量,没中断一次+1,满180,清零,同时时间计数器额外+1,这样每一小时加多了20妙秒,补偿那20秒。
回复
支持
反对
使用道具
举报
女神去哪了
女神去哪了
当前离线
积分152
5#
楼主|
发表于 2015-3-19 08:51:13
|
只看该作者
林定祥 发表于 2015-3-18 20:31
中断程序中另加一个变量,没中断一次+1,满180,清零,同时时间计数器额外+1,这样每一小时加多了20妙秒,补 ...
谢谢大神
回复
支持
反对
使用道具
举报
tsaiwn
tsaiwn
当前离线
积分594
6#
发表于 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;
}
}
这样就会很准 !
不信你测试看看 :-)
回复
支持
反对
使用道具
举报
女神去哪了
女神去哪了
当前离线
积分152
7#
楼主|
发表于 2015-4-8 15:08:16
|
只看该作者
tsaiwn 发表于 2015-4-7 01:01
这是因为 MsTimer2 使用 timer2 (废话 :-)
且 prescaler 用 64
timer2 是 8 bit counter
原来是这样. 解释的太详细了 谢谢
回复
支持
反对
使用道具
举报
tsaiwn
tsaiwn
当前离线
积分594
8#
发表于 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 这三个之一个,
多出来的误差要累计到下次的计算內。
回复
支持
反对
使用道具
举报