PDA

View Full Version : DS1994 Memory/Time iButton



NavMicroSystems
- 24th February 2004, 18:00
I know I'm a pain...
but
does anyone have a piece of PBP-code that converts the binary data from a DS1994 iButton to "readable" time and date ?

Thanks!

NavMicroSystems
- 10th August 2004, 00:53
60+ Views on this posting (so far) and no reply.

So may I assume there are 60+ people interested in a solution?

Has really nobody used the DS1994 yet?

I have a bunch of DS1994 sitting in a drawer and think they are a good replacement for DS1307 or PCF8583 as they have an embedded crystal and Lithium Backup Battery (that lasts 10+ years) and they are 1-wire.
In addition there is 4k of NV-RAM on Chip.

They count seconds from a reference Date and Time.

How to calculate Year, Month, Day, Day of Week and Time from this DoubleWord Counter
using the Integer Brain of a PIC?
(ok, the counter is actually 5 Bytes, but I don't need the Least Significant Byte that counts fractional seconds)

Any Input is appreciated!

regards

Ralph

NavMicroSystems
- 11th August 2004, 16:26
Does really nobody have any Input?

(Or do you just nor want to share it?)

Melanie?

Melanie
- 11th August 2004, 17:13
I have not used this part before.

When I've a moment free I'll download the Datasheet and see what's cooking...

NavMicroSystems
- 11th August 2004, 18:18
Melanie thanks for your response.

I have used a number of 1-wire devices and iButtons in the past.
So reading from and writing to the DS1994 is no problem.

The "Time" reading from the DS1994 is a 5 Byte value
that contains the number of seconds (and fractional seconds) elapsed since the counter has been started.

The least significant of those 5 bytes contains fractional seconds.
So we actually have only to deal with a four bytes (or Dword) seconds counter.

To set the Clock choose a reference date, calculate the number of seconds that have elapsed from then til "NOW" and write this to the DS1994 as the "start-value"

All this works fine and the clock is running.

But how do I calculate Year, Month, Day, Day of Week and Time from that DWord Counter to have something more readable to display having only the poor integer math of the PIC?

Best regards

Ralph

Dwayne
- 11th August 2004, 19:55
Hello Nav!

Nav>>I know I'm a pain...but<<

forgot "in the" <chuckle>.... No, just kidding here. Giving a friend a hard time.

Nav>>does anyone have a piece of PBP-code that converts the binary data from a DS1994 iButton to "readable" time and date ?<<

I have never used a DS1994, or any RTC before. I am getting a little interested in them right now... I biggest problem with RTC, is I want "forever", instead of 10 years <g>. I would like to know what you come up with on these RTC chips. I may order one, next time I order from allelectronics.com

Dwayne

NavMicroSystems
- 14th August 2004, 04:05
Let's hope for a "MN1994.txt"
("MN1307.txt was excellent!)

NavMicroSystems
- 16th August 2004, 16:23
Some progress has been made.

I managed to calculate the number of Days, Hours, Minutes and Seconds
elapsed since the reference date (01/01/2000 00:00:00)
from the DoubleWord seconds counter reading.

So far I can display the correct time from the iButton reading.

Next steps will be:
- calculation of Year, Date and Day of Week.
- A "Set Clock" Routine.
- automatic DST adjustment.

Your Input is still appreciated.

regards

Melanie
- 16th August 2004, 16:47
I've got as far as downloading the Datasheet... looks like a linear extension of Julian Date kind of application... this is a bad two weeks for me (lots of work plus being an Olympics fan I'm in Athens at the weekends!)... but I'll see what I can do after.

Melanie
- 26th September 2004, 20:54
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)…



'
' 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

Does it work? As if you need to ask!

Now… having got this to work, your hour is up, and JULIAN DATE routines is another story…

Melanie

NavMicroSystems
- 27th September 2004, 00:42
Melanie,

many thanks for taking the time to teach us in math,
I like your approach.

In the meantime I have found a slightly different solution that works.

