Thank you for reminding me Ralph... I thought if I stayed nice and quiet and kept my head down the problem will go away...
To achieve the answer is easy... to write it up so you all know how we get to it is what I wasn’t looking forward to doing... ah well, here goes an hour out of my life... and as always I draw your attention to the fact that there are many ways to get to the result - this is just one that’s simple and easy to grasp...
1. Firstly let’s stand back and look at the big picture and try to understand it...
The problem is that we have linear seconds represented as a 32 bit number which in theory we can’t handle. What we need to achieve is linear DAYS, with the remainder in the form of HOURS, MINUTES and SECONDS. That remainder of HOURS, MINUTES and SECONDS we can display as our REAL-TIME CLOCK component, whilst the linear DAYS we can then simply add to a reference point (ie a PIC version of a JULIAN DATE) to give us our YEAR, MONTH and DAY.
There are two further problems here... not too obvious but pretty serious if you think about it, and that is (a) We can’t take significant time to calculate the result (after all it’s a clock and really you’d like to see the seconds ticking by and still have time to do something else), and (b) We can’t use up serious amount of codespace (what’s the point of using the equivalent of floats and then not having any space left in your PIC).
Here’s our known limitations so far... We can’t have numbers bigger than 16-bits (65535), and we can’t have a divisor bigger than 15 bits (32767) if we decide to use DIV32 which is the only large calibre weapon in our maths arsenal. We could embed Microchips 32-bit Assembler routines but that will burn codespace and anyway, that’s cheating, quite apart from the fact that as soon we write a single line of Assembler, half the readers of this forum will immediately drop-out through lack of understanding or interest.
2. How do we reduce the numbers to something manageable?
What we ideally want to do is divide our 32-Bit number by 86400 (the number of seconds in a day). Well, the divisor is already 17 bits, and that’s two bits more than DIV32 can handle... looks on the surface like we’re losing this one - but are we?
Let’s analyse those 32 bits... what have we got?
The lower 16 bits are seconds from zero, up to 65535. The upper 16 bits are multiples of 65536 seconds. If we load the two upper bytes as a word, then each count represents 65536 seconds. What is 65536 seconds? It is 18 HOURS, 12 MINUTES and 16 SECONDS. Knowing this, we can simply explode our most significant word into multiples of HOURS, MINUTES and SECONDS...
3. Let’s program...
ByteA var BYTE
ByteB var BYTE
ByteC var BYTE
ByteD var BYTE
DAYS var WORD
HOURS var BYTE
MINUTES var BYTE
SECONDS var BYTE
TempA var WORD
TempB var WORD
TempHOURS var WORD
TempMINUTES var WORD
TempSECONDS var WORD
Now assume we’ve read our 32-Bit number from the RTC into variables ByteA, ByteB, ByteC and ByteD where ByteA holds our Most Significant Bits, and ByteD holds the Least Significant Bits.
TempA.Highbyte=ByteA
TempA.Lowbyte=ByteB
TempB=TempA*18
DAYS=DIV32 24
HOURS=R2
TempB=TempA*12
TempHOURS=DIV32 60
MINUTES=R2
TempB=TempA*16
TempMINUTES=DIV32 60
SECONDS=R2
This now completely resolves our most significant byte:-
DAYS holds our resolved days.
HOURS and TempHOURS together hold the Hours quotient
MINUTES and TempMINUTES together hold the Minutes quotient
SECONDS holds the Seconds quotient
Now for our least Significant 16 Bits...
TempA.Highbyte=ByteC
TempA.Lowbyte=ByteD
TempHOURS=TempHOURS+(TempA/3600)+HOURS
TempA=TempA//3600
TempMINUTES=TempMINUTES+(TempA/60)+MINUTES
TempA=TempA//60
TempSECONDS=TempA+SECONDS
And finally, let’s resolve the issue in it's entirity...
TempMINUTES=TempMINUTES+(TempSECONDS/60)
SECONDS=TempSECONDS//60
TempHOURS=TempHOURS+(TempMINUTES/60)
MINUTES=TempMINUTES//60
DAYS=DAYS+(TempHOURS/24)
HOURS=TempHOURS//24
So at the end of this, we have DAYS (a word variable which can be used to offset our Reference Point), HOURS, MINUTES and SECONDS, the latter all being BYTE variables. The only clever thing we have used, is a little known system variable called R2 which holds the remainder from a DIV32 execution. Before anyone asks that they've never hear of R2, that little golden nugget on this forum was brought to you by Darrel Taylor in the Code Examples section. Bet nobody's paid attention!
4. Let’s work through a random example…
10154 days, 18 Hours, 48 Minutes and 52 seconds… is 877373332 Seconds, which equates to the 32 bit number… $344BA794… (assuming my math serves me correctly)…
Does it work? As if you need to ask!Code:' ' Software Defines ' ---------------- ByteA var BYTE ByteB var BYTE ByteC var BYTE ByteD var BYTE DAYS var WORD HOURS var BYTE MINUTES var BYTE SECONDS var BYTE TempA var WORD TempB var WORD TempHOURS var WORD TempMINUTES var WORD TempSECONDS var WORD ' ' Enter Test Data ' --------------- ByteA=$34 ByteB=$4B ByteC=$A7 ByteD=$94 ' ' Calculate and Display ' --------------------- Loop: Gosub CalculateLinearDAYS LCDOut $FE,$01,"Days=",#DAYS LCDOut $FE,$C0,"Time=",DEC2 HOURS,":",DEC2 MINUTES,":",DEC2 SECONDS Pause 1000 Goto Loop ' ' Calculate Linear DAYS ' --------------------- CalculateLinearDAYS: TempA.Highbyte=ByteA TempA.Lowbyte=ByteB TempB=TempA*18 DAYS=DIV32 24 HOURS=R2 TempB=TempA*12 TempHOURS=DIV32 60 MINUTES=R2 TempB=TempA*16 TempMINUTES=DIV32 60 SECONDS=R2 ' TempA.Highbyte=ByteC TempA.Lowbyte=ByteD TempHOURS=TempHOURS+(TempA/3600)+HOURS TempA=TempA//3600 TempMINUTES=TempMINUTES+(TempA/60)+MINUTES TempA=TempA//60 TempSECONDS=TempA+SECONDS ' TempMINUTES=TempMINUTES+(TempSECONDS/60) SECONDS=TempSECONDS//60 TempHOURS=TempHOURS+(TempMINUTES/60) MINUTES=TempMINUTES//60 DAYS=DAYS+(TempHOURS/24) HOURS=TempHOURS//24 Return End
Now… having got this to work, your hour is up, and JULIAN DATE routines is another story…
Melanie




Bookmarks