DS1307 12 hour mode

mproia

Member
Hello all,

I gotten my DS1307 hooked up to my 28X1 and it is telling time on my LCD rather nicely but I would like it in 12 hour format and I do not know how to do this. I know you have to change bit 6 of the hour byte but I am lost on how to do this. I have tried to find the answer on my own but I have had no luck. So if someone could help me out I would really appreciate it.

Cheers
 

westaust55

Moderator
This would normally be done at the time you write the setup time and date to the DS1307.

Yes you are correct and you need to used bit6 to define 12 vs 24 hour mode.
Also bit 5 when in 12 hour mode defines if it is AM or PM.

The table in the DS3107 datasheet is a bit confusing. I have some hand written notes in my copy whihc indicate:

bit 6 = 0 for 24 hr and 1 for 12 hour.
bit 5 = 0 for AM and 1 for PM.

You do not say whether you have the DS1307 chip in an AXE033 LCD module or as a separate chip. Please advise.
If in the AXE033 are you using serial or i2c comms?
 

westaust55

Moderator
okay. Try this.
Hopefully makes sense and you can adjust to your local time zone and date

Code:
; =================================================
;   File....... 
;   Purpose.... Set the Real Time Clock
;   Author..... Westaust55
;  ===============================================
;
;
; -----[ Initialization ]------------------------------------------------------
;
Init:    I2CSLAVE %11010000, i2cslow, i2cbyte
         PAUSE 10
;
;
;
; -----[ Program Code ]----------------------------------------------------
;
;                    sec  min   12A-hrs   day  date mon  year  ctrl
Main:	  WRITEI2C 0, ($10, $12, %01010010, $02, $23, $06, $08, $10)
;                               
;%01010010
;  |||____   
;  ||| |
;  ||| +--- units digit for hours = 2 
;  ||+----- tens  digit for hours = 1 
;  |+------ 0 = AM  and 1 = PM when in 12 hr mode when in 24hrs mode used as 2nd bit for tens of hours
;  +------- 1 = 12Hrs mode   0 = 24 hrs mode

        PAUSE 10
;       
           END
;
 

mproia

Member
Hi Westaus55,

Thank you for the code. I downloaded it into my 28X1 and made the necessary changes to the sec, min, day, date, month and year but left the hour code as you wrote it to see what it would have come up as. I was assuming it would display 12 AM but all I saw in the hour position was a lower case r. I'm confused. Also how do you display whether it is AM or PM?

Thanking you in advance.
 

mproia

Member
Okay my fault on the lower case r thingy, please ignore. Programming error.

When I straightened that out I placed in this code for the hour byte %01100100. I was hoping that it would display a 4 in the hours position but I got 64? :confused:

Here is a snippet of code to display the hours on my LCD. Hour is defined as

symbol hour = b2

bcdtoascii hour,b8,b9 ' Hours
serout 7,T9600_8,(254, 132, b8,b9,":")

Thanks
 

westaust55

Moderator
Can you please post your entire program code so we can see how you are setting up the DS1307 and reading from it, etc.

About to dash for the door . . . . but others may help during the day as well.
 

KIGX

Member
As westaust55 requests: Put your entire code up, I wonder if your readi2c statement is correct.
 

mproia

Member
Hello All,

Here is the code I am using now:



setfreq m8 'you need to set freq. to 8hz to get a 9600 baud

Main:

'DS1307 CODE

pause 2500


symbol sec = b0
symbol minute = b1
symbol hour = b2
symbol day = b3
symbol date = b4
symbol month = b5
symbol year = b6
symbol control = b7
symbol fullyear = w6


serout 7,T9600_8,(254, $01) 'Clear Screen

hi2csetup i2cmaster,%11010000,i2cslow_8,i2cbyte


Timedate:

hi2cin 0, (sec,minute,hour,day,date,month,year,control)



'Day Of The Week Conversion

bcdtoascii day,b8,b9 ' Day of the week

If b9 = "1" then serout 7,T9600_8, (254, 128, "Sun")
elseif b9 = "2" then serout 7,T9600_8, (254, 128, "Mon")
elseif b9 = "3" then serout 7,T9600_8, (254, 128, "Tue")
elseif b9 = "4" then serout 7,T9600_8, (254, 128, "Wed")
elseif b9 = "5" then serout 7,T9600_8, (254, 128, "Thu")
elseif b9 = "6" then serout 7,T9600_8, (254, 128, "Fri")
elseif b9 = "7" then serout 7,T9600_8, (254, 128, "Sat")
EndIf

'Time

