In this part of the tutorial we’ll add support for the Real Time Clock (RTC) on nRF52832. Final code of this blog post can be downloaded at Ruuvi GitHub in the ruuviblog-branch, tag 3.8.0-alpha.
Please follow part 1 of the series for details on how to clone the repository and compile the code. Final hex of this tutorial can be downloaded from the Ruuvi Jenkins.
Real Time Clock
The RTC is used for keeping track of the passage of time outside the program state. For now we’ll be happy with counting milliseconds since the program boot and timestamping our sensor data samples. Later on we might add a mechanism to synchronize internal clock of nRF52832 to externally given time, such as the Unix Epoch.
Since configuring things such as scale, resolution or sample rate does not really make sense for the RTC we won’t be using the sensor interface for the RTC peripheral. We’ll define only init, uninit and mills -functions for the RTC and add a function pointer to the timestamp function in our sensor interface.
The internal counter of nRF52 has only 24 bits, but luckily we have an overflow event which can be used to track the times when the internal counter has rolled over.
Up until now our sleep has kept all the clocks off, and only way to wake up has been via external interrupt, such as button press. This however means that time does not exist for our device while it is sleeping. We can start the low-frequency clock (LFCLK) when we initialize the RTC and keep the LFCLK active during sleep to keep track of the time through the sleep periods.
As the Nordic Softdevice used by the Bluetooth stack will keep the low-frequency crystal active, LFCLK comes without any “extra” energy cost in the final program. The SDK provides convenient request– and release-functions for the LFCLK which keep track on if the LFCLK can be stopped in case there are several users for the clock, so we can let our RTC driver request and release the LFCLK without worrying other drivers state.
Since we’re using 64 bits as the counter width, we don’t really need the prescaler of the RTC. We’ll increment the ticks at 32.768 kHz of the LFCLK which gives us roughly 512 seconds between the hardware counter overflows and 17850 years before our software counter overflows. We could also get sub-millisecond resolution for the timestamps, but for now it won’t be necessary.
Integrating the RTC
We’ll add the timestamping function to our RTC interface and adjust our drivers to call it rather than just marking the timestamp of the samples as invalid. To avoid dependencies between the sensor interfaces and RTC itself, we add timestamping function as a function pointer to the sensor interface and set the function pointer to the interface at the RTC initialization.
Now we can also finally add debouncing support to our button code: we timestamp the button press and unless enough time has passed since last registered press we’ll ignore the event.
Finally, we’ll fix the UINT64 printing bug on ARMGCC by simply dropping out the higher 32 bits of the timestamp. This still gives us almost 50 days before our timestamp rolls over.
In previous part we started building unit tests which check that each sensor interface has proper behavior on initialization and uninitialization. Let’s append the test and add test for getting and setting scale, sample rate and resolution. Since all of the functions have the same signature we can use the same tests on each of the functions.
This time, our new test finds a problem in the previous test. Since the initialization test does not set the sensor structure to all zeroes before first initialization, our initialization test is happy to see that a function pointer is not NULL if some old values remain in the memory where our struct points at. When we actually try to call the function, program crashes as the function pointer does not point to a valid address. Let’s get both the test and the bug fixed and continue.
Time to go back and clean up the drivers a bit. First we see that the we have had issues with lines 187, 193 and 223 of our ADC self-test.
It seems that our ADC does not have proper NULL pointer checking in the scale setter and getter, let’s fix it.
Now we re-run the tests:
After the fix we pass 11 / 16 tests, up from previous 9 / 16. Rest of the errors are similarly tracked down and fixed.
As we have the LFCLK constantly running, we can expect some power consumption increase after enabling the RTC. Let’s see how it looks like.
Our current consumption is now 6.4 μA at idle, 1.4 μA up from 5.0 μA in our previous post.
In this tutorial we have added RTC support and timestamps to our sensor readouts. Now we have everything we need to implement the Ruuvi data format 3. In the next post we’ll start broadcasting the data from RuuviTag.
Stay tuned and follow @ojousima and @ruuvicom on Twitter for #FirmwareFriday posts!