MCP7940 Tutorial 03: Oscillator Trimming


Now that we know how to get data in and out, we’ll get a little more into the advanced waters by looking at how to trim the oscillator correctly and utilize the battery backup feature.


  1. Learn which registers affect the oscillator operation.
  2. Trim the oscillator to correctly adjust the RTC timing.

MCP7940 Functionality Overview
I2C Library Functions
I2C Signaling
Microchip MCP7940 RTC Datasheet

Education Shield – MCP794N RTCC Subsystem

For this module, you’ll need the following equipment:

1. Mate the Education Shield with your Arduino UNO R3. If you’re using the MCP7940 Breakout Board with the Debounce Button Breakout Board, connect 5V to 5V, GND to GND, SCL to Analog 5, SDA to Analog 4 and MFP to Digital3 on the MCP7940 and connect 5V to 5V, GND to GND and B1 to Digital8.

2. Connect your Arduino to your USB cable, and use the Arduino IDE to upload the “Bare Minimum” sketch from the Basics section of the Examples.

Trimming the MCP7940 Oscillator

The MCP7940 doesn’t adjust for thermal differences out of the box, and every crystal oscillator has a “PPM” or “part per million” error factor… all of this means that a minute on your MCP7940 might not be exactly the same as a minute on mine. At least not until we do some calibration. There are two ways of accomplishing this calibration.

The first involves using a factory calibrated frequency counter and comparing the 1hz square wave output of the RTC versus the idea value counted by the freq counter and doing some calculations… essentially the difference between the two, multiplied by the length of a single clock pulse from the crystal gives you the deviation between the two. Now this is great if you have a $1000 frequency counter, but I do not.

The second method involves comparing the number of elapsed seconds on your RTC versus some other relatively standardized time source. In my case, I’ll use the time server run by the National Institute of Science at The NIST clock displays in your browser after compensating for network delays, so it’s pretty accurate. What we’re going to do is build a simple stop watch. Press the button and you’ll turn the oscillator bit on and the RTC starts running while record the current time on the NIST site. Wait a few hours and press the button again to stop the oscillator and display the current time in the serial monitor. Then you take the current NIST time and use some calculations from the datasheet to figure out your TRIMVAL.

\mathtt{PPM} &= \mathtt{\frac{SecDeviation}{ExpectedSec}\times1000000} \\
\mathtt{TRIMVAL\left ( bit6:0\right )} &= \mathtt{\frac{PPM\times32768\times60}{1000000\times2}}\\

So the first thing we need to do is get our code ready to build a stop watch. The actual time we start with isn’t that important, all we really care about is how many seconds pass between the first button press and the second one. For grins, we’ll also capture how many seconds pass for the Arduino clock by using the millis function.

Here are the design goals…

  1. Pressing the DATA button starts the stopwatch and displays the current time in the serial monitor.
  2. The sketch will maintain a counter for the amount of times millis() advances by 1000.
  3. Pressing the DATA button again stops the stopwatch and displays the current time again, the amount of seconds that have passed and the value of the millis counter

For the stopwatch, our code won’t vary dramatically from what we did in the last module, with the exception of a little math and a little bit of button logic.

The only real additions to the declarations section is the timerButton variable showing that we’ll use the DATA button on the Education Shield as a digital input, and an oePinvariable that will allow us to disable the shift register outputs… unless you want to enable them and have the LEDs do something. My code will just leave oePinhigh.

In the setup function, we add pinMode entries for timerButton and oePin, and set the oePin state to HIGH, disabling the shift register outputs.

In the init_MCP7940() function, we need to make sure we set the startClock bit to 0, this way the chip will start up with the oscillator disabled. It should only start running when we press the button.

The way we’re going to start the oscillator is with a simple function, that mimics the functionality of the initialization function. The only difference is that this function will accept a parameter, “oscState” which will tell the function whether to turn the oscillator on or off

To allow us to verify calculations, we’ll display the time when we both start and stop the clock, but the loop is going to be messy enough as it is, so we’ll move that into it’s own function, and add our two BCD conversion functions as well. Both of these functions are covered in detail in the MCP7940 Tutorial – Setting and Getting Time.

That’s all the preliminary stuff out of the way. Let’s tackle the final hurdle, the loop. The trick with the loop, is making sure we’re verbose in our communications through the serial monitor, so we can make sure we know what’s happening, but, as we’re measuring the differences in seconds, we don’t want to alter things too drastically by placing Serial statements all over the place, because they are slooooow.

The way we’ll map out the functionality is like this…

  1. Press the button once, start the timer, record the starting timestamp in an array
  2. Press the button again, stop the timer, record the ending timestamp in an array

