我已经使用Arduino制作了一个时钟,但是时间似乎在漂移。我知道过渡问题;一周内时钟似乎会偏移15分钟左右。
我正在为此 Digi-key的谐振器使用定制PCB 。该代码在每个循环的开头读取millis()函数,并从该值开始工作。
我的问题是:我该如何使用Arduino测量时间,准确到足以制作出可通过的台式时钟?
millis()
。
我已经使用Arduino制作了一个时钟,但是时间似乎在漂移。我知道过渡问题;一周内时钟似乎会偏移15分钟左右。
我正在为此 Digi-key的谐振器使用定制PCB 。该代码在每个循环的开头读取millis()函数,并从该值开始工作。
我的问题是:我该如何使用Arduino测量时间,准确到足以制作出可通过的台式时钟?
millis()
。
Answers:
注意:尽管我的回答被接受并且具有较高的投票分数,但是请确保您阅读了Edgar Bonet关于如何使Arduino保持时间而不使用RTC 的出色回答。
我在使用DS1307实时时钟方面非常成功。这是其数据表的链接。
以下是其一些功能:
它使用IC接口与Arduino进行通信,从而可以轻松地使用正确的库(可从网上获得)进行编程。
它通过SCL和SDA引脚(分别为模拟A4和A5)连接到Arduino,因此仅使用2个引脚。
它只需要很少的外部组件即可运行。
IT可以连接到纽扣电池,因此即使关闭Arduino也可以节省时间。在其低功率模式下,纽扣电池可持续使用数年。
它的漂移很小(在我的情况下,每周仅漂移几秒钟)。
它不是很贵。
如果您不打算使用RTC,则可以替换通常用于为arduino提供时钟的晶体,例如Farnel的晶体振荡器模块或另一种晶体振荡器模块。它们采用4引脚封装,如下图所示。它们将为您的arduino生成更精确的时钟。
提到的两个模块的公差均为50 ppm,并在5V下工作。
同样,请注意,不要将这些晶体振荡器模块与常规的2引脚晶体混淆,如下所示。例如,这些是MCU外部时钟电路的一部分。
您不需要RTC来构建时钟:ATmega芯片具有执行RTC本身职责所需的所有硬件。方法如下:
获得32768 Hz的手表晶体:购买或拆卸旧时钟。这些专为计时而设计的晶体具有极小的温度漂移。如果您想使用RTC芯片,则还需要其中之一。
配置ATmega的保险丝,使其在8 MHz RC振荡器上运行。这将使您的millis()
功能极其不准确,并且还会释放XTAL1和XTAL2引脚。
将手表晶体连接到TOSC1和TOSC2引脚。这些引脚与XTAL1和XTAL2相同(328P上为9和10)。不同的名称用于表示不同的功能。
将定时/计数器2配置为异步操作,正常计数模式,预分频器设置为128,并启用定时器溢出中断。
现在,您将以非常稳定的速率(每秒一次)获得TIMER2_OVF中断。您只需在ISR中将时钟显示提前一秒。在两次中断之间,您可以使MCU处于深度睡眠状态(节能睡眠模式:只有定时器2才能运行),并且可以在几个AA单元上运行数年。显然,除非显示器耗电。
我正是这样做的,目的是建造我的24小时单手挂钟。现在,该链接指向原始文档的法文英文翻译。
如果您不校准石英,则可能会出现很大的漂移,通常每周几秒钟。漂移速率取决于将晶体连接至MCU的走线的杂散电容。原则上,可以通过添加一些额外的,经过微调的电容将其删除。值得注意的是,使用RTC时也会遇到相同的漂移问题。
如果您对这种准确性感到满意,那就忍受它并感到高兴。但是,如果您想测量漂移,则会发现它非常稳定。然后,您可以轻松地用软件对其进行补偿,并达到每年几秒钟的精度。
校正漂移的算法非常简单。从测得的漂移中,您可以得出中断之间的精确延迟,该延迟应非常接近10 9 纳秒,然后:
#define ONE_SECOND 1000000000 // in nanoseconds
#define ONE_INTERRUPT 999993482 // for example
ISR(TIMER2_OVF_vect)
{
static uint32_t unaccounted_time;
unaccounted_time += ONE_INTERRUPT;
while (unaccounted_time >= ONE_SECOND) {
advance_display_by_one_second();
unaccounted_time -= ONE_SECOND;
}
}
在上面的示例中,石英速度有点过快,并且软件会每隔几天“丢失”一个刻度来进行补偿。如果石英速度太慢,则同一代码将每隔几天两次双击。
也可以对RTC进行这种校准,但是它将更加复杂,因为RTC以细分形式报告时间,这自然不适合进行算术运算。
您指定的谐振器具有0.3%的稳定性,其中晶体或晶体振荡器(如Ricardo所述)为50ppm。稳定很多倍。更不用说谐振器的温度漂移是可怕的。阳光加热会改变它。因此,谐振器不应用于长时间保持时间。
因此,使用晶体或晶体振荡器将得到您想要的。要么在ATmega上使用它并分别设置保险丝,要么我们将其连接到RTC。
如果您不想使用诸如实时时钟之类的额外硬件(例如DSDS1307),则可以通过禁用所有未使用的中断来显着提高定时精度。默认情况下,Arduino草图带有启用的各种中断例程,通常它们实际上并未用于草图。找出是否可以做的最快方法,尝试通过发出尝试禁用它们noInterrupts();
millis()
除非您一次要花费超过一毫秒的时间来维护它们,在这种情况下,您还会遇到其他问题... 4.禁用中断noInterrupts()
会完全millis()
浪费时间!
我了解Arduino的许多精神是节俭,偶尔会遇到问题。我在工作场所使用Arduino(现在使用chipKIT,因为它具有10倍的RAM和10倍的时钟速度),而我需要“外围功能”以使其尽可能快地工作。
我在我的一个项目中使用了sparkfun实时时钟,对此感到非常满意。他们也有一个“ Dead on”变体。