bcdtoascii hour,b8,b9 ' Hours
serout 7,T9600_8,(254, 132, b8,b9,":")


bcdtoascii minute,b8,b9 ' Minutes
serout 7,T9600_8,(254, 135, b8,b9,":") '


bcdtoascii sec,b8,b9 ' Seconds
serout 7,T9600_8,(254, 138, b8,b9)


'Date

bcdtoascii month,b8,b9 ' Month
serout 7,T9600_8,(254, 192, b8,b9,"/") 'Sets Date to line 2 position 1

bcdtoascii date,b8,b9 ' Date
serout 7,T9600_8,(254, 195, b8,b9,"/") 'Sets Date to line 2 position 4

bcdtoascii year,b8,b9 ' Year
serout 7,T9600_8,(254, 198, b8,b9) 'Sets Year to line 2 position 7


goto Timedate


The problem seems to be in the 10 digit


Thanks for your time
 

BCJKiwi

Senior Member
Firstly,

Move the symbol statement lines and the line
hi2csetup i2cmaster,%11010000,i2cslow_8,i2cbyte

before the Main:
These are one-off set up procedures to be run only once per power on.

Secondly;
Please show the code used to configure the clock.
I put this into the program immediately after the hi2csetup line and then you can rem it out once the clock is set and download the program again (you have the clock powered up all the time right?).
 

mproia

Member
Here is the altered code with the DS1307 time set line included.


setfreq m8 'you need to set freq. to 8hz to get a 9600 baud

symbol sec = b0
symbol minute = b1
symbol hour = b2
symbol day = b3
symbol date = b4
symbol month = b5
symbol year = b6
symbol control = b7
symbol fullyear = w6


serout 7,T9600_8,(254, $01) 'Clear Screen

hi2csetup i2cmaster,%11010000,i2cslow_8,i2cbyte


Main:


' To sets clock in 12 hour mode


'hi2cout 0, ($00, $07, %01110010, $02, $17, $03, $09, $10)


'DS1307 CODE

pause 2000


Timedate:

hi2cin 0, (sec,minute,hour,day,date,month,year,control)



'Day Of The Week Conversion

bcdtoascii day,b8,b9 ' Day of the week

If b9 = "1" then serout 7,T9600_8, (254, 128, "Sun")
elseif b9 = "2" then serout 7,T9600_8, (254, 128, "Mon")
elseif b9 = "3" then serout 7,T9600_8, (254, 128, "Tue")
elseif b9 = "4" then serout 7,T9600_8, (254, 128, "Wed")
elseif b9 = "5" then serout 7,T9600_8, (254, 128, "Thu")
elseif b9 = "6" then serout 7,T9600_8, (254, 128, "Fri")
elseif b9 = "7" then serout 7,T9600_8, (254, 128, "Sat")
EndIf

'Time

bcdtoascii hour,b8,b9 ' Hours
serout 7,T9600_8,(254, 132, b8,b9,":")


bcdtoascii minute,b8,b9 ' Minutes
serout 7,T9600_8,(254, 135, b8,b9,":") '


bcdtoascii sec,b8,b9 ' Seconds
serout 7,T9600_8,(254, 138, b8,b9)


'Date

bcdtoascii month,b8,b9 ' Month
serout 7,T9600_8,(254, 192, b8,b9,"/") 'Sets Date to line 2 position 1

bcdtoascii date,b8,b9 ' Date
serout 7,T9600_8,(254, 195, b8,b9,"/") 'Sets Date to line 2 position 4

bcdtoascii year,b8,b9 ' Year
serout 7,T9600_8,(254, 198, b8,b9) 'Sets Year to line 2 position 7


goto Timedate
 

mproia

Member
And yes the power is always on. I have a 4.5 volt battery supply when the system is on and there is a battery backup for the DS1307 when I shut the system down.

Thanks!
 

BCJKiwi

Senior Member
Hmm

Run it in the simulator continuously for a while - seems to be OK.

Can you be a little more specific about when and where the problem arises.
 

mproia

Member
When I run the exact program that I have shown in this post in simulation it will run fine but will be in 24 hour format not the 12 hour format as I have programmed it to be by using Westaus55's (thank you again) code. I believe this is because the simulator uses the clock off my computer (which is in 12 hour format) and will only display in 24 hour format. When I load this exact same program into my 28X1 the hour it displays is 72. See the attached picture of my LCD display after I loaded the program into my picaxe. :confused:

Any thoughts?
 

Attachments

KIGX

