Bitmask hour byte rtc [SOLVED]

Hello friends,
I'm having some confusion when working with my clock, specifically the hour byte. It works fine except when it turns over from 1900 to 2000, the hour byte goes to "0". My question would be do we mask the bit when writing the byte, reading it, or both? I'm not sure where to begin on this so I thought I might start with this.

Wire.write(decToBcd(0x13)) & 0x7F;
hours=Wire.read() & 0x7F;

Bit 7 of the hour byte is MilTime==1 and bit 5 is pm==1. I dont think it matters which clock because I'm sure this applies to all of them, but just to be sure, datasheet here (corrected link ) http://www.intersil.com/content/dam/Intersil/documents/isl1/isl1208.pdf . Runs in 12hr mode fine, just 24hr mode where I'm having problems.

Thanks in advance
Louie

Not enough information - can you post the whole sketch?

The register is storred in BCD. Sounds like you are masking off the top three bits so the value can't exceed 0x1F. When it tries to roll from 0x19 (hour 19 in BCD) to 0x20 (hour 20 in BCD) it gets clipped and comes out 0x00. Since the 12-hour format doesn't use that 6th bit it works OK.

I was reluctant to post the whole code because of the state of disarray its in due to me trying things to correct the issue, but I will. John, thats exactly whats wrong, I'm just unsure of how to correct it.

//device isl1208 address 0x6F
#include <Wire.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
byte seconds, hours, minutes, date, months, years, statusReg;

void setup () {
  pinMode(2, INPUT);
  digitalWrite(2,HIGH);
  attachInterrupt(0, alarmTriggered, FALLING); 
  interrupts();
  delay(50);
  Serial.begin(9600);
  Wire.begin();

  Wire.beginTransmission(0x6F); 
  Wire.write(0x07); //status register
  Wire.write(0x90);  //
  Wire.endTransmission();

  /*readStatusReg(statusReg);
  Serial.print(statusReg,HEX); //should be 144/0x90
  Serial.println("h - status register");*/

/*
  //alarm setup
  Wire.beginTransmission(0x6F); 
  Wire.write(0x08); //alarm register
  Wire.write(0xD0); 
  Wire.endTransmission();
  Wire.beginTransmission(0x6F); 
  Wire.write(0x0c); //seconds alarm register
  Wire.write(0xB0); //secs
  Wire.endTransmission(); */
  
   
  
// set time
   Wire.beginTransmission(0x6F); 
   Wire.write(0x00); //seconds register
   Wire.write(decToBcd(55)); //secs
   Wire.write(decToBcd(59));//min 
   Wire.write(decToBcd(0x17)) | 0xA0;//& 0x7F;

   Wire.write(decToBcd(23)); //date 
   Wire.write(decToBcd(02)); //month
   Wire.write(decToBcd(15)); //year
   Wire.endTransmission();
   Wire.beginTransmission(0x6f);
   Wire.write(0x07); //status reg addr
   Wire.endTransmission();
   readStatusReg(statusReg);  
   
}  

void loop () {
  Wire.beginTransmission(0x6f);
  Wire.write(0x00); //status reg addr
  Wire.endTransmission();
  Wire.requestFrom(0x6f,6); // now 6 bytes of data...
  seconds=Wire.read();
  minutes=Wire.read();
  hours=Wire.read();// & 0x5F;
  date=Wire.read();
  months=Wire.read();
  years=Wire.read();
  Serial.println(hours,BIN); //debug
  hours=bcdToDec(hours);
  Serial.println(hours,BIN); //debug
  minutes=bcdToDec(minutes);
  seconds=bcdToDec(seconds);
  Serial.print(hours);// & 0xa0;
  Serial.print(":");
  Serial.print(minutes/10%10);
  Serial.print(minutes%10);
  Serial.print(".");
  Serial.print(seconds/10%10);
  Serial.println(seconds%10);
  Serial.print(bcdToDec(months));
  Serial.print('/');
  Serial.print(bcdToDec(date));
  Serial.print('/');
  Serial.println(bcdToDec(years));
  delay(1000);

}

byte readStatusReg(byte val){

  Wire.beginTransmission(0x6f);
  Wire.write(0x07); // read status reg to clr bits
  Wire.endTransmission();
  Wire.requestFrom(0x6f,1); // now get the byte of data...
  statusReg=Wire.read();
  return(statusReg);

}


byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

void alarmTriggered()
{
  Serial.println("** ALARM WENT OFF! **");

}

Are you interpreting a chip like DS1307?

The DS1307 can be run in either 12-hour or 24-hour mode. Bit 6 of the hours register is defined as the 12-hour or 24-hour mode-select bit. When high, the 12-hour mode is selected. In the 12-hour mode, bit 5 is the AM/PM bit with logic high being PM. In the 24-hour mode, bit 5 is the second 10-hour bit (20 to 23 hours).

You read bit 6 to decide what to do with bits 5/4.
If 6 is low, then 5/4 are interpreted as 0,1,2, and bits 3-2-1-0 will read out as 0 to 9.
If 6 is high, then 4 will be 0 or 1, 5 can be used for an AM/PM indicator, and bits 3-2-1-0 will read out as 0 to 9.

You have not linked the data sheet for the RTC you are using, and the exact register structure, but if the hour register is equivalent to the DS 1307, then the read mask should be OX3F to get the first 6 bits (0>5) which contain the bcd hour value.

Edit --typo 0x3F Thanks CrossRoads.

