From 2d096da6d19a76afe220a85c2a4d8f727068f6c3 Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Tue, 5 Jun 2018 20:48:48 +0200 Subject: [PATCH 1/3] Make RTC_Millis immune to millis() rollover events --- RTClib.cpp | 18 +++++++++++++++--- RTClib.h | 5 +++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/RTClib.cpp b/RTClib.cpp index a08dfe92..804168ec 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -337,14 +337,26 @@ void RTC_DS1307::writenvram(uint8_t address, uint8_t data) { //////////////////////////////////////////////////////////////////////////////// // RTC_Millis implementation -long RTC_Millis::offset = 0; +// Alignment between the milis() timescale and the Unix timescale. These +// two variables are updated on each call to now(), which prevents +// rollover issues. Note that lastMillis is **not** the millis() value +// of the last call to now(): it's the millis() value corresponding to +// the last **full second** of Unix time. +uint32_t RTC_Millis::lastMillis; +uint32_t RTC_Millis::lastUnix; void RTC_Millis::adjust(const DateTime& dt) { - offset = dt.unixtime() - millis() / 1000; + lastMillis = millis(); + lastUnix = dt.unixtime(); } +// Note that computing (millis() - lastMillis) is rollover-safe as long +// as this method is called at least once every 49.7 days. DateTime RTC_Millis::now() { - return (uint32_t)(offset + millis() / 1000); + uint32_t elapsedSeconds = (millis() - lastMillis) / 1000; + lastMillis += elapsedSeconds * 1000; + lastUnix += elapsedSeconds; + return lastUnix; } //////////////////////////////////////////////////////////////////////////////// diff --git a/RTClib.h b/RTClib.h index c69da115..2faeaf2f 100644 --- a/RTClib.h +++ b/RTClib.h @@ -121,7 +121,7 @@ class RTC_PCF8523 { }; // RTC using the internal millis() clock, has to be initialized before use -// NOTE: this clock won't be correct once the millis() timer rolls over (>49d?) +// NOTE: this is immune to millis() rollover events class RTC_Millis { public: static void begin(const DateTime& dt) { adjust(dt); } @@ -129,7 +129,8 @@ class RTC_Millis { static DateTime now(); protected: - static long offset; + static uint32_t lastUnix; + static uint32_t lastMillis; }; #endif // _RTCLIB_H_ From aa112e874c35261e74d0380ad6c843d4783fac48 Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Wed, 6 Jun 2018 22:16:04 +0200 Subject: [PATCH 2/3] Implement tunable RTC_Micros This is identical to RTC_Millis, except for: * The method now() has to be called more often than once every 71.6 minutes. * The clock can be syntonized (i.e. its drift tuned) with a 1 ppm resolution using adjustDrift(): a positive adjustment makes the clock faster. Note that syntonization does not introduce time discontinuities. --- RTClib.cpp | 26 ++++++++++++++++++++++++++ RTClib.h | 18 ++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/RTClib.cpp b/RTClib.cpp index 804168ec..6f47ad62 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -360,6 +360,32 @@ DateTime RTC_Millis::now() { } //////////////////////////////////////////////////////////////////////////////// +// RTC_Micros implementation + +// Number of microseconds reported by micros() per "true" (calibrated) +// second. +uint32_t RTC_Micros::microsPerSecond = 1000000; + +// The timing logic is identical to RTC_Millis. +uint32_t RTC_Micros::lastMicros; +uint32_t RTC_Micros::lastUnix; + +void RTC_Micros::adjust(const DateTime& dt) { + lastMicros = micros(); + lastUnix = dt.unixtime(); +} + +// A positive adjustment makes the clock faster. +void RTC_Micros::adjustDrift(int ppm) { + microsPerSecond = 1000000 - ppm; +} + +DateTime RTC_Micros::now() { + uint32_t elapsedSeconds = (micros() - lastMicros) / microsPerSecond; + lastMicros += elapsedSeconds * microsPerSecond; + lastUnix += elapsedSeconds; + return lastUnix; +} //////////////////////////////////////////////////////////////////////////////// // RTC_PCF8563 implementation diff --git a/RTClib.h b/RTClib.h index 2faeaf2f..65d66461 100644 --- a/RTClib.h +++ b/RTClib.h @@ -133,4 +133,22 @@ class RTC_Millis { static uint32_t lastMillis; }; +// RTC using the internal micros() clock, has to be initialized before +// use. Unlike RTC_Millis, this can be tuned in order to compensate for +// the natural drift of the system clock. Note that now() has to be +// called more frequently than the micros() rollover period, which is +// approximately 71.6 minutes. +class RTC_Micros { +public: + static void begin(const DateTime& dt) { adjust(dt); } + static void adjust(const DateTime& dt); + static void adjustDrift(int ppm); + static DateTime now(); + +protected: + static uint32_t microsPerSecond; + static uint32_t lastUnix; + static uint32_t lastMicros; +}; + #endif // _RTCLIB_H_ From d8cf2533725700b73cda179aec35cd55562d3fef Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Tue, 7 May 2019 13:13:42 +0200 Subject: [PATCH 3/3] Update keywords.txt for RTC_Micros --- keywords.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keywords.txt b/keywords.txt index 0288774a..437ff081 100644 --- a/keywords.txt +++ b/keywords.txt @@ -10,6 +10,7 @@ DateTime KEYWORD1 TimeSpan KEYWORD1 RTC_DS1307 KEYWORD1 RTC_Millis KEYWORD1 +RTC_Micros KEYWORD1 Ds1307SqwPinMode KEYWORD1 ####################################### @@ -27,6 +28,7 @@ secondstime KEYWORD2 unixtime KEYWORD2 begin KEYWORD2 adjust KEYWORD2 +adjustDrift KEYWORD2 isrunning KEYWORD2 now KEYWORD2 readSqwPinMode KEYWORD2