So you’re programming for a platform with a built-in RTC… cool. Now what?
Most (all?) XMEGA‘s have an on-board real-time clock, some can even be backed-up by a distinct battery (like the xmega256a3bu, shown below) which can be a real time saver (oh, the pun-anity!).
The question then becomes how to best use it.
I’ve rolled my own solution a few times, which quickly got tiresome. Then I encountered the 256A3BU, which uses a totally different system. Finally, I wound up implementing a generalized solution that you can be used on both RTC and RTC32 platforms, includes a RealTimeClock system library, and has some neat features like periodic callbacks.
While I was digging around in the Arduino SDK (the Xmegaduino implementation, to be precise), I found a few things that made me an unhappy pappy. Most importantly:
- there was no support for RTC32
- the code in wiring.c was a jumbled mess of preprocessor conditionals (are we using RTC or not… do different stuff)
- the RTC implementation was continuously interrupting your code to tick the millisecond counter, even when you were busy doing something completely unrelated, or trying to sleep, or whatever with this:
ISR(RTC_OVF_vect) { rtc_millis = rtc_millis+4; // sadness }
Hm. Without going into all the mods (which you can inspect for yourself on https://github.com/psychogenic/Xmegaduino), what I did was split out the RTC stuff from wiring.c (into wiring_rtc.c and wiring_rtc32.c) and implement a few modifications that will now allow the counter to do its thing on its own, only becoming active when either:
- you need to know what time it is (calls to millis()/micros() or the delays)
- the RTC counter overflows, in which case the milliseconds are preserved as the clock loops around.
Also, I added low-level functions that allow you to set a callback and handle setting up the comparator to wake-up and trigger your function at the right time.
So we now have a nice and clean(er) implementation for both RTC and RTC32 that don’t spend much time mucking about when they’re not needed. Still, though most of it is masked by the Arduino SDK functions, if you want to do anything fancy you have to mess around with low-level internal stuff… nah, could be better.
RealTimeClock library
In order to allow easy access to the neat features, and to actually provide the services we really want from an real-time clock, I also created a RealTimeClock system library. It knows about the details of the back-end implementation and lets you focus on what you want to do, i.e. keep track of time. It has some features that are most useful when you have a battery backed clock, but these can still be of use in other cases with good-ol-regular RTC.
The library is inspired by the public domain RTClib by JeeLabs and mostly sticks to the same APIs. There are RealTimeClock::DateTime and RealTimeClock::TimeSpan helper classes and, most importantly, a RealTimeClock::Device.
In essence, you use the RealTimeClock::Device class to set the current time (using begin() or adjust()), use setPeriodicCallback()/clearPeriodicCallback() if you want to set/clear an alarm (a function that will be called after every N seconds), and call now() to get a DateTime object.
If you have a battery-backed RTC, then you can use the periodic callback to stash the latest correct time in some kind of permanent storage (EEPROM whatever) and then retrieve it for your call to begin on power-up. The RTC keeps running while your down, so the call to begin will actually hold the current time.
void somethingIWannaDoEveryFewSeconds() { // do it! Serial.println(F("My time has come!")); } void setup() { Serial.begin(115200); // you could get the start datetime from EEPROM, had you stored it there... RealTimeClock::DateTime startDate(2014, 8, 30, 9, 0, 0); RealTimeClock::Device::begin(startDate); // arrange to call somethingIWannaDoEveryFewSeconds() every 15 seconds: RealTimeClock::Device::setPeriodicCallback(somethingIWannaDoEveryFewSeconds, 15); } void loop() { RealTimeClock::DateTime nowTime = RealTimeClock::Device::now(); Serial.print(F("It's now: ")); Serial.print((int)nowTime.hour()); Serial.print(':'); Serial.print((int)nowTime.minute()); Serial.print(':'); Serial.println((int)nowTime.second()); Serial.print(F("millis() says: ")); Serial.println(millis()); delay(2500); }
I’ll be putting a page up asap for the RealTimeClock project. In the meantime, you can apply this patch to your Xmegaduino installation or check out the code from github.
Of Batteries and Fuses
If you’re using the xmega256a3bu and want to have the battery backup working, you actually have to ensure that you are using the brown-out detector. It needs to be enabled and setup correctly. You can follow your own recipe but the short version is that if you set:
Fuse2: 0xFD (sampled powerdown BOD) Fuse5: 0xEB (enabled active BOD, and BOD level 2.6V--011)
Then things should work fine.
Drop me a line with comments/questions. Enjoy!