No, it's 4 bits for the ones of hours, 0 to 9, then 1 bit for tens of hours 12 hour mode, or 2 bits for tens of hours in 23 hour mode. Just like the extract of datasheet I posted says.

The mask would also be 0x (zero), not Ox (letter O).
So one could do this:
tensHours = (hours & 0x30) >> 4; // two bits for 24 hour mode
tensHours = (hours & 0x10) >> 4; // one bit for 12 hour mode

No I'm not interpreting to the 1307, it's my confusion thats confusing everyone I think, and oh noobie strikes again, the datasheet came up different, but the registers are the same for this device (same company). I believe my confusion got in the way perhaps. If I'm not mistaken, bit 7 should be high for 24 hr mode?

datasheet = If the MIL bit of the HR register is “1”, the RTC uses a
24-hour format. If the MIL bit is “0”, the RTC uses a 12-hour
format and HR21 bit functions as an AM/PM indicator with a
“1” representing PM. The clock defaults to 12-hour format
time with HR21 = “0”.

So I believe I need to mask bits 7 and 5?

datasheet corrected link:
http://www.intersil.com/content/dam/Intersil/documents/isl1/isl1208.pdf

Wire.write(decToBcd(0x17)) | 0xA0;//& 0x7F;

0x17 is decimal 23 so decToBcd(0x17) is 0x23. If you or in 0xA0 you get 0xA3 which is hour to 23 (and 24-hour clock). Perhaps you meant:

Wire.write(decToBcd(17)) | 0x80; // Set hour to 17:00 military time

This code might cause a problem since readStatusReg() already sends 0x07:

  Wire.beginTransmission(0x6f);
  Wire.write(0x07); //status reg addr
  Wire.endTransmission();
  readStatusReg(statusReg);

You are picking up the MIL bit here:

  hours = Wire.read(); // & 0x5F;

Perhaps you meant:

  hours = Wire.read() & 0x3F;

Yes, it appears bits 6 & 7 are swapped between the 2 devices.
Still need to interpret 4 bits and 1 or 2 bits?

If the MIL bit is “0”, the RTC uses a 12-hour
format and HR21 bit functions as an AM/PM indicator with a
“1” representing PM. The clock defaults to 12-hour format
time with HR21 = “0”.

Only need 4 bits to count from 1 to 12, and 5 bits from 0 to 23, so one can assume that 4 +1 and 4+2 format is still used.

Hi John,
I made all the changes you suggested, but my output is as follows:

25:59:55

with 25 being the hour. Can't seem to wrap my head around this. I'm feeling Johns first post, and Crossroads posts is wherein my answer lies.

Yes, if you want to be in the 24 hour mode then bit 7 of the hour register should be set to 1. Then to read from that register in 24 mode the mask is 0x3F for the first 6 bits which you pass into your bcdToDec function.

ok Cat, I'm gonna try that. Check this board out, I made it last night in my basement, its running Uno optiboot, 5volts with crystal (no resonator) and I only brokeout the pins I wanted for my project. Pretty nice eh? BUT I CANT SET MY CLOCK BECAUSE IM A NOOB!!!! :smiley: Help me guys!!! I also appreciate the time everyone is giving me on this.

When you are setting the time, change

Wire.write(decToBcd(0x17)) | 0xA0;//& 0x7F;

to

Wire.write(decToBcd(0x80 |(the hour you want));

If for example the hour you want is 23 the line will be

Wire.write(decToBcd(0x80 |0x17));

After conversion, the hours register should be 10100011

Didn't work Cat, got 99 for output. But let's break this down, in theory

Wire.write(decToBcd(19)) | 0x80;

is setting my bit 7, miltime bit. And my output is correct

19:59:55
19:59:56 etc

to read this, I'm clearing the bit 7 with 0x7F (01111111) correct? But once output rolls from 19:00 to 20:00 its

19:59:59
0:01:00
Wire.write(decToBcd(19)) | 0x80;

That puts the "| 0x80" part outside the call to .write() so that bit is set on the value returned by the function and the result is never used. Maybe it rolls from 19:59:59 to 0:00:00 because 20:00:00 isn't valid for 12-hour time. That should probably be:

Wire.write(decToBcd(19) | 0x80);

You could improve readability by using some named constants like:

const unsigned char RTC_ADDRESS = 0x6F;
const unsigned char MIL_TIME_BIT = 0x80;
Wire.write(decToBcd(19)) | 0x80;

I was thinking that the 0x80 which is setting bit 7 should be within the Wire.write() and the dectoBcd() which was probably incorrect since its not decimal and I don't know what the conversion math does with it. Edit-- I think John has the correct arrangement of the brackets in the post above this one.

Until we get this straight, let's stop using both dec and hex expressions mixed together.

We know the register wants to wind up like 10011001 which is the bcd representation of 19hrs i.e. 1x10 + 9 with the top bit set to mil time.

10011001 is decimal 153 or 0x99.

Try Wire.write(dectoBcd(153)) to set the time to 19 hrs with the 24 hr bit set to 1..

You do not clear the top bit to read this. If you do not leave it alone, you will come out of mil time. Your are only going to read the lowest 6 bits of the hours register with the 0x3F mask. Leave the top two bits alone.

SOLVED. I appreciate all the posts, I've learned a bit from each one, which is after all why we are all here. To advance a really fun board (Arduino) with knowlegde!!!

The fix was Johns idea. This is what is working for this chip now:

Wire.write(decToBcd(19) | 0x80);

and

hours=bcdToDec(hours & 0x7f);

Now if I could only write a library for this chip LOL.

But thank you all for you time and help today.
Cheers,
Louie