DS3231 Drift Results (5 months)

It’s been just under 5 months since I simultaneously synchronized ten DS3231/DS3231M RTCs as part of a long-term experiment to measure their drift. Of the ten, seven are crystal-based DS3231 chips, while three are DS3231M chips. Since they’re all on the same breadboard connected to the same power supply, all of them have been subject to the same physical conditions of temperature, movement, voltage, etc. insofar as I can control for them in my apartment.

In the table below “Number” is the identifying number of each chip I arbitrarially asigned to uniquely identify each one, “Type” refers to its type (all the DS3231 modules are marked as the wide-temperature-range SN type, while the DS3231Ms are listed as M. The official DS3231M chip (#2) I received directly from Maxim is marked with an asterix.), “Offset” is the aging offset in register 0x10 expressed in decimal form, “PPM” is the stability in parts per million, and “Drift” is the number of seconds the clock has drifted since the start. For both the PPM and Drift columns, a positive value indicates that the RTC has run faster than the NTP-synchronized system clock while a negative value indicates the RTC has run slower than the system clock.

All results were collected over a 26 minute period starting 12904549 seconds after the clocks were first synchronized. Each clock was measured three times and the resulting values averaged and rounded to two decimal places. Keep in mind that this dataset consists of just two data points (zero drift at the start, and the measured drift now) for each clock: unlike Dan, who continuously collected data and made many nice graphs, I set the clocks and essentially ignored them for five months.

Anyway, I digress. Here’s the results:

Number	Type	Offset	PPM	Drift
0	SN	-6	0.19	2.46
1	SN	0	-0.69	-8.96
2	M*	0	-1.62	-20.85
3	M	0	-3.06	-39.54
4	M	0	-2.76	-35.65
5	SN	0	0.16	2.07
6	SN	0	0.01	0.10
7	SN	-15	0.33	4.32
8	SN	0	0.08	0.98
9	SN	0	-0.05	-0.60

Some commentary, bullet-pointed for your reading pleasure:

  • All clocks are within their advertised tolerances (2 ppm for the crystal-based clocks and 5 ppm for the MEMS-based clocks).
  • Five of the seven crystal-based DS3231 chips run fast, while two run slow. All three of the MEMS-based DS3231M chips run slow.
  • Clock #6 has essentially no drift whatsoever. There’s nothing particularly noteworthy about it: just luck of the draw.
  • The Raspberry Pi used to set the clocks in September was also used to measure the offset today. It has run continuously since the clocks were set, and has been synchronized continuously to another Raspberry Pi (+/- 0.1 ms), which was in turn synchronized to GPS using a Motorola Oncore UT+ receiver (+/- 150 ns). Time errors on the Pis are negligible.
  • I have two PCA9548A I2C switches that allow me to wire up all the clock chips and switch between them using software commands rather than needing to physically move wires around. This makes life easy.
  • At the start of the measurement, I had observed each of the clocks’ outputs using my oscilloscope and compared them to a GPS-synchronized PPS signal. Clocks #0 and #7 drifted faster than the others but were still within the advertised specs (unfortunately I didn’t write down how fast they were drifting and have since forgotten). I adjusted the aging register until the short-term drift was minimal; the results are acceptable, though I note they drifted the most of any of the crystal-based clocks.
  • All ten clocks are part of a cheap module available on eBay from various Chinese sellers. The module comes with either a DS3231 or DS3231M chip (the sellers don’t sort them so you can get either type at random) of various vintages. The oldest I’ve seen is from 2006. None of the chips seem to be new, with various smudges and wear visible on the face of the chip, so they’re likely pulled from old equipment and reused on these boards. Even so, they work well.
  • Each board also comes with a 24C32A I2C EEPROM, which is nice, but not strictly necessary. I use them for storing the aging offset in case I remove the backup battery and want to tune it again without using the oscilloscope.
  • The boards also come with a holder for a backup coin cell battery. Critically, the boards are also wired with a 2N4148 diode and a 200-ohm series resistor feeding the positive terminal of the battery, presumably for use with a (not included) LIR2032 rechargeable coin cell. If you use a non-rechargeable CR2032 coin cell you must remove either the diode or resistor or else the circuit will try to charge the coin cell battery, which can damage the battery. I’ve removed the battery holder entirely from one or two other test boards and the charging circuit works well to charge a backup supercapacitor, but the charging circuit must be removed or disabled if you use non-rechargeable coin cell batteries.
  • The data here is just a brief summary; I have more detailed data in a spreadsheet that’s available upon request.

High Precision DS3231 Reads

My ensemble of DS3231 and DS3231M RTCs has been running since early September 2017 and I’m looking to start gathering data on their performance in the near future. Thus, I’ve taken one of the DS3231 RTCs from the ensemble (leaving ten) and done some experiments with it to figure out how best to read the time from the clock chips at the highest precision I can using a Raspberry Pi.

This is complicated by three issues:

  1. Although the DS3231 RTC has an internal 15-bit counter that counts individual crystal ticks, that counter is not accessible by the user. Instead, we can only access the timekeeping registers with a precision of one second.
  2. Due to weird historical quirks that trace back to the original MC146818 RTC used in the first PC/AT standard, the hwclock utility sets the RTC so there’s a half-second difference between the system clock and the RTC.
  3. With the reference NTP daemon running on a stock Raspbian system, the kernel enters an “eleven minute mode” where it will periodically set the RTC’s clock to the NTP-synchronized system clock. This is undesirable, but turning it off requires recompiling the kernel and I’m lazy.

First Issue

To address the first issue, I use the hwclock -c command, which repeatedly reads the timing registers on the RTC until the second changes. With the help of an oscilloscope and logic analyzer, I’ve confirmed that the seconds register (hex value 0x0, or “00h” in the notation the datasheet uses) is updated on the next read after the 1 Hz square wave output falls.

It’s worth quoting this part of the DS3231 datasheet:

On an I2C START or address pointer incrementing to location 00h, the current time is transferred to a second set of registers. The time information is read from these secondary registers, while the clock may continue to run. This eliminates the need to reread the registers in case the main registers update during a read.

This means that nothing weird will happen if the 1 Hz counter ticks in the middle of a read and the next read from the seconds register after the tick will have the latest value. Perfect. This means we can mark the new second with a precision of +/- 1 I2C read packet, which is ~1 ms when using hwclock to read the data. Not bad.

Second Issue

For the second issue, we need to account for a half-second offset between the system clock and the RTC. If we let the kernel automatically set the RTC using the “eleven minute mode”, the RTC is a half-second ahead of the system clock. The output of hwclock -c looks like this:

hw-time system-time freq-offset-ppm tick
1517492769 1517492768.514489 -3 -0
1517492780 1517492779.514081 -3 -0
1517492791 1517492790.513630 -3 -0
1517492802 1517492801.513253 -3 -0

That is, the RTC is 0.486747 ahead of the system clock. The reported precision is far too high: I’ve measured a difference of 3-4 milliseconds between the value reported by hwclock and difference between the RTC’s 1 Hz pulse and the UTC-aligned one-pulse-per-second (PPS) signal from a timing GPS receiver.

However, if we set the RTC manually using hwclock -w,the system clock is a half-second ahead of the RTC (at least until the eleven minute mode resets the RTC). Here’s an example:

hw-time system-time freq-offset-ppm tick
1517493232 1517493232.502916
1517493244 1517493244.502209 -59 -1
1517493255 1517493255.502850 -3 -0
1517493266 1517493266.502547 -11 -0
1517493277 1517493277.502220 -15 -0

Keep this in mind if you decide to do a similar test. For consistency, I used hwclock -w to set all the clocks in the ensemble simultaneously. One nice thing about the DS3231 and DS3231M is that it resets its internal 15-bit fractional seconds counter whenever the seconds register is written, so once set you only need to account for the half-second offset and not any leftover fractional seconds. (See the “Clock and Calendar” section of the datasheet: “The countdown chain is reset whenever the seconds register is written.”)

The RTC’s 1 Hz pulse goes high 500 ms after the seconds register is written, with the falling edge (which delineates the seconds) occurring 500 ms after that. Thus, the output of the 1 Hz pulse is synchronized to the seconds boundary (modulo the half-second offset discussed above) whenever the RTC is set.

This is rather interesting, as I had assumed the 1 Hz output from the RTC to simply be the buffered, divided-by-32768 output of the crystal with no additional processing, but it’s actually something the chip can adjust the timing of based on user input. That’s really cool and could come in handy when measuring the phase drift of two or more such clocks (keeping in mind that the 1 Hz pulse is aligned to the closest crystal tick).

As an experiment, I removed the battery from the RTC and reset it to factory defaults, enable the 1 Hz output, and measured its offset relative to the GPS PPS pulse (it was about 115 ms, but it was free-wheeling After setting the RTC with hwclock -w, the rising edge was within 1 ms of the GPS pulse. Yup, it works.

Let’s look at this visually (click to enlarge):

In this image, the RTC time is set using hwclock -w at the A1 marker (there’s a short burst of SDA/SCL data that’s a bit hard to see at this level of zoom). The RTC’s 1 Hz signal (“DS3231 SQW”) goes high 500 ms later, with the rising edge aligned with the GPS PPS marker (A2). At ~1.9 seconds hwclock starts repeatedly reading the RTC (the long burst of SDA/SCL data) to catch the boundary between RTC seconds, which occurs at the falling edge of of the 1 Hz signal — note the 0.5 second offset between the rising edge of the GPS PPS (which marks the UTC second) and the falling edge of the RTC pulse.

Third Issue

The solution to this issue is easy: I basically ignore it. I have two PCA9548A I2C multiplexers connected to my Raspberry Pi’s I2C bus, so I can programmatically switch between each RTC (all of which have the same fixed I2C address, necessitating the multiplexers). Thus, I can rapidly scan through each of the RTCs, gather data from each, and finally set the multiplexer to a position where there is no RTC.

The probability of the kernel setting the RTC on its eleven minute mode schedule during this quick burst of activity is non-zero but low enough that I don’t worry. If I wanted to turn off the eleven minute mode entirely, I could recompile the kernel with that option disabled, but I’m lazy.


Based on measurements of both the software and hardware timing, I can conservatively read the time from the RTC within +/- 10 ms. That’s both better than I expected and more than adequate for my testing. Going into this, I was expecting around 200-500 ms precision.

The major (and I use that relatively) issue I face is the half-second offset. Since I know I used hwclock -w to set all the RTCs simultaneously, I know they were all set 0.5 seconds behind the system time at the moment they were set. Also, since the output of hwclock -c is based on the tick of the RTC clock, the system clock would show as 0.5 seconds ahead. Later, when I measure the difference between the RTC and system clock I need to make sure to subtract 0.5 seconds from the reported system time to get a proper comparison.

Lastly, datasheets have a lot of interesting details that are really easy to overlook. Having a logic analyzer (I really like Saleae) and a cheap timing GPS with a PPS output (an old Motorola Oncore UT+ I use for my house NTP server; it’s old but keeps ticking along) makes it really easy and fun to explore these details. I enjoyed digging into the behavior of the hwclock software and DS3231 hardware and hope this information is of some use to you.

My DS3231 test setup

I wanted to test several DS3231 (M and non-M variants) boards for drift, so I mounted eleven of them (including one known-genuine DS3231M, the leftmost one on the front row, with a green bodge wire) to a breadboard, connected a regulated power supply (AMS1117) at 3.3V to both power rails, and made sure they all worked.

Eleven DS3231 (including 3 M variants) on a breadboard for testing.

Yup, they all work. The boards have either orange or red LEDs, so they emit a pleasing glow at night that prevents me from crashing into things in my office at home.

Why use 3.3V? One, it makes interfacing with the 3.3V I2C pins on a Raspberry Pi easy since I don’t need a level-shifter, and two, it minimizes drift in the event that I need to disconnect the power and have the clocks run on their coin cell backups. The CR2032 batteries have a nominal voltage of 3.0V, but all currently measure 3.3V (they’re brand-new, Energizer-brand cells from Digi-Key). The DS3231 datasheet says the drift can change by up to 1ppm/volt, so I want to minimize the voltage difference between the normal power supply and the coin cells.

To ease the comparison of drift, I want to ensure all the clocks start counting at the same moment. I could set them all one at a time, but this is complicated because (a) I don’t have an I2C multiplexer chip, and (b) setting them sequentially means they’re not all set at the same moment. It probably wouldn’t matter much in the long run, but it would make me happy to set them at the same time.

The DS3231 modules all have the I2C address of 0x68, and it cannot be changed. Normally, you cannot have multiple chips with the same address on the same I2C bus, as they’ll talk all over each other and the resulting signals will be garbage.

Fortunately, we don’t need the DS3231s to talk; they need only listen to the master and make the appropriate ACK/NAK signals as needed. They should all send the same ACK/NAK signals at the same time so, in theory, there shouldn’t be a problem.

Next, we need to worry about bus current. Each module has a 4.7k ohm pull-up resistor for the I2C bus. With eleven modules, that means the effective pull-up resistance is ~430 ohms. At 3.3V working voltage, a device would need to sink nearly 8mA to correctly signal a logic low. The Raspberry Pi I have can sink 16mA per GPIO pin, so that’s fine. The DS3231 datasheet says the IOL is 3mA, though I spoke with an engineer at Maxim Semiconductor and they said the absolute maximum current the process used on the chips is 10mA. 8mA is close to that limit, but the current would hopefully be spread across many devices and would only be for a few microseconds in total, so it should be fine.

I was satisfied I wasn’t going to blow anything up (and if I did, replacements are cheap), so I connected all eleven modules in parallel to the same I2C bus and commanded them to set their date and time to an arbitrary date in the past. If this was successful, I could send a command to read the time and, if all the modules had the same time, it would come through without an error. If things didn’t work, garbage would come in and I’d have to check them individually for the correct time. One read to all of the devices simultaneously, and I had valid data for that arbitrary time and date. Excellent. It worked!

Using the Raspberry Pi synchronized to a local NTP server (another Raspberry Pi running NTP with a GPS reference clock) within less than a millisecond, I send the command to set the date and time on all the modules to the current time on Friday 8 Sep 11:18:16 UTC 2017 (unix time: 1504869496). Reading the date and time from all the modules confirms they all have the correct date and time with no errors.

Now I’ll let them run for a while to see how they drift. A few have hand-tuned aging registers, so they should hopefully drift less than the others, while others use the default aging register of 0.


Major differences between the DS3231 and DS3231M RTC chips

As should be clear from one of my earlier posts, I’m really interested in clocks and precision timekeeping. In particular, I rather like the Dallas Semiconductor DS3231 series of temperature compensated RTC/TCXO (real-time clock/temperature compensated crystal oscillator) modules.

Recently, I had ordered several DS3231 boards from my regular eBay vendor in Shenzhen for some testing, only to find two oddities: first, the factory had evidently gotten an incorrect chip with the same sized 0.300″ SOIC package as the DS3231. This chip was the wholly-incompatible DS1315. It happens, particularly at this price point and via gray market suppliers. No worries, I contacted the seller and they sent me a replacement board.

Continue reading “Major differences between the DS3231 and DS3231M RTC chips”

A look inside the DS3231 real-time clock

Dallas Semiconductor, now owned by Maxim Integrated, is well known for making some excellent real-time clocks (RTCs). Take, for example, the DS1307: it’s simple, works with essentially any cheap 32,768 Hz watch crystal, is easily accessible over I2C, and is extremely power efficient (500nA current when running the oscillator on battery power).

As great as it is, the DS1307 has a major drawback: it relies on an external crystal and lacks any sort of temperature compensation. Thus, any change in temperature will cause the clock to drift. A 20ppm error in the frequency of the crystal adds up to about a minute of error per month. Not so great.

Fortunately, Maxim also offers the DS3231, which is advertised as an “Extremely Accurate I2C-Integrated RTC/TCXO/Crystal”. This chip has the 32kHz crystal integrated into the package itself and uses a built-in temperature sensor to periodically measure the temperature of the crystal and, by switching different internal capacitors in and out of the crystal circuit, can precisely adjust its frequency so it remains constant. It’s specified to keep time within 2ppm from 0°C to +40°C, and 3.5ppm from -40°C to +85°C, which means the clock would only drift 63 and 110 seconds per year, respectively. Very cool.

Continue reading “A look inside the DS3231 real-time clock”

Well, that was an interesting failure mode…

I have a bunch of eBay-sourced DC-DC converters that I use for a bunch of purposes around the house. Most are ordinary “LM2596” (in scare quotes, as most seem to be clones: they’re marked as LM2596 and generally work well, but have different switching frequencies. Supposedly this is an issue with such things.) buck converters configured as adjustable, constant voltage power supplies where the output voltage is set by a multi-turn potentiometer. Very handy.

Others can be used in either constant voltage mode or constant current mode. For the latter, a serpentine strip of PCB trace acts as a low-value sense resistor. An LM358 dual op-amp integrates the difference between the voltage across the trace and a voltage set by a potentiometer, with the output connected to the regulator’s feedback pin via an LED so you can tell when the regulator is in constant current mode. Another potentiometer sets when the “charging” LED lights up; this is purely cosmetic, and the LED turns off when the current through the regulator drops below the setpoint set by the potentiometer.

Caleb Engineering has an excellent teardown of such a regulator here.

Continue reading “Well, that was an interesting failure mode…”

My Daughter’s First Circuit

My daughter turns three in June. Yesterday, we were playing and an idea popped into my mind: she likes to help me build various electronic things at my desk, but she’s never really built anything of her own. I asked if she wanted to make something with me and she energetically agreed.

Here it is:

It’s a simple two-transistor astable multivibrator that alternates between the red and green LEDs at around 2Hz. Everything to the right of the red wire is pretty bog-standard: 5% tolerance 470 ohm current-limiting resistors for the LEDs and 100k ohm resistors for charging the 10uF capacitors. Two BC548 transistors do the switching. Some 24 AWG wire connects parts too far apart (or awkwardly placed) for component leads to reach.

In retrospect, I could have laid things out better, but she didn’t mind. The only major thing I’d change is using ceramic capacitors instead of electrolytic, as I’d like to keep this circuit around until she’s older and have it still work without the capacitors drying out, but I didn’t have any 10uF ceramics at hand. I’ll order some, have her pick them out, and swap them out.

On the left is a simple terminal block for connecting a power supply. I wanted the circuit to be robust in terms of polarity, so I used a bridge rectifier so it can operate regardless of how the DC power supply is connected (I could have added a filter cap so AC could be used too, but I don’t have any wall warts with AC out, and she likes batteries, so this was not a major design consideration). I could have used a cheap diode, but the bridge rectifier uses Schottky diodes and so drops only 0.6V compared to a 1N400x’s 0.7V, plus it means the circuit will work (rather than simply not be destroyed) regardless of how it’s connected, so that was an easy and robust choice.

A 50mA polyfuse provides protection from faults (important when using old cellphone Li-Ion batteries as a power source). All the exposed underside contacts of the unfused section (i.e. terminal blocks and rectifier) are liberally coated with hot glue for insulation, with the jumper wires on the top and bottom tacked down with hot glue as well. All solder and components are lead-free, with burrs and other sharp points on connections filed smooth for minimal danger.

My daughter loved picking the components out of the parts drawers, listened attentively while I explained what they did and how they work, and helped me put them in the correct places on the breadboard. After things worked and she (later) went to bed, I moved the same parts over to a protoboard for a bit more durability. Now she’s running around the house waving it (and the 1000mAh cellphone battery stuck to the bottom with double-sided tape) around, blinking it at her baby brother, and integrated it into playing with her other toys.

This makes me happy.

Looking at a TP4056 Li-Ion charger with a FLIR ONE thermal camera

I recently acquired a FLIR ONE thermal camera, which deserves a separate post reviewing it, but for now let’s look at the TP4056 Li-Ion charger with integrated protection circuitry.

This is a pretty bog-standard, dirt-cheap Li-Ion charger that works really well. It does what it says on the tin: CC/CV charging, with charging current adjustable by replacing a specific resistor, 5V MicroUSB input, and pads/holes to accept connections to the cell, the load, and the charging power source (if one doesn’t want to use the USB port). No complaints at all, and no surprises.I like that it has a battery protection circuit as well: the protection chip monitors the charging or discharging current and voltage, and protects the cell against overvoltage (e.g. from over-charging), undervoltage (e.g. from over-discharging), and over-current situations by switching off the MOSFET that connects the battery to the load and charging chip.The FET is arranged in a cool way such that, even if the over-discharge protection has tripped and the FET is open, you can trickle charge through the FET’s body diode at a very low rate in order to slowly charge the cell up without stressing it. Once it reaches the release voltage, the cell will charge at the normal speed.One of the main reasons I bought the FLIR ONE thermal camera is to observe various electronic devices I have and see how hot they get, where the heat is dissipated, etc. Since the TP4056 is a linear charger and produces a modest amount of heat while charging, I figured this would make a great first test. Here’s one of the images I snapped:

As you can see, the chip gets moderately toasty when charging at 1A, and I can’t hold my finger on it for a more than a second or two. This is a top view with the chip and other components visible to the camera. The TP4056 also has a thermal “radiator” (using the language in the datasheet) pad on the bottom that should be connected to a copper plane on the PCB. The board has a bunch of thermal vias under the chip to conduct the heat away to the other side and the backside of the board is about the same temperature as the front. Neat.

I foresee a lot of fun (and useful projects) with both the camera and the battery charger.

Note to self: HC-05 bluetooth-to-serial modules need a pull-up resistor on the TX pin

I ran into some trouble today getting an HC-05 bluetooth-to-serial module to communicate with my Trimble Resolution T GPS receiver.

The ResT will send some data automatically once per second, but needs to be polled to send other data. Lacking the polling packet, weird things happen.

Some devices have built-in pull-up resistors so the module works fine, but the ResT doesn’t. The HC-05’s TX pin is open-drain, so without a pull-up it does nothing, causing confusion. Putting a >1k pull-up to 3.3v on that pin works wonders.

See https://mcuoneclipse.com/2014/03/30/getting-bluetooth-working-with-jy-mcu-bt_board-v1-06/ for more details.