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
Bookmarks