Member
I have been using a DS1307 in 24 hr mode because that is what I wanted and I have not tried 12 hr mode. Thinking a bit about it though, when the DS1307 returns to you the data in the 'hour' register isn't it going to return to you the data with bit 5 set as a 1 or 0 depending on whether or not it is am or pm and therefore you can't use the entire hour byte as a 'number' you would have to separate out the am, pm part of it....?

Edit: For the hour byte, you set bits 7, 6, 5, 4 to 0111 which = 7 and that is what it returns to you and bits 4, 3, 2, 1 are 0010 which = 2.

Edit 2: My binary math is a little rusty and I still haven't done this, but:

if hour_byte >= 96 then it is pm ---- bit 6 is always a 1, when it is pm bit 5 is also a 1 -- = 96
Then you need to blow off the 3 highest bits to get rid of bits 5 and 6 and then return the remaining bits back to where they belong:
hour_byte = hour_byte * 8 / 8
Now read hour_byte as a BCD
 
Last edited:

leftyretro

New Member
I use my DS1307 in 24 hour mode, however if and when need to display time somewhere in AM or PM mode I just add a few lines of code to subtract 12 if hours are greater then or equal to 12 and flip a software AM/PM flag, that way I only need one routine for setting and reading from the RTC.

Lefty
 

mproia

Member
KIGX your analysis of the binary 01110010 seems sound. The reason why I input the hour variable this way was because if you look at the DS1307 data sheet (attached) it breaks down the hour byte by bits, Westaus55 provided me with this code which takes this into account.

; sec min 12A-hrs day date mon year ctrl
Main: WRITEI2C 0, ($00, $27, %01110010, $02, $16, $03, $09, $10)
;
;%01010010
; |||____
; ||| |
; ||| +--- units digit for hours = 2
; ||+----- tens digit for hours = 1
; |+------ 0 = AM and 1 = PM when in 12 hr mode when in 24hrs mode used as 2nd bit for tens of hours
; +------- 1 = 12Hrs mode 0 = 24 hrs mode

If this isn't working the way we believe it should, setting bit 6 of the hour byte high for 12 hour format, then how is this accomplished? The DS1307 can do 12 hour mode.
 

KIGX

Member
mproia:

westaust55 is quite correct and you set the hr correctly using %01110010. Everything is fine but I think there are 2 things you need to deal with:

1) the data in the byte is encoded as binary coded decimal. For example: %00010010 is BCD for 12. The upper nibble gives a 1 and the lower nibble gives a 2 which makes 12. This is the way all the time bytes work for the DS1307, including the hour byte except:

2) when you want to run in 12 hour mode. In 12 hr mode you need to set hour_byte bit 6 high. That puts it in 12 hr mode and it will always be high. Bit 5 will be 0 for am and 1 for pm. When you set the hour byte to %01110010 you set bit 6 high for 12 hr mode, you set bit 5 high to initialize the clock with the current time as pm. Those bits are high but they actually don't encode the real numeric time. The numeric time is encoded in bits 4,3,2,1,0 so ignoring bits 7,6, and 5 you have them set as XXX10010 which would be read as 12 in BCD. So your %01110010 says the clock is running in 12 hr mode and it is 12 pm. Now, the problem is that if you read this byte into a variable such as b0 it will be read in as %01110010 and in BCD that is 72. You need to drop bits 5 and 6 and THEN read the byte as %00010010 which is BCD for 12. Go back and read my other post to convert the hour byte into a real time byte.

Failing all that - it's a lot easier to run in 24 hr mode and retrolefty had a simple solution to turn 24 hr mode into 12 hr mode without these issues.
 

mproia

Member
KIGX thanks for the info. Unfortunately, looking at your previous post I am unaware of the terms hour_byte >= 96. I understand what you are saying about ignoring bits 5,6,7. Unfortunately I am a newb at this and still learning. Can you expand on you answer in terms I may understand or point me in the right direction where I can read up on how to do this. Also in retrolefty's post he mentions an AM/PM Flag can you expand on this.

Thank you for your time. I appreciate any info you can give me.
 

leftyretro

New Member
KIGX thanks for the info. Unfortunately, looking at your previous post I am unaware of the terms hour_byte >= 96. I understand what you are saying about ignoring bits 5,6,7. Unfortunately I am a newb at this and still learning. Can you expand on you answer in terms I may understand or point me in the right direction where I can read up on how to do this. Also in retrolefty's post he mentions an AM/PM Flag can you expand on this.

Thank you for your time. I appreciate any info you can give me.
Well you only really need a AM/PM flag ( a byte variable you set aside) if your going to display or print out AM or PM, otherwise it's not needed at all. However if you want a completely explicit display, displaying it 5:00 doesn't tell you if it's day or night. A simple variable that you set to 0 when hours are less then 12 hours or set to 1 if 12 or greater, then when you go to display you can test the flag to decide to display the AM or PM characters. Make sense?

