DS1994 Memory/Time iButton


Closed Thread
Page 1 of 2 12 LastLast
Results 1 to 40 of 48
  1. #1
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762

    Default DS1994 Memory/Time iButton

    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!

  2. #2
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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
    Last edited by NavMicroSystems; - 10th August 2004 at 00:13.

  3. #3
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    Does really nobody have any Input?

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

    Melanie?

  4. #4
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    I have not used this part before.

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

  5. #5
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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

  6. #6
    Join Date
    Dec 2003
    Location
    Wichita KS
    Posts
    511


    Did you find this post helpful? Yes | No

    Default

    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
    Ability to Fly:
    Hurling yourself towards the ground, and missing.

    Engineers that Contribute to flying:
    Both optimists and pessimists contribute to the society. The optimist invents the aeroplane, the pessimist the parachute

    Pilots that are Flying:
    Those who know their limitations, and respect the green side of the grass...

  7. #7
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default


    Let's hope for a "MN1994.txt"
    ("MN1307.txt was excellent!)

    Last edited by NavMicroSystems; - 14th August 2004 at 03:08.

  8. #8
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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

  9. #9
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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.

  10. #10
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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)…
    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
    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

  11. #11
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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
    Last edited by NavMicroSystems; - 27th September 2004 at 01:18.

  12. #12
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default Converting Linear date to DAY, MONTH, YEAR

    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.

    Code:
    	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

  13. #13
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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.

    Code:
    CalculateMonthLoop:
    	Lookup2 MONTH,[31,28,31,30,31,30,31,31,30,31,30,31],DAY
    Best regards

    Ralph

  14. #14
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

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

  15. #15
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    Great,
    those 14 plus another 20 by moving the LOOKUP Table to EEPROM

    regards

  16. #16
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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
    Last edited by NavMicroSystems; - 27th September 2004 at 18:28.

  17. #17
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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?

  18. #18
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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
    Last edited by NavMicroSystems; - 27th September 2004 at 20:13.

  19. #19
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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!

  20. #20
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default


    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 !

  21. #21
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

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

  22. #22
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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

  23. #23
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    When you first go through the loop, MONTH=0.

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

  24. #24
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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!

  25. #25
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    Melanie,

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

    regards

  26. #26
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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!

  27. #27
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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.

  28. #28
    Join Date
    Dec 2003
    Location
    Wichita KS
    Posts
    511


    Did you find this post helpful? Yes | No

    Default

    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
    Ability to Fly:
    Hurling yourself towards the ground, and missing.

    Engineers that Contribute to flying:
    Both optimists and pessimists contribute to the society. The optimist invents the aeroplane, the pessimist the parachute

    Pilots that are Flying:
    Those who know their limitations, and respect the green side of the grass...

  29. #29
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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
    Last edited by NavMicroSystems; - 28th September 2004 at 14:59.

  30. #30
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

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

  31. #31
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    Melanie,

    shouldn't it be . . .

    IF TempA=3

    Code:
    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

  32. #32
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

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


  33. #33
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    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

  34. #34
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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.

  35. #35
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

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

  36. #36
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

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

    Code:
    	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.

    Code:
    	'
    	'	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).

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

  37. #37
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    WOW

  38. #38
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    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.

  39. #39
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    Bah... you can end up improving code until the cows come home...

    In the EditDateTime subroutine...
    Code:
    	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.

  40. #40
    Join Date
    Feb 2004
    Location
    Germany
    Posts
    762


    Did you find this post helpful? Yes | No

    Default

    Melanie,

    UserPos should be $c8, not $cb


    Code:
    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

Similar Threads

  1. Writing & Reading to iButton EEPROM
    By crhomberg in forum Code Examples
    Replies: 2
    Last Post: - 6th October 2008, 19:40
  2. DS1920 IButton
    By scottl in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 2nd November 2007, 00:39
  3. iButton
    By menze in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 26th June 2006, 23:48
  4. RS485 - ibutton network
    By ccsparky in forum mel PIC BASIC Pro
    Replies: 7
    Last Post: - 6th June 2005, 21:48
  5. Dallas DS1994 iButton CLOCK
    By NavMicroSystems in forum Code Examples
    Replies: 0
    Last Post: - 29th September 2004, 22:19

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts