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