If you ever use the RTC module for controlling events in a program, say a timer function for sprinkler system, it's a little easier to code things using the RTC in 24 hour mode then to have to always test for AM or PM.

Lefty
 

westaust55

Moderator
Solution is easy. :)

What you are reading back as well as the time is also the 12hr/24hrs flag and AM/PM indicators embedded in the hours data. :eek:

You must get the indicators back otherwise, how do you know what mode the clock is in and whether it is AM or PM? :confused:

Say your hours are read into variable b1 then, once you have read in hours into b1 you need to do some masking to extract the data.

So use this:
Code:
flag = b1 AND $40
IF flag = $40 THEN
   hrs = b1 AND $1F  ; a vlaue from 0 to 12 results
   pm = b1 AND $20   ; if pm time = $00 then it is AM time if pm = $20 (decimal 32) then it is PM time
ELSE 
  ; do 24 hrs stuff if required 
ENDIF
 
Last edited:

KIGX

Member
mproia;

Well I'm pretty much a newb too, particularly relative to the talent that's on this forum, so keep at it and you'll get it.

I'd say that the first thing you should do is make yourself happy by getting the clock working in 24 hr mode. Simply change the hr code you used in:

hi2cout 0, ($00, $07, %01110010, $02, $17, $03, $09, $10)

to read the hour you want, such as $22 for 7:00 pm. Presumably you have noticed that for the other entries you made that the hex codes correspond to the dates and times ie, the $17 is the 17th day, $03 was the third month, $09 is 2009, etc. Therefore $09 is 9 am and $13 is 1 pm. So replace the %01110010 with a $XX where XX is the hr you want. I haven't studied your code completely but since everything else worked I will bet that this will work. You should now have a functional 24 hr clock.

Since you got this far with writing your code I suspect you can execute retrolefty's suggestion. Retrieve the hr number from the DS1307 with your

hi2cin 0, (sec,minute,hour,day,date,month,year,control)

command. The byte that was in the DS1307 for the hour data is now stored in your variable 'hour' which you defined as b2. This is the byte that I was referring to as hour_byte, sorry for the confusion. If you haven't learned the difference between decimal, hexadecimal, binary and binary coded decimal now's a good time to go do it. Also learn some binary math. Treat the data you retrieved as hexadecimal because it 'reads' just like the data you are looking for but which in decimal looks all wrong. Now, if b2>$12 (notice the hexadecimal) then it is pm. Let's say that b2 = $14 which is 2 pm. Retrolefty would subtract $12 from b2 which would leave you with $02 which you can then use in your BCDTOASCII command to split up and send to your LCD just like you did all the other numbers. So now you have it giving you 12 hr time not 24 hr time but you did it in software as opposed to getting the DS1307 to do it for you. Retrolefty says to set a flag. What's a flag? It's kind of a note you leave for yourself. You leave a note telling yourself it is pm. How do you do that? Take another variable that you aren't using and call it a flag or any name you want. When you test to see if the hour is > $12, if it is, then set your flag variable to 1 (flag=1). If it isn't > $12 set your flag to 0 (flag=0). Then, you can check the flag to see if it is am or pm (0 or 1) and you can do whatever you want with the result such as print something out to the LCD. You can get fancier and embed the flag into other data which is exactly what the DS1307 is doing in 24 hr mode using bit 5 in the hour byte but we'll get back to that later.

OK, you should be able to get the clock running in 24 hr mode and if you figured out how to write the If Then statements you should have figured out how to change it to 12 hr time on your LCD.

I'll get back to this in a bit and see if we can tackle the DS1307 in 12 hr mode.
 

KIGX

Member
OK, so what if you want to get the DS1307 to do the 12 hr time for you? I think you now realize that the hr byte has not only the time embedded in the byte but that it has other stuff embedded in there as well. That is why it printed out 72 instead of the time you wanted. You were printing out the entire byte as a time and %01110010 is 72 in BCD and for that matter it is $72 in hexadecimal (but it is 114 in decimal) so after your BCDTOASCII it printed out 72.

OK, so for the hour byte that you retrieve from the DS1307 using your

hi2cin 0, (sec,minute,hour,day,date,month,year,control)

command. hour = b2. That byte will always have bit 6 high because that is what makes the DS1307 run in 12 hr mode. So the smallest value that byte could possibly contain is %01000000 - (but it never will be that small particularly in 12 hr mode because the time is never 00:00:00. The highest number it could be, but still be in am time, bit 5 low, is %0101111 which in decimal is 95. In pm time, the smallest number it could be is %01100000 which is 96 in decimal. Therefore, if the decimal value of b2 >=96 then it is pm. If it is <=95 it is am. So now you know whether or not it is am or pm.

But you still have to get rid of those extra bits, 5 and 6, to be able to convert the byte into the real time. There are several ways to do this. The method I suggested was to shift all the bits left until bits 5 and 6 'fell off the end' and then shift the remaining bits back to where they belong. You need to learn some binary math and learn about overflow but here's a quick look:

If you multiply a binary byte by 2, all the bits shift left one spot. The bit at bit 7 falls off the end and disappears because it overflows.

2X1 = 2 ------- 2 X %00000001 = %00000010 = 2
2X2 = 4 ------- 2 X %00000010 = %00000100 = 4

Notice how the bits shifted left. Now:

2X192 = 384 ---- 2 X 11000000 = %10000000 = 128

ya, right, the math didn't work out because of an overflow error because the byte can't hold a number > 255 but you notice that it shoved bit 7 out into space and it is now gone for good. So to get rid of 3 bits you multiply the byte by 2, 3 times. ie multiply by 6 (I spaced out in my earlier post and said to multiply by 8 which would remove 4 bits not 3). After you have multiplied by 6, all the bits are shifted left 3 places. Now you have to move them back where they belong - so divide by 2, 3 times, or divide by 6. Now all the 'time data' bits are back where they belong and you can do your BCDTOASCII just like before.

So, if b2>= 96 then ' it is pm
flag = 1
endif
b2 = b2*6/6
BCDTOASCII b2, bx,by
print it out

So the truth is I haven't actually done all this but I'm pretty darned confident in it or I wouldn't be writing this. If the the senior members see errors in my ways please speak up.

Now, westaust55 gave a very elegant solution. He used what I have heard called 'masking'. You can specifically uncover or mask any bits in a byte using logical and/or/xnor etc statements. ie, go learn some binary math.

He uses the hour byte as b1. Let's say that it is 12:00pm. The hour byte will read %01110010 - you have seen this before... He does a logical AND with $40. $40 = %01000000. So

%01110010 AND
%01000000 =
-----------
%01000000

Actually, I'm not sure why he did that since bit 6 is always high the result will always be $40. But the rest works out for me:

hrs = b1 and $1F ----- $1F = %00011111, so

%01110010 AND
%00011111 =
------------
%00010010 which in BCD is 12 for the 12 in 12:00.

He then uncovers the am pm bit using b1 AND $20, $20 = %00100000, so:

%01110010 AND
%00100000 =
-----------
%00100000 since this result has bit 5 high it must be pm and you know it is high because the answer is $20 and not $00.

Slick...

I hope that helps.
 

mproia

Member
westaus55, KIGX and retrolefty, Thank you all so much for your time in helping me with the issue. I will be dissecting each and everyone of your posts to get all I can from your wisdom. I am going to take your advice KIGX and really learn the difference between decimal, hexadecimal, binary and binary coded decimal as well as learn some binary math. From looking at each of your posts and the instruction you all provided, the solution to this task is becoming clearer and I believe I will be displaying 12 hour format correctly on my LCD in no time. :D

Before I posted my query I was able to get the code to run in 24 hour mode but I wanted to get it into 12 hour format. I thought it was going to be easy, boy was I in for a surprise. :eek:

I will let you know how I make out.

Thanks again!
 

westaust55

Moderator
Now, westaust55 gave a very elegant solution. He used what I have heard called 'masking'. You can specifically uncover or mask any bits in a byte using logical and/or/xnor etc statements. ie, go learn some binary math.

He uses the hour byte as b1. Let's say that it is 12:00pm. The hour byte will read %01110010 - you have seen this before... He does a logical AND with $40. $40 = %01000000. So

%01110010 AND
%01000000 =
-----------
%01000000

Actually, I'm not sure why he did that since bit 6 is always high the result will always be $40. But the rest works out for me:
The reason for the first Mask using $40 is to ascertain that the DS1307 is actually in 12 hour mode.

You might set the RTC later to 24 hr mode for some other program, then upload your program expecting 12 hr mode again.
So the mask and test with $40 makes the program more universal. If the program finds the mode is 24 hour mode then you could proceed to display the time accordingly.

Thought it was a "bright" idea at the time . . . .
 

KIGX

Member
westaust55:

Ya, duh, it is a bright idea. Never thought of that... Certainly the masking is much more elegant and universally applicable than kicking bits off into overflow :)
 
Top