-
And here is the Release Candidate:
It's actually the two examples from Melanie merged into one program
with some minor changes: (LOOKUP-Table in EEPROM)
and additions: (Calculation of WeekDay)
and it reads the DWORD seconds counter from a real iButton.
This Version is about 250 Words smaller than my initial Version.
Many thanks to Melanie
Code:
DEFINE LOADER_USED 1
DEFINE OSC 20
ADCON1=7
DEFINE LCD_LINES 2
DEFINE LCD_BITS 4
DEFINE LCD_DREG PORTD
DEFINE LCD_DBIT 4
DEFINE LCD_RSREG PORTD
DEFINE LCD_RSBIT 1
DEFINE LCD_EREG PORTD
DEFINE LCD_EBIT 0
' Software Defines
' ----------------
iButton var PortD.2
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
CounterA var BYTE
DAY var BYTE
MONTH var BYTE
YEAR var BYTE
DoW var byte[3]
DATA @0,"S","a","t","S","u","n","M","o","n","T","u","e","W","e","d","T","h","u","F","r","i"
DATA @21,31,28,31,30,31,30,31,31,30,31,30,31
' Calculate and Display
' ---------------------
Loop:
' Read iButton
' ------------
owout iButton,1,[$cc,$f0,$03,$02]
owin iButton,0,[ByteD,ByteC,ByteB,ByteA]
Gosub CalculateLinearDAYS
GOSUB CalculateDateFromLinear
LCDOut $FE,$01, STR DOW\3,". ", dec2 Day,".",dec2 month,".","20",dec2 year
LCDOut $FE,$C0," ",DEC2 HOURS,":",DEC2 MINUTES,":",DEC2 SECONDS
Pause 1000
Goto Loop
'
' Calculate Linear DAYS and Day of Week
' -------------------------------------
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
'
Read (DAYS//7*3),DoW[0]
Read (DAYS//7*3+1),DoW[1]
Read (DAYS//7*3+2),DoW[2]
'
Return
' Subroutine Calculates Date from Linear Days
' -------------------------------------------
CalculateDateFromLinear:
YEAR=0
DAYS=DAYS+1
For CounterA=4 to 183
TempA=CounterA//4
TempB=365
If TempA=0 then
TempB=TempB+1
endif
If DAYS>TempB then
YEAR=YEAR+1
DAYS=DAYS-TempB
else
Goto CalculateMonth
endif
Next CounterA
' add 2000 to the YEAR value, ie YEAR=4 implies 2004.
CalculateMonth:
MONTH=0
CalculateMonthLoop:
READ (MONTH+21),DAY
IF TempA=0 then
If MONTH=1 then DAY=DAY+1
Endif
If DAYS>DAY then
DAYS=DAYS-DAY
MONTH=MONTH+1
Goto CalculateMonthLoop
Endif
MONTH=MONTH+1
DAY=DAYS
Return
End
Best regards
Ralph
-
That looks good Ralph...
Have you worked out how to calculate 32-Bit Linear Seconds from a Gregorian Date (ie Year, Month Day) in order to set the iButton to say today's Date and Time?
-
Melanie,
to be honest, I'm still struggling with it.
It would be a lot easier if PBP would have DWORD vars and math.
To initially set the iButton I have calculated the set value manually and written it to the iButton.
The accuracy of the "uncalibrated" DS1994 is surprisingly good, it is up for 61 days now and is "only" 29 seconds behind.
Could you help with a SET-Routine that is less codespace hungry than my attempts so far?
BTW
I have written a "AUTO-DST-adjustment" that I will add as well.
best regards
Ralph
-
The way I see to handle this is in three stages...
1. A Data-Entry routine to capture individual YEAR, MONTH, DAY, HOUR and MINUTE. You have the LCD screen, do you also have say three buttons available that we can assign to UP, DOWN and SET function?
2. CalculateLinearFromDate routine, to calculate Linear Days from the Date component (again a form of Julian Date - the inverse of my CalculateDateFromLinear routine.
3. A method of generating the 32-Bit number containing the Linear Seconds to feed the iButton.
Sure it would be easier to have all those other functions in PBP... but isn't it fun doing it this way? And it also proves that IT CAN BE DONE!
I recall going to dinner once with an old retired gentleman that used to be the Data Processing Manager/IT Director of the Rolls Royce company. He recalled, that when he joined the company as a young Systems Analyst, one of his first tasks was to find an unsed BIT in memory to act as a Flag for a new feature in their Payroll program... they ran a weekly payroll for over 2000 employees on a computer with 8kb core memory! We've got it easy!
-
1. A Data-Entry routine to capture individual YEAR, MONTH, DAY, HOUR and MINUTE. You have the LCD screen, do you also have say three buttons available that we can assign to UP, DOWN and SET function?
YES, there are three Buttons
2. CalculateLinearFromDate routine, to calculate Linear Days from the Date component (again a form of Julian Date - the inverse of my CalculateDateFromLinear routine.
I think the principle is clear (eventhough I don't have the code ready yet)
3. A method of generating the 32-Bit number containing the Linear Seconds to feed the iButton.
This is the challange !
-
Ralph... you may have a small flaw in your code... shouldn't...
READ (MONTH+20),DAY
actually be...
READ (MONTH+21),DAY
I have a habit of checking other peoples changes to my code before I use it myself... an old adage I always go by "Trust - but verify".
-
Melanie,
thanks for checking the code.
the first EEPROM Address for the lookup table is 21, so far you are right.
as the address is calculated from "Month+20" the result for January (Month=1) would give 21.
(We are counting months starting from 1, not zero.)
best regards
Ralph
-
When you first go through the loop, MONTH=0.
(We don't add the MONTH correction until you're out of the loop.)
-
Thanks Melanie,
you are absolutely right.
I messed it up when I changed the DoW Table from two characters to three characters for the weekday.
Thanks again!
-
Melanie,
I'm past the timelimit for editing the post, could you correct that for me?
regards
-
Here's the solution to item 2 from my previous post yesterday (as it's the easiest to complete). It's the inverse of the CalculateDateFromLinear subroutine.
This subroutine takes YEAR, MONTH and DAY and returns with Linear DAYS starting with DAYS=0 for 01/01/2000. Note, it's only good for 100 years as it does NOT account for year 2100 leap anomaly. I'm reusing variables and EEPROM presets previously defined in your program.
Code:
'
' Subroutine Calculates Linear Days from Date
' -------------------------------------------
CalculateLinearFromDate:
DAYS=0
For CounterA=0 to YEAR
TempA=CounterA//4
If CounterA < YEAR then
TempB=365
If TempA=0 then TempB=TempB+1
DAYS=DAYS+TempB
endif
Next CounterA
IF MONTH > 1 then
For CounterA=1 to MONTH-1
Read (CounterA+20),ByteA
If TempA=0 then
If CounterA=2 then ByteA=ByteA+1
endif
DAYS=DAYS+ByteA
Next CounterA
endif
DAYS=DAYS+DAY-1
Return
Oh and in this routine, my MONTHS start counting from 1, so CounterA+20 is valid here!
-
Just have enough time to squeeze in Part 3 (supposedly the most difficult part) before I have to go and do something meaningful with my life...
This next subroutine takes our previously calculated Linear DAYS, and together with our HOURS, MINUTES and SECONDS builds a 32 bit number. It's the PBP mathematical equivallent of...
X=(DAYS*86400)+(HOURS*3600)+(MINUTES*60)+SECONDS
...but using simple 16-bit integer math only. It would be interesting to compare which method uses less codespace.
Before you look at the subroutine, I'd like to point out, that again I'm re-using variables previously defined earlier. Don't get confused with the names of the WORD variables such as TempHOURS, TempMINUTES and TempSECONDS. They have been previously defined in our program and I'm just using them as convenient WORD-sized storage.
Code:
'
' Subroutine Calculates Linear Seconds (as 32-Bit Number)
' -------------------------------------------------------
CalculateLinearSeconds:
TempA=0 ' Holds the HighWord (Bits 16-31) ie ByteA and ByteB
TempB=0 ' Holds the LowWord (Bits 0-15) ie ByteC and ByteD
IF HOURS > 11 then
TempB=$A8C0
HOURS=HOURS-12
endif
TempSeconds=(HOURS*3600)+(MINUTES*60)+SECONDS
TempB=TempB+TempSeconds
If TempB < TempSeconds then TempA=TempA+1
TempMINUTES=$A8C0:TempSeconds=$0000
TempHOURS=$8000
If DAYS=0 then CalculateLinearSecondsExit
While DAYS > 0
If DAYS = > TempHOURS then
TempB=TempB+TempSECONDS
If TempB < TempSECONDS then TempA=TempA+1
TempA=TempA+TempMINUTES
DAYS=DAYS-TempHOURS
endif
TempSECONDS=TempSECONDS>>1
If TempMINUTES.0=1 then TempSECONDS.15=1
TempMINUTES=TempMINUTES>>1
TempHOURS=TempHOURS>>1
Wend
CalculateLinearSecondsExit:
Return
Naturally we can extract our four 8-Bit Bytes (ByteA, ByteB etc) from the two 16-Bit variables (TempA and TempB) holding our 32-Bit result, and use that to directly set our iButton.
What the above program does is a very simple Binary Recursive Addition to add together up to a maximum of 65535 days worth of seconds in sixteen (or less) itterations - which makes this a fast solution. We start off with TempMinutes and TempSeconds together holding a 32-Bit number ($A8C0:0000) which is the equivallent of 32768 days worth of Seconds. TempHOURS contains our DAY count-down, starting with 32768 days. Each time we travel through the WHILE-WEND loop, we halve the number of days (and so halving the number of seconds), until we run out of days and the loop is exited.
Actually we can only calculate around 49710 days worth of seconds, before we spill out of 32 bits, so there's no point in calculating any dates beyond about 135 years anyway.
I'll give you a pretty Data-entry routine to tie it all together later when I've some more time.
Once again. for all the skeptics complaining about the lack of big-number math in PBP, this demonstrates that it CAN be done.
I've not actually tested this routine, but it's simple enough that I'm confident it'll hold water.
-
Hello You two,
I wish I had a RTC chip to play around with you all....I used to (many years ago) play with calanders and converting... But its been way to long now... I can't even remember the differences between them <g>.
I delt with the YY.MMDD method, and the DD/MM/YY. In my programs in accounting, I wrote a very short routine that took any two dates and figured the differences between the two (Hey you got to produce agings somehow... <g>.
Would you mind explaining exactly what you are trying to do?
Dwayne
-
Dwayne,
by re-reading this thread from the beginning you should get the full picture.
If there are still questions let us know.
regards
Ralph
-
This is a great thread, because it's demonstrating and reminding us of a whole heap of things... and these are useful things...
So far...
1. We've been reminded of a little known but very useful variable called R2 in the DIV32 command.
2. We've manipulated a 32 bit variable which on the face of it seemed impossible with PBP, and reduced it to something useful and manageable.
3. We've converted Gregorian Date (DAY, MONTH, YEAR) into a Julian Date (Linear DAYS).
4. We've converted Julian Date (Linear DAYS) back into a Gregorian Date (DAY, MONTH, YEAR). This has been mentioned you can't do it with only 16-bit variables.
5. We've discovered a way of saving a Date (as DAY, MONTH and YEAR) compactly as two BYTES (great for space saving when Data-Logging).
6. Better than that we've discoverd a way to save DAY, MONTH, YEAR, HOUR, MINUTE and SECOND, as only FOUR bytes. Again, beat that for a Data-Logging application.
Just using those you could enter two dates, and not only determine the DAYS between them, but you could determine it to the SECOND.
7. We've demonstrated a method of 16 by 17 bit multiplication... to produce a 32 Bit result.
And we've done it all in 'pure' PBP with no Assembler or other 'bought-in' foreign routines. That way, everybody can follow it and learn something to their benefit.
And this thread isn't done yet...