But I still feel it takes too much code space and memory.

After I have cleaned up the code (hopefully tomorrow) I will post my example.

BTW.
I followed the discussion" PBP vs. PROTON+"

I have tried my DWORD calculations using PROTON+
and feel it does not really help.
If one writes own double precision math routines in PBP
the same result can be achieved.
PROTON+ (and its math routines) do not save any memory or code space
as they don't (and can't) do any magic.

best regards:

Ralph

Melanie
- 27th September 2004, 13:44
As we all know, Julian Date was fixed by Julius Caesar, this was a guy that played with PICs a lot - and look where it got him. The problem we have is that PBP doesn’t handle Roman Numerals too well, and whilst the idea of having a Linear Date is a good (and very useful) one, generally, we only have to handle the Century that we’re in. So let’s start our PIC’s handling of a Linear Date from 1st January 2000... this way an integer variable (counting in DAYS) can ‘theoretically’ handle over 170 years worth of data.

Now I don’t know about you, but it’s highly unlikely that any of my designs are still going to be working in the year 2170 (in best engineering tradditions they're only guaranteed to work until the warranty expires), and I’m sure not going to be fielding the tech-support calls. If you find that the date gets screwed past 05/06/2179 (integer value 65534), don't email me - I'm not interested.

Converting Linear DAYS Back to a Date in the form of YEAR, MONTH and DAY is a little tricky... This is a clumsy way of working out the result, however, the routine I actually use I’m not willing to share, so you’ll have to make do with this one I threw together this morning... starting at January 1 2000 it’s good for almost 180 years (and being a thoughtful kinda girl, I've accounted for the fact that Year 2100 is NOT a Leap Year)...

The Subroutine shares variables that I've used earlier in this thread, and is called with the Linear Date held in the variable DAYS, and returns with a useable YEAR, MONTH and DAY.



CounterA var BYTE
DAY var BYTE
DAYS var WORD
MONTH var BYTE
TempA var WORD
TempB var WORD
YEAR var BYTE

'
' 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
If CounterA<>104 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:
Lookup2 MONTH,[31,28,31,30,31,30,31,31,30,31,30,31],DAY
IF TempA=0 then
IF YEAR<>100 then
If MONTH=1 then DAY=DAY+1
Endif
Endif
If DAYS>DAY then
DAYS=DAYS-DAY
MONTH=MONTH+1
Goto CalculateMonthLoop
Endif
MONTH=MONTH+1
DAY=DAYS
Return

Melanie

NavMicroSystems
- 27th September 2004, 16:03
Melanie,
thanks again !!

your code is smaller than my one and I will use it for the DS1994.

You could save another 79 words of codespace by replacing the LOOKUP2 with LOOKUP.



CalculateMonthLoop:
Lookup2 MONTH,[31,28,31,30,31,30,31,31,30,31,30,31],DAY


Best regards

Ralph

Melanie
- 27th September 2004, 17:19
I originally used LOOKUP2 because my first rough code this morning used the last day of each month cumulative, which means as you get towards the end of the year the DAY spills out of a byte variable, before I changed the code to the version I actually posted, and forgot to change it back.

You can of course save yourself another 14 words of codespace by not caring that your code runs past year 2100 and removing the century leap-year correction. As if we're going to lose sleep over it...

NavMicroSystems
- 27th September 2004, 18:48
Great,
those 14 plus another 20 by moving the LOOKUP Table to EEPROM

regards

NavMicroSystems
- 27th September 2004, 19:23
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







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

Melanie
- 27th September 2004, 20:44
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?

NavMicroSystems
- 27th September 2004, 21:05
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

Melanie
- 27th September 2004, 21:21
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!

NavMicroSystems
- 27th September 2004, 21:29
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 !

Melanie
- 28th September 2004, 13:04
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".

NavMicroSystems
- 28th September 2004, 13:19
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

Melanie
- 28th September 2004, 13:26
When you first go through the loop, MONTH=0.

(We don't add the MONTH correction until you're out of the loop.)

NavMicroSystems
- 28th September 2004, 13:34
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!

NavMicroSystems
- 28th September 2004, 13:37
Melanie,

I'm past the timelimit for editing the post, could you correct that for me?

regards

Melanie
- 28th September 2004, 14:12
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.



'
' 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!

Melanie
- 28th September 2004, 14:40
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.



'
' 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.

Dwayne
- 28th September 2004, 15:31
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

NavMicroSystems
- 28th September 2004, 15:57
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

Melanie
- 28th September 2004, 16:33
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...

NavMicroSystems
- 28th September 2004, 17:33
Melanie,

shouldn't it be . . .

IF TempA=3




CalculateLinearFromDate:

DAYS=0
IF YEAR>0 then
For CounterA=0 to YEAR-1
TempA=(countera)//4
LCDOUT $FE,$8e,dec2 tempa
TempB=365
If TempA=0 then TempB=TempB+1
DAYS=DAYS+TempB
Next CounterA
endif
IF MONTH>1 then
For CounterA=1 to MONTH-1
Read (CounterA+20),ByteA

'-----------------------------------------------
If TempA=3 then
'-----------------------------------------------
If CounterA=2 then ByteA=ByteA+1
endif
DAYS=DAYS+ByteA
Next CounterA
endif
DAYS=DAYS+DAY-1
Return

Melanie
- 28th September 2004, 18:36
Nope.

TempA=CounterA//4

2000//4=0
2004//4=0
etc etc

OK, CounterA holds 0 for 2000, 4 for 2004 etc etc, the '2000' is implied.

TempA=0 only when there is a leap year...

We use it twice in the routine... once with whole years by adding a day to any leap year. But when we calculate out a date that ends on a leap year itself, we have to add 1 additional day to February's month total. So that Februarys days read from EEPROM (or LOOKUP) is adjusted.

That particular routine I did check before I posted it... and I've since checked the CalculateLinearSeconds one too, and both work as expected. I'll post the Data-entry routines shortly.

Meanwhile... you can do a test like this to check for yourself...

DAY=24
MONTH=12
YEAR=66 ' 2066 Implied
Gosub CalculateLinearFromDate
'
' at this point DAYS=24464
'
HOURS=14
MINUTES=37
SECONDS=46
Gosub CalculateLinearSeconds
LCDOut $FE,$C0,"$",HEX4 TempA,HEX4 TempB

Your LCD should display $7DFD25BA

This equates to the decimal number 2113742266.

A quick calculator check of (24464*86400)+(14*3600)+(37*60)+46 confirms we're doing something right!

Now feed that figure back through the routines I posted yesterday and you get... surprise, surprise...

NavMicroSystems
- 28th September 2004, 19:22
Try it with:

DAY=28 Month=2 Year=4
DAY=29 Month=2 Year=4
DAY=1 Month=3 Year=4

and doublecheck the above with year=8

Melanie
- 28th September 2004, 20:04
You're both right and wrong...

I couldn't figure why my test program works and you say it didn't... it took me ages to discover I've posted the wrong subroutine...

So yes, there was an erroneous result, but the solution was a rehash of the existing lines... TempA must still stay as If TempA=0...

I have corrected it now...

28/2/04 is Days 1519
29/2/04 is Days 1520
01/3/04 is Days 1521

Also now valid for DAYS=2980, 2981, 2982 etc up the leap years.

NavMicroSystems
- 28th September 2004, 20:18
The change TempA=0 to TempA=3 on the line marked was only a workaround, but it would have done the trick with the initial routine (I think).

Melanie
- 28th September 2004, 22:21
Right now you're pausing every Second and redisplaying whilst clearing the screen every time. This has a number of drawbacks... (1) it will cause your LCD to 'flicker' every time you display... (a more professional way is to clear the display only every 'n' seconds)... (2) the 1 second delay can cause an unacceptible 'lag' when somebody presses Buttons (to enter the Setup routine for example)... and (3) can cause a strange effect when you're watching seconds ticking by, as every now and again you get the feeling a Second is longer than it should be, because you're adding one 'software' second (plus propogation delay in your software program) to your RTC second and they can get out of step...

Fisrt we've added some more variables...

KeyBut var BYTE
TimeOut var BYTE
UserField var BYTE
UserMax var BYTE
UserMin var BYTE
UserPos var BYTE

and we've increased your DOW array...

DOW var BYTE [5]

and introduced a constant...

TimeoutConstant con 200

... more about these later.

My Buttons are connected on PortB and I'm using pull-ups (pin goes Low on Button Press). At the start of my proram I have the following defines...

ButtonUp var PortB.0
ButtonDown var PortB.1
ButtonSet var PortB.2

and I've also put...

OPTION_REG.7=0

...at the start where I first initialse my PIC. Change these around, (along with my Getkey routine - later) to suit your own arrangement.

Now we rehash the Main Program Loop like so...



TimeOut=TimeoutConstant
Loop:
'
' Read iButton
' ------------
owout iButton,1,[$cc,$f0,$03,$02]
owin iButton,0,[ByteD,ByteC,ByteB,ByteA]
'
' Display Date & Time
' -------------------
Gosub CalculateLinearDAYS
GOSUB CalculateDateFromLinear
If TimeOut = > TimeoutConstant then
LCDOut $FE,1
TimeOut=0
endif
LCDOut $FE,$80, STR DOW\3,". ", dec2 Day,".",dec2 month,".","20",dec2 year
LCDOut $FE,$C4,DEC2 HOURS,":",DEC2 MINUTES,":",DEC2 SECONDS

' Note the $FE,$C4 above... saves you a few bytes...

TimeOut=TimeOut+1
Gosub GetKey
If KeyBut < > 3 then goto Loop
'
' Setup Date & Time
' -----------------
DOW(0)=YEAR
DOW(1)=MONTH
DOW(2)=DAY
DOW(3)=HOURS
DOW(4)=MINUTES
Gosub EditDateTime
If TimeOut=0 then ' Only Save if SET Button Pressed
YEAR=DOW(0) ' ie not TimeOut
MONTH=DOW(1)
DAY=DOW(2)
HOURS=DOW(3)
MINUTES=DOW(4)
SECONDS=0
Gosub CalculateLinearFromDate
Gosub CalculateLinearSeconds
ByteA=TempA.HighByte
ByteB=TempA.LowByte
ByteC=TempB.HighByte
ByteD=TempB.LowByte
'
' Set your iButton here
'
endif
TimeOut=TimeoutConstant ' This causes a Clear Screen in our main Loop
Goto Loop

Now you'll only get a flicker once every 20 Seconds, and with our GetKey subroutine we have a worst case lag of 100mS. We've also created a neat way of dropping into our SetUp at that point.

You'll also see I'm using two new variables KeyBut and TimeOut, both of which are BYTES. Keybut returns a value from 0-7 (per the GetKey routine table below) depending on what Button or combination of Buttons are pressed. TimeOut is an incremental 100mS counter. It is reset to zero should any key be pressed... more about this later... Finally I use a Keyboard Loop-Time of 100mS. I have found that less than that and Button autorepeat is too fast to handle easily. More than that, and autorepeat feels sluggish, and you get bored holding the Button. Change this value to suit your personal preference - but in doing so you may also need to change the TimeoutConstant value.



'
' Subroutine Scans Buttons
' ------------------------
' KeyBut - 0 = Invalid (SET+UP+DOWN Pressed)
' - 1 = Invalid (SET+UP Pressed)
' - 2 = Invalid (SET+DOWN Pressed)
' - 3 = SET Pressed
' - 4 = Invalid (UP+DOWN Pressed)
' - 5 = UP Pressed
' - 6 = DOWN Pressed
' - 7 = No Buttons Pressed
GetKey:
KeyBut=0 ' Zero any previous Result First
KeyBut.0=ButtonDown ' Read Input Buttons
KeyBut.1=ButtonUp
KeyBut.2=ButtonSet
If KeyBut < > 7 then TimeOut=0 ' Resets Timeout Counter
Pause 100 ' Allows for 10cps auto-repeat
Return

Now for the EditDateTime Routine... you'll notice I've increased the size of your DOW array to 5 elements as I'm going to use that as my editing array since there are 5 things I need to edit... YEAR, MONTH, DAY, HOUR and MINUTE and I really need a byte for each... and since DOW is unused during SetUp, this seems a good use of otherwise unused resources to me.

I've put the EditDateTime into a subroutine, because you may want to use the same routine for entering and setting the iButton alarms later as your program gets more sophisticated.

This Date & Time subroutine is BULLET-PROOF. There's nothing worse than attempting to enter the 31st of April, or 29th of February in a non-leap year. Makes your code look amateurish and quickly causes loss or credibility with clients. Try entering an invalid Date or Time setting - I think you'll find you can't break it.

The subroutine also assumes DOW is preloaded with a valid date and Time on entry (even if it's just a simple 1/1/2000 00:00), usually you would read the RTC and if it's got a sensible Time & Date you would preload the figures with that... (when setting an Alarm for example, you would want the previous setting to be displayed so you can edit it).

Look-out for the bonus features... (a) if you don't press any Buttons for 20 seconds, then you will be returned to the main Loop without any new settings being saved (that's where that TimeOut variable comes in)... and (b) of course you've got that 10Hz autorepeat on the Buttons (except SET).



'
' Subroutine for Date and Time Entry
' ----------------------------------
EditDateTime:
LCDOut $FE,1,"Set Date & Time"
LCDOut $FE,$C0,"20",DEC2 YEAR,"/",DEC2 MONTH,"/",DEC2 DAY
LCDOut $FE,$CB,DEC2 HOURS,":",DEC2 MINUTES
UserField=0
EditDateTimeLoop:
UserMin=0:UserMax=99:UserPos=$C2
If UserField=1 then
UserMin=1:UserMax=12:UserPos=$C5
endif
If UserField=2 then
UserMin=1:UserMax=31:UserPos=$C8
If DOW(1)=2 then
CounterA=DOW(0)//4
If CounterA=0 then
UserMax=29
else
UserMax=28
endif
endif
If DOW(1)=4 then UserMax=30
If DOW(1)=6 then UserMax=30
If DOW(1)=9 then UserMax=30
If DOW(1)=11 then UserMax=30
endif
If UserField=3 then
UserMax=23:UserPos=$CB
endif
If UserField=4 then
UserMax=59:UserPos=$CE
endif
Gosub GetKey
If KeyBut=3 then goto EditDateTimeLoop
EditDateTimeFieldLoop:
LCDOut $FE,UserPos,DEC2 DOW(UserField),$FE,$0E,$FE,UserPos+1
EditDateTimeIdleLoop:
Gosub GetKey
If KeyBut=5 then
DOW(UserField)=DOW(UserField)+1
If DOW(UserField)>UserMax then DOW(UserField)=UserMin
Goto EditDateTimeFieldLoop
endif
If KeyBut=6 then
If DOW(UserField) > UserMin then
DOW(UserField)=DOW(UserField)-1
else
DOW(UserField)=UserMax
endif
Goto EditDateTimeFieldLoop
endif
If KeyBut < > 3 then
TimeOut=TimeOut+1
If TimeOut < TimeoutConstant then goto EditDateTimeIdleLoop
Goto EditDateTimeExit
endif
LCDOut $FE,$0C
UserField=UserField+1
If UserField < 5 then goto EditDateTimeLoop
EditDateTimeExit:
LCDOut $FE,$0C,$FE,$01
While KeyBut < > 7
Gosub GetKey
Wend
Return

Note also the creative use of Curson ON and Cursor OFF, so that the user is guided to take their finger OFF the SET Button and progress to editing...

Have fun

Melanie

NavMicroSystems
- 28th September 2004, 22:34
WOW

Melanie
- 28th September 2004, 22:47
When you're done, you can create Ralph1994 and send it with a resume of what it does to MeLabs for inclusion in their Submitted Programs section... people must be getting bored with my offerings.

Melanie
- 29th September 2004, 10:27
Bah... you can end up improving code until the cows come home...

In the EditDateTime subroutine...


If UserField=2 then
UserMin=1:UserPos=$CB
Read DOW(1)+20,UserMax
If UserMax=28 then
CounterA=DOW(0)//4
If CounterA=0 then UserMax=29
endif
endif

Is a bit more professional and saves 34 bytes.

NavMicroSystems
- 29th September 2004, 13:42
Melanie,

UserPos should be $c8, not $cb




If UserField=2 then
UserMin=1:UserPos=$CB '<---------------------
Read DOW(1)+20,UserMax
If UserMax=28 then
CounterA=DOW(0)//4
If CounterA=0 then UserMax=29
endif
endif


regards

Yup - you're right - Melanie

NavMicroSystems
- 30th September 2004, 20:26
You can find the "final" version in the code examples section:

http://www.picbasic.co.uk/forum/showthread.php?s=&postid=3041#post3041

regards:

Ralph

isaac
- 23rd March 2005, 22:57
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.



'
' 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!

I can't seem tp follow the variable delaration of the program
Can i use this with my DS1307 RTC
i can read YEAR, MONTH and DAY from my RTC what variable to i send them to before calling the sub.
i am confused here

Isaac

isaac
- 25th March 2005, 14:22
Hi Nav / Mel
I have being reading the trend DS1994 Memory/Time iButton and it is very very interesting and just need your help in using the program.
I am using a DS1307 RTC of which i read the following variables

Date =23
Month=03
Year=05
Hour =21
Mins =30
Seconds=30
to give me 23-03-05 21:30:30 (my data & time)
which are all byte variables

But i want to convert this to Linear DAYS and add 365 but can't figure out which subroutine to use as from my understand the subroutines are looking for a 32bit number

Could you be kind enough to explain to me

Best Regards
Isaac

NavMicroSystems
- 25th March 2005, 14:47
ISAAC,

the Subroutine "CalculateLinearFromDate" isn't really that complex, is it?

with your variables beeing:

Date =23
Month=03
Year=05
Hour =21
Mins =30
Seconds=30

all you need to do is:
rename your VAR "Date" to "DAY"
(or create an alias)
and declare the following variables in addition:

CounterA VAR Byte
TempA VAR WORD
TempB VAR WORD
DAYS WAR WORD

once you have read the DS1307 just

GOSUB CalculateLinearFromDate

and you will find the result in "DAYS"

Yuantu Huang
- 10th May 2005, 03:40
Hi Ralph & Melanie,

How to calculate the elapsed hours between 16:30:45 (HH:MM:SS), 28/05/2005 (DD/MM/YYYY) and 02:26:38, 01/08/2004.

Assume the elapsed time is limited to less than one year. Can I use the CalculateLinearFromDate subroutine to do the job?

Best regards,

Melanie
- 10th May 2005, 12:47
CalculateLinearFromDate will account for the DAYS. Multiply by 24 for your TotalHours. Then to account for the Hours...

If EndDateHours > StartDateHours then TotalHours=TotalHours+(EndDateHours-StartDateHours)

If EndDateHours < StartDateHours then TotalHours=TotalHours-(StartDateHours-EndDateHours)

Yuantu Huang
- 11th May 2005, 02:55
Hi Melanie,

Thank you very much.

naga
- 22nd October 2006, 12:55
When you're done, you can create Ralph1994 and send it with a resume of what it does to MeLabs for inclusion in their Submitted Programs section... people must be getting bored with my offerings.

hi,

i succeed to read ibutton (1990a) and display it on lcd..but iam not able to store serial# on variable..i want to compare ibutton's serial nos.iam trying to read multiple ibuttons and store their sr# in eprom.. if sr# is 1 of the stored # then do something...

Thanking you

Regards.