Much like we usually check to see if this is the first time the button press has been detected (so we don’t rapidly cycle through the function when the button is held down for longer than 15 or 20 µS), we’ll need to make sure we check if the timer is already running, so we’ll create a variable for that. We could do it with an I2C call to the REG_RTCSEC register, but this is faster.

The only thing we’ll need to do manually, is make sure we write down the current NIST time somewhere when we start and stop the clock so that we have something to compare it to. We could probably do this via Processing, but I’ll leave that up to you to figure out 🙂 Once we have our beginning and ending timestamps, we can go to the Time Duration Calculator to find out the elapsed seconds. I’m sure there’s an efficient algorithm that could be used to do the math inside the sketch, but I can’t seem to wrap my brain around it at the moment 🙂

Here is the code to measure the oscillator functionality in full. In the next step, we’ll adjust the oscillator using this measurement with the formulas above.

Running the Numbers

Ok, I ran my test overnight, and came up with the following result…

MeasurementStart TimeStop TimeElapsed Time in Seconds
ARDUINO01:00:01 28/03/201508:56:03 28/03/201528562
NIST23:22:00 30/03/201507:18:00 31/03/201528560

Remember, the wacky start time for the Arduino is just because it’s arbitrary, all we need it to do is count seconds, not display the accurate time of day.

Using the formulas at the top of the page, we have a SecDeviation of 2, with an ExpectedSec of 28560. That provides us with a PPM of 70.03.

Result of Overnight Test 01

\mathtt{70.03} &= \mathtt{\frac{2}{28560}\times1000000} \\
\mathtt{TRIMVAL\left ( bit6:0\right )} &= \mathtt{\frac{70.03\times32768\times60}{1000000\times2}}\\
\mathtt{TRIMVAL\left ( bit6:0\right )} &= \mathtt{68.84}\\
\mathtt{TRIMVAL\left ( bit6:0\right )} &= \mathtt{Bx1000101}\\
\mathtt{TRIMVAL\left ( bit6:0\right )} &= \mathtt{0x45}\\

The “x” in the resulting byte is the sign bit for the entire thing. If you place a 1 in that position, it will add clock cycles to compensate for a slow running clock. In my case, it’s running fast so I will place a 0 in there to adjust for a fast running clock.

Trimming the MCP7940 Oscillator

You would think that the way we’re altering our RTC is very subtle… we’re just shaving a couple of oscillations off from something that’s hammering away 32768 times a second. Well, there are two ways of setting up the trimming. The first way, standard trimming, adds or subtracts your TRIMVAL, up to 254, of those 32768 cycles per minute. However, there is also the ability to perform a very rough adjustment called a “course trim mode”, which will also add or subtract up to 254 clock cycles, but does it 128 times every second! The datasheet basically says, “we can’t think of any reason why you’d want to use course trimming, but it’s there if you want it.” We’ll stick to the standard trimming.

We want to do two things, first, we want to ensure that course trimming is OFF in the REG_CONTROL register (although go ahead and turn it on and see what happens), and then we want to upload our TRIMVAL into the REG_OSCTRIM register. These just require a modification to the init_MCP7940() function, so I would recommend changing it in the stopwatch sketch we wrote above, so you can immediately run another test with a trimmed clock and see if your device is matching more closely to the NIST clock.

We add variables to hold our CRSTRIM set/clear bit (clearing the bit to disable course trimming), and the TRIMVAL we concocted with the forumula. At the top of the initializations, we quickly set the entire Control register to zero, just to be safe and then add similar code to the original initialization to write the crsTrim and trimVal variables to their appropriate registers. Now when I rerun the tests, I should be much closer, and hopefully dead on, with the NIST clock.

Result of Overnight Test 02

MeasurementStart TimeStop TimeElapsed Time in Seconds
ARDUINO00:00:00 31/03/201513:55:00 31/03/201550100
NIST20:06:00 31/03/201510:01:00 01/04/201550100

The results of the second test confirm that, at least over a nearly 14 hour span, we don’t deviate by even a single second. Longer tests would allow you to hone in with even greater accuracy, but in the end, it’s only 2 – 254 clock cycles per minute that you can adjust, so at some point, you’ll get as accurate as you can… which is still pretty accurate.

The fact is the single thing with the greatest affect on your oscillator’s accuracy is the temperature in which it is running. Unfortunately, Microchip hasn’t published any documentation with a curve showing what the change is that you can expect, so it would be up to you to determine what the change would be. If you have the chip running in a relatively constant environment, inside a living space for example, then a single calibration would be fine, as it won’t fluctuate much. However, if you’re putting the chip in a car, for example, where it is subjected to far greater thermal changes, then you would need to characterize the trim value necessary for a variety of temperature bands.