Converting I2C RTC register values to actual time

Hey all,

I'm in the middle of writing a library to read the time from a TI BQ32000 I2C real-time clock. I've successfully read and written to the registers, but I can't figure out a simple way to convert the read values to a readable time format.

Let's just deal with the seconds register for now, as once I figure out how to handle this, I can apply the same to all other registers.

The seconds register on the BQ32000 is set up the same way as the DS1307, the register bits are:

|ctrl|   |   10 seconds     |    |       1 second           |
  D7      D6      D5      D4      D3      D2      D1      D0

In this form, 59 would be (D7 is always 0): 0-101-1001 where 101 represents "5", and 1001 represents "9", thus 59. I am able to read the register values just fine, however I am a little stuck at how to convert them to actual seconds. What I mean is, if I read this value straight from the clock and convert it to decimal, I will obviously not get a correct value for seconds as 01011001 is 89 in decimal, not 59.

I thought of mapping each and every value from 0-000-0000 to 0-101-1001 to it's equivalent seconds value, but that seems like the wrong way to do it. Then I figured if I could read the register bit by bit, storing each bit in its own variable and using that to calculate the seconds, but even that seemed a little "bit" unnecessarily complex.

Here's the code I'm using to test the chip, although I don't think it's necessary to solve this problem.

#include <Wire.h>
#define CLOCK_ADDRESS B01101000

byte secs;

void setup()
{
   Serial.begin(9600); //Start serial comms
   Wire.begin();       //Start I2C bus
  
}

void loop()
{
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.send(0x00); //Set pointer to register 0 (seconds)
  Wire.endTransmission();
  
  
  Wire.requestFrom(CLOCK_ADDRESS, 1);
  secs = Wire.receive();
  Serial.println(secs, BIN);
  delay(1000);
  
}

Thanks!

P.S. I'm writing my own library to get a better understanding of how to use the I2C bus. I hate having to choose my parts based on what libraries are available, so I'm learning how to write my own.

There are a couple of ways to do this. You could define a union with a structure containing bit-fields. You could hack up some sort of BCD decoding, perhaps from an existing library. The easiest to understand way is to use binary operators (&) and shifting (>>) to extract the values.

ones = secs & B00001111;
tens = (secs >> 4) & B00000111;
realsecs = tens * 10 + ones;

HTH,

Very cool. I had no idea about binary operators, will be reading up more on this for sure.

Interestingly, I seem to have stumbled upon a solution for this problem by only changing one word in my existing code. I changed

Serial.println(secs, BIN);

to

Serial.println(secs, HEX);

Not sure if this is a feature of the RTC or what, but now I correctly see the seconds values from 0-59. Any ideas on why this worked?

Thanks.

EDIT: Nevermind, worked with seconds but not minutes.

I will obviously not get a correct value for seconds as 01011001 is 89 in decimal, not 59.

But in hex, the bit pattern 01011001 is 59, so printing it as hex will work. Except that if the high order bit is set (the |ctrl| bit), then the 5 would print as D. To avoid this you have to use a logical operation to clear that bit:

Serial.println(secs & 0x7F, HEX);

As long as you're only printing the values, this will work. If you need to do arithmetic, for example the difference between two dates or times, then you do have to convert the number to binary, or find a library to do it for you.

Pete

AAAAAH I understand now!!! This makes sense because the |ctrl| bit (which is actually a failure detect bit in the minutes register) is set to 1 which explains my crazy values for the minutes register.

So using Serial.print(secs & 0x7F, HEX)
is telling it to print only the last seven bits since 0x7F == 01111111b? Would I use
Serial.print(secs & 0x3F, HEX) if I only wanted the last 6 bits?

Thanks a bunch Pete.

So, having figured out how to use only selected bits for conversion, it would seem I've got my problem worked out. Unfortunately, that isn't the case.

For some reason, the minutes register on the chip starts with a value of 9, i.e. the initial value of the register is for some reason 1-000-1001. I can not figure out why this is, but when 59 seconds pass after I give power to the RTC (no backup battery for testing), the minutes register resets to 1-000-0000 and starts counting normally after that.

Any ideas on why this is happening?I'll try writing 1-000-0000 to that register in the setup function and report back...

EDIT: This seems to have worked.
EDIT2: NOPE, it didn't

The value of the registers may not be defined when you first power it up. When first powering it up you should set the clock to a known value, such as zero, or today's date/time. It would be best to have a backup battery connected and then set the clock. After that you should get consistent values.

Pete

OK, I will try that. Pete, can you please confirm that I've correctly understood how to use the "& 0xXX" command as described in my earlier response to you?

Thanks

if I only wanted the last 6 bits?

Yes. (Sorry, I missed that one).

Pete