[mathjax][/mathjax]In this MCP7940 tutorial, we’ll start to explore the MCP7940 by setting the time and retrieving the time. We’ll also do all of this using the I2C Library instead of the Wire Library.

Objectives

  1. Determine how to turn the clock on.
  2. Map out the values that need to go into the appropriate registers.
  3. Convert values to and from Binary-coded Decimal (BCD).
  4. Write starting values into the date and time registers.
  5. Retrieve the current date and time.

Background
MCP7940 Functionality Overview
I2C Library Functions
I2C Signaling
How To Read A Datasheet
Microchip MCP7940 RTC Datasheet


Schematic
Education Shield – MCP7940 RTCC Subsystem


Setup
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, connect 5V to 5V, GND to GND, SCL to Analog 5, SDA to Analog 4 and MFP to Digital3.

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.


MCP7940 Tutorial: A Quick Rant

Before we get started on setting and retrieving anything time related, I want to clearly explain how to make the chip actually work. This is a common issue when dealing with ICs… you design your schematic, get a prototype board spun, write some basic test code and the blasted thing just… doesn’t… work. Invariably, there is some teeny little bit hidden away in the datasheet that is the “ON” bit or the “ENABLE” bit or “I/O” bit or something like that… essentially an on/off switch.

As an exercise, open up the MCP7940 datasheet, and try to find where it explains how to make the clock run. If you get frustrated, here’s the answer…

Determine how to turn on the MCP7940 functionality…

Hover to read answer

I have read through that datasheet, without exaggeration, at least a hundred times in preparation for writing these education modules, and to this day I have yet to see where that is clearly spelled out.

Anyway…


Binary-coded Decimal

Our date and time values are stored in Binary-Coded Decimal (BCD). Let’s examine the seconds register and see how we can convert values to and from BCD and regular decimal.

The seconds register is comprised of a set of three bits that make up the tens place of a decimal number and a set of four bits that make up the ones place of a decimal number. The most significant bit, the eighth bit of RTCSEC, has special powers which we’ll worry about below… for now, we’ll just label it as “x”. So, if you have the number 25, you would split it into 2 and 5, and then convert each into its equivalent binary value, and mash the two back together with the binary value for 2 on the left and the binary value for 5 on the right and spit that into the register.

The BCD value can be a little confusing, because you can’t simply process it as is. BCD Bx0100101 = 25, while the regular binary value Bx0100101 = 37.

The conversion back from BCD to regular decimal is pretty much the converse of that process. The register map tells us how many bits are assigned to the tens and ones places, so we just split, convert and recombine again… just in the other direction.


Convert 38 seconds to BCD…

Hover to read answer

Convert BCD Bx0010110 seconds to decimal…

Hover to read answer


So now that we know how to convert to and from BCD mechanically, how do we do it programmatically? It turns out to be pretty easy, actually, when you use the “modulo” operator. Instead of using the “/” you use “%” and what it does is return the remainder from your division operation instead of the whole number. By using both, you can extract the tens place and the ones place pretty easily. Then just use a little bit shifting…

To convert from BCD back to Decimal is preeeeetty much the converse. Take the binary value, split it into two separate variables and shift the MSB byte over to the left, then multiply the MSB Byte times ten and add it to the LSB Byte. You could take this code and eliminate the extra variables entirely by squashing everything together into one command, but it would be impossible to understand.

This code is only sending bytes back and forth, so if you wanted to use it on larger values like integers, you’d have to change the variable types to accommodate more than 8 bits.

Now, you could choose to do this mathematically instead, rather than using bit shifting. The lower four bits are equal to their value multiplied by 1 and the upper four bits are equal to their value multiplied by 16. Why 16? Because the first value in the upper byte is B00010000 = 16, so multiplying them by 16 is the equivalent of shifting the bits over four places to the left.

So, in order to convert from Decimal To BCD…

\[\mathtt{\left[\frac{value}{10}\right ]\times16 + \left(val\%10\right)}\]

and to convert form BCD back into Decimal…

\[\mathtt{\left[\frac{value}{16}\right ]\times10 + \left(val\%16\right)}\]

The choice is yours for how you want to do your conversion.


MCP7940 Tutorial: Configuration Planning

We’re going to start very simply: setting the time and retrieving the time. We won’t worry about trimming the oscillator, setting alarms or any of that stuff yet.

Looking at the register map, we see that the top of the register addresses are all time and date related. That’s where we need to start concentrating our efforts. To make it a little easier to read, I’ve removed all the non-time related bits from the following register map.

AddressNameBit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0
0x00RTCSECSTSECTEN2SECTEN1SECTEN0SECONE3SECONE2SECONE1SECONE0
0x01RTCMINMINTEN2MINTEN1MINTEN0MINONE3MINONE2MINONE1MINONE0
0x02RTCHOUR12/24AM/PMHRTEN0HRONE3HRONE2HRONE1HRONE0
0x03RTCWKDAYWKDAY2WKDAY1WKDAY0
0x04RTCDATEDATETEN1DATETEN0DATEONE3DATEONE2DATEONE1DATEONE0
0x05RTCMTHLPYRMTHTEN0MTHONE3MTHONE2MTHONE1MTHONE0
0x06RTCYEARYRTEN3YRTEN2YRTEN1YRTEN0YRONE3YRONE2YRONE1YRONE0

We have a few things in our favor when it comes to setting all those values. First, once we figure out how to set the values for one of these things, we’ll know how to set it for all of them. Second, the time and date registers read and write sequentially, just like the SRAM does, so we can start writing our seconds register and just flow into the next and the next, etc. Third, since we’re not really going to be going in and writing to these registers repeatedly, we can set the time and date bits first, and set everything else to zero, then go in and set any configuration bits afterwards before flipping the ST bit.

So what we’ll do, is start setting the seconds register by sending in our BCD value, then move down the register list in order, filling the appropriate values. Then we’ll set whatever configuration bits we need to, and after that, we’ll flip the ST bit to start the clock and then start printing out the time.

As usual, we start the sketch with our declarations, defining constants for our I2C and register addresses. We’re also creating a byte array called “timeStamp” that will hold the full set of time and date values we read and write to the MCP7940. Since there are seven registers, we size the array with seven positions.

There is so much magic happening in that Setup function because of the I2C library, it’s amazing. At the top, all we do is start our Serial connection, no big deal, but then we set four parameters for our I2C is going to function. We start it, we turn off the internal pullup resistors, we set the speed to 100kHz and we specify a bus timeout of 250ms. It’s so nice to know that you’re setting it assertively, rather than just expecting some backend library to contain the configuration without you knowing.

After that, we load the timeStamp array with a series of values. You can obviously see what time I was writing this education module 🙂

Then, in one command, we transfer all the data into the MCP7940 at once. BANG! Here’s what I2c.write(MCP7940_I2C, REG_RTCSEC, timeStamp, 6); looks like on the oscillocscope…

MCP7940 Set Time I2C Command
MCP7940 Set Time I2C Command

After we set the time, we call our chip initialization function that we’ll use to set some configuration parameters. Have a look at this…

The only two items we have in there, for now, are enabling or disabling the 12 hour clock mode, and turning the oscillator on or off. Now, we could have placed the values directly into the I2C functions, but I think this makes it more clear which settings you’re using at the expense of a little bit of memory space in the form of two bytes of variables, and a little processing time.

You set the parameter you want at the top as a single bit. Now, because the configurations appear inside the date and time registers, we have to retrieve the current value, so we can send it back, with the configuration bit changed as we desire. A quick check of the bit we’re trying to pass occurs with the if statement: 0x00 = bitClear, 0x01 = bitSet.

When we start adding more configurations for oscillator trimming, battery enable, etc, we can set all of that in here using the same pattern, and it will make it much easier to keep track of when you return to the code after a few months.


MCP7940 Tutorial: Retrieving and Displaying the Time

Now, after all that I wish I could give the big reveal on how we’re going to grab the time value, but really, the I2C library makes it really easy at this point.

The I2C command retrieves seven bytes of data, starting at the the location REG_RTCSEC. Because the MCP7940 is able to read from the registers sequentially, it just moves from one to the next to the next. Our function handily loads all of those values directly into the timeStamp array for us. We’re done using it to set the time, so we may as well use the same variable to hold the current time now.

Then, we send the values to the Serial monitor. It’s not entirely easy to do that though. Remember, our values have two that need to be changed before we can recognize them as human readable numbers…

  1. They contain configuration bits that need to be masked off.
  2. They are in BCD format.

In order to eliminate the configuration information, we AND the raw data in the timeStamp array with the appropriate mask.

After we mask off the unwanted bits, we can take the remaining data and send it to convertFromBcd and send it straight to the Serial monitor. The if statements check to see if the value returned is less than ten, and if it is, it appends the field with a “0”, since we’re used to seeing minute and second values represented as two digits.

Here’s the full code all assembled for you to refer to…

Previous Post
MCP7940 Tutorial 01: Functionality Overview
Next Post
MCP7940 Tutorial 03: Oscillator Trimming
You might also like
Menu