PDA

View Full Version : Working clock example using 32K watch crystal



timc
- 6th November 2010, 19:51
Looked all over trying to find an example with a second crystal connected to the TIMER1 overflow. This example uses the earlier 18F452 version of the Olimex PIC-WEB board. Ran the clock for a day without gaining or loosing a second! Lots of places where I was not sure what to do. Comments are welcome.
Regards
Tim C


' Name : TMR1CLK.pbp
' Target PIC : PIC18F452 or similar 18F types
' Hardware : PIC-WEB ver A board with added LCD
' Oscillator : 10MHz crystal. You May have to modify default 18F452 fuses for > 4Mhz
' Resolution : Expect to get +- 5sec/day or better
' Description : PicBasic Pro program using external 32K watch xtal and Timer1
' interrupt for a real-time clock.
' Origional ver from: http://melabs.com/resources/samples/18f/tmr1clk18.htm
'
DEFINE OSC 10 ' We're using a 10MHz oscillator
DEFINE LOADER_USED 1 ' Boot-Loader is being used
Define INTHAND myint ' Define interrupt handler
Symbol LED_0 = PORTD.5
symbol BUTTON_0 = PORTB.0

wsave VAR BYTE bankA system ' Saves W
ssave VAR BYTE bankA system ' Saves STATUS

seconds VAR BYTE bankA ' variables within myint must be in bank 0.
minutes VAR Byte ' Elapsed minutes
hours var byte

' LCD config
Define LCD_DREG PORTD
Define LCD_DBIT 0
Define LCD_RSREG PORTE
Define LCD_RSBIT 0
Define LCD_EREG PORTE
Define LCD_EBIT 1

ADCON1 = 7 ' Set PORTA and PORTE for digital operation
Low PORTE.2 ' Enable the LCD

Pause 150 ' Pause to allow LCD to initialize
LCDOut $fe,1 ' Clear LCD

hours = 2
minutes = 23 ' Pre Set time here then add seconds to clock using button
seconds = 0

T1CON.7=0 ' 8 Bit r/w (16 bit seems to cause problems)
T1CON.5=0 ' Part 1 of 1:1 prescale
T1CON.4=0 ' Part 2 pf 1:1 prescale
T1CON.3=1 ' turn on the low freq clock osc
T1CON.2=1 ' Do not Sync low Freq clock with main clock
T1CON.1=1 ' use 32Khz xtal as the Timer1 source
T1CON.0=1 ' enable Timer1

PIE1 = $01 ' Enable TMR1 overflow interrupt
INTCON = $C0 ' Finally Enable global interrupt and peripheral interrupts

GoTo mainloop ' jump over the interrupt handler and sub

' Assembly language interrupt handler
Asm
myint
; Save the state of critical registers
movwf wsave ; Save W
swapf STATUS, W ; Swap STATUS to W (swap avoids changing STATUS)
clrf STATUS ; Clear STATUS
movwf ssave ; Save swapped STATUS

; Set the high register of Timer1 to cause an interrupt every
; 32768 counts (2^15).

movlw 080h ; Prepare to set TMR1 high register
movwf TMR1H ; Set TMR1H to 80h
incf _seconds,F ; INCREMENT seconds COUNT
bcf PIR1, 0 ; Clear interrupt flag

swapf ssave, W ; Retrieve the swapped STATUS value (swap to avoid changing STATUS)
movwf STATUS ; Restore it to STATUS
swapf wsave, F ; Swap the stored W value
swapf wsave, W ; Restore it to W (swap to avoid changing STATUS)
retfie ; Return from interrupt
EndAsm

' Subroutine to update the time variables
get_time:

if seconds > 59 then
seconds = seconds - 60 ' better then making seconds=0
minutes = minutes + 1
if minutes > 59 then
minutes = 0
hours = hours + 1
if hours > 12 then ' simple 12 hour clock format
hours = 1
endif
endif
endif

Return

mainloop:

GoSub get_time ' Update minutes and seconds
TOGGLE LED_0

LCDOut $fe,2,"Time: ",DEC hours, ":", DEC2 minutes, ":", DEC2 seconds, " "

Pause 300 ' Pause to see LED blink. Also help with debounce
if button_0 = 0 then
LCDOut $fe,$C0,"+1s "
seconds = seconds + 1
else
LCDOut $fe,$C0," "
endif
GoTo mainloop ' Repeat main loop

End

malc-c
- 6th November 2010, 23:43
' Description : PicBasic Pro program using external 32K watch xtal and Timer1


But 50% of the program is in assembler :)

Darrel Taylor
- 7th November 2010, 00:09
But 50% of the program is in assembler :)

Then show him how it's done. (in straight PBP) :D
This task is worth 2 free 4x20 Parallel LCD's ... shipping outide of US/Canada extra.

In case of multiple responses ... style will prevail.
As judged by Darrel Taylor
Ends 11/10/2010 Midnight Mountain Time

Archangel
- 7th November 2010, 03:13
INCLUDE "DT_INTS-18.pbp" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.pbp" ; Include if using PBP interrupts"

timc
- 7th November 2010, 06:20
The earlier versions I wrote had NO asm but lost almost a minute an hour. To make up for that one could re-tune using TMR1H and TMR1L with a larger value. Problem is every change made to the program would mean spending a few days retuning and it would never be as precise.

So yea I would love to be able to do this without asm in PBP AND be as accurate!

Down the road I intend to join this interrupt sample with a SNTP internet program so I can have a near perfect millisecond low power $60 clock.

Regards
Tim C

Bruce
- 7th November 2010, 18:33
' Int handler doesn't mess with any PBP system variables - or lower 15-bits
' of TMR1 accumulated time. Uses only 4 instructions to set TMR1H high bit,
' increment seconds, clear the interrupt flag bit, save & restore WREG, STATUS,
' BSR and return from int handler.

@myint ; create myint label
TMR1H.7=1 ' Set TMR1 high bit for 1 second overflows
seconds = seconds+1 ' Increment seconds
PIR1.0=0 ' Clear TMR1 int flag
@ retfie FAST ; Return with 'auto restore' of WREG, STATUS and BSR
Should do it.

malc-c
- 7th November 2010, 21:14
Then show him how it's done. (in straight PBP) :D


I wish --- (I'll remove my tongue from in my cheek now :) )

timc
- 8th November 2010, 14:57
Thank you Bruce
Smaller, tighter, yet perfect speed. Started the clock last night with your change and so far the PIC18F452 has not gained or lost one second.
Tim C

Acetronics2
- 8th November 2010, 16:00
Hi,

I didn't see that one :
http://www.pbpgroup.com/modules/wfsection/article.php?articleid=8

did I miss something ???

Alain

timc
- 9th November 2010, 04:38
Alain,
Darrel's great example uses one crystal mine uses 2. And oh if something is sitting there soldered to the dev board and you don't have code to see if it works!!

Acetronics2
- 10th November 2010, 22:51
Alain,
Darrel's great example uses one crystal mine uses 2. And oh if something is sitting there soldered to the dev board and you don't have code to see if it works!!

Hi, Timc

Lol ... I didn't remember I had modified Darrel's code to use a 32 Khz Xtal ...

and thought it was already done in the original release ...

Here it is.

Alain

Darrel Taylor
- 11th November 2010, 16:48
There's a couple good examples. I like Bruce's.
But it uses in-line assembly language instead of straight PBP.

The Elapsed Timer modification is nice too ... but ...

Nobody wins the LCD's. :(

pedja089
- 25th May 2011, 00:49
I was playing with TimC RTC code, and I found 1 problem...
If the interrupt occurs between these lines


GoSub get_time ' Update minutes and seconds
TOGGLE LED_0
LCDOut $fe,2,"Time: ",DEC hours, ":", DEC2 minutes, ":", DEC2 seconds, " "
There is a chance to show 60 seconds.
It happened to me, so I then wondered what was going on...
Solution:

seconds VAR BYTE bankA ' variables within myint must be in bank 0.
minutes VAR Byte bankA ' Elapsed minutes
hours var byte bankA
:
:
:
@myint ; create myint label
TMR1H.7=1
seconds = seconds+1 ' Increment second
if seconds > 59 then
seconds = seconds - 60 ' better then making seconds=0
minutes = minutes + 1
if minutes > 59 then
minutes = 0
hours = hours + 1
if hours > 12 then hours = 1 ' simple 12 hour clock format
endif
endif
PIR1.0=0 ' Clear TMR1 int flag
@ retfie FAST
And remove get_time procedure

pedja089
- 28th May 2011, 00:01
I'm trying to add the calendar.
Does anyone have an idea how to make a calculation of leap year.
I tried this
If day>30+month.0 or (Month=2 and day>28 and year//4<>0) or (Month=2 and day>29 and year//4=0)then
month=month+1
but the code is 300b larger then this
If day>30+month.0 or (Month=2 and day>28)then
month=month+1
Is there a more efficient way?

Ioannis
- 28th May 2011, 20:57
How about using 3 If-Then's insead of one with 3 OR's?

Ioannis

pedja089
- 28th May 2011, 21:46
I spent all night trying to find the best way ... I think this is

If month=2 then
If year//4<>0 then
DMonth=29
else
DMonth=28
endif
else
Dmonth=30+month.0
endif
If day>Dmonth then
@ CLRF _Day
@ INCF _Month,F
If month>12 thenAnd everywhere there is any = 0, I replaced the @ clrf, +1 replaced with @ incf.
The end result was the RTC which consumes about 2uA and code size about 500B. :o

Ioannis
- 30th May 2011, 12:00
I think you have to change the <>0 to =0 when you test for the modulo 4.

A better solution to cover more years (you code will not cover year 2100) is to use mod 400, mod 100 and mod 4 to test for leap year (mod 100 is NOT leap year, while 400 and 4 ARE leap years).

Of course we may not live in year 2100, but just in case... :)

Now about the @ commands, just be sure you are on page 0. Else you have to check the page bits. Or put this subroutine on top of your code.

Ioannis

pedja089
- 30th May 2011, 17:59
Thanks for that... It was just typo... 0-99 year is fine for this aplication.
On the top of ISR I have BSR=0, and isr routine is on top of my programm.

Ioannis
- 31st May 2011, 08:47
If I may suggest, make the minutes and hours update in your main loop and not in Interrupt. A temp variable wll be needed for this.

Ioannis

pedja089
- 31st May 2011, 19:25
If pic loses power, it goes to sleep. And everything is shut off, only tmr1 is on. So i need that in ISR. Is there better way?