DS3232 is an extremely accurate RTC with a guaranteed accuracy of 2.5 ppm (0 °C to 40 °C), which translates into an error of just 80 seconds over the course of a year under the worst case scenario. I had done a few projects using this chip before (you can read about them here).

While by default DS3232 is already very accurate, we can push its accuracy even higher by adjusting its aging offset register (8bit). This adjustment works by adding or subtracting the corresponding capacitance to or from the oscillator capacitor array. The adjustable range is represented as 2’s complement (-128 to 127) and each LSB change corresponds to roughly 0.1 ppm of change in frequency. So the overall adjustment range can be achieved programmatically is roughly ±13 ppm.

In its default configuration, the TCXO frequency is adjusted every 64 seconds depending on the environmental temperature by switching in or switching out capacitance via an internal look-up table. By utilizing the aging register, we can further null out any remaining offset. The aging offset adjustment is independent of the automatic adjustment via temperature compensation.

The code snippet below shows how to adjust the aging register of DS3232 via I2C. The full Arduino code listing can be found towards the end of the post. The aging offset register is at 0x10 and the valid values for the input parameter offset ranges from -128 to 127. Note that this number is later converted into 2’s complement before sending to DS3232.

void setAgingOffset(int offset)
    if (offset < 0) offset += 256;


Since normally the TCXO is adjusted every 64 seconds, in order for the changes to take effect immediately, we need to force the CONV bit (bit 5) to high in the control register (0x0E):

void forceConversion()


The picture blow shows my circuit setup. The DS3232 is soldered onto a protoboard using the reference design. The backup battery (CR2302) is on the other side of the board. Two buttons are attached to digital pin 2 and 3 for increasing and decreasing the offset value of the aging register. By default, the offset value is 0 and it can be adjusted between -128 to 127. If you have the Arduino serial monitor open while running the code, you can see the read back of the current aging offset value displayed.


As mentioned earlier, each adjustment step changes the clock frequency by roughly 0.1ppm (which translates into roughly between 0.002 to 0.003 Hz). Here’s a picture showing the calibrated clock frequency and in this case the aging offset is adjusted to -40. The frequency is measured on my fixed-up HP 5350B in high resolution mode.


Finally, here’s a short video showing the adjustment process. Once the offset has been adjusted, power to the MCU can be disconnected and the value will remain as long as the chip is powered by the backup battery.

View on YouTube in a new window



Be Sociable, Share!

9 Thoughts on “DS3232 Clock Frequency Calibration”

  • Mr. Wong,
    Just admiring your DS3232 calibration sketch. You put a lot of work into this.
    I have several DS3231 RTC’s that need to be calibrated. I ran your sketch on
    three of them and it seems to function since I can see + and – entries in the
    serial monitor but the frequency output does not change. I have a frequency
    counter and can see the 32K clock but it does not move at all. Is it because
    it is not a DS3232?

  • This “converted into 2’s complement before sending to DS3232.” makes no sense whatsoever. Why are you adding 256 to a negative `offset`? What does it achieve?

    `Wire` class converts all numerical values to `unit8_t` before sending them. Which means that adding 256 to the value being sent is a completely inconsequential action: just a waste of code and CPU cycles.

    • But Kerry’s code taught a few of us newbs a thing or two about two’s complement (pun intended). And I think our machines today have more than enough processor cycles to cope! :P

      • I can’t say that I subscribe to the principle “just because our CPUs have lots of processing cycles let’s gratuitously waste them on nonsensical actions”.

        Things like that are not just about CPU cycles. Such nonsensical cargo-cult actions also make the code unreadable because they constantly force a normal reader to stumble in dumbfounded amazement: “why are they doing that?”

  • Thanks – I’ve used some of your code in my binary clock code. I did modify it a little, so that the setAgingOffset function accepts a float value of the ppm error, then converts this to the offset…. easier for future me to understand when (if!) I need to recalibrate my DS3231…… the two’s complement part would have caught me out, thanks for that too.

Leave a Reply

Your email address will not be published. Required fields are marked *