Log in

View Full Version : RTC via TMR0 + HSERIN and 16F916



boban
- 26th March 2008, 13:30
Hello,
I am designing the application which should wait for the serial data and based on this do something. Also I should have in the same chip the clock and each minute perform some tasks. So I implemented the clock using tmr0 interrupt and I have a loop, which is waiting for the serial commands. It is working quite well, but I have problem, that my clock are not ticking well it is loosing each hour approx. 2 minutes. But I don't know why. Here is my code, I am using 16F916 with 4 Mhz external crystal resonator.

I have tmr0 prescaller in OPTION_REG = $d7 so I think, that tmr0 overflow happens 15 times per second. But seems, that it is not like this. Any suggestion?

DEFINE OSC 4
'hserin definition
DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 24h
DEFINE HSER_SPBRG 12 'DEFINE HSER_BAUD 19200 on 4 Mhz
DEFINE HSER_CLROERR 1
'variables for timer
day VAR BYTE ' Define day in the week
hour var byte ' Define hour variable
minute var byte ' Define minute variable
second var byte ' Define second variable
ticks var word ' Define pieces of seconds variable

'put port A to input output
ANSEL = 0
CMCON0= 255
' interrupt for timer
OPTION_REG = $d7
INTCON = $a0 'enable TMR0 interrupts and global interrupt
PIE1 = 0
On Interrupt Goto intManagement
clear

enable
while 1
if (manageIntSecond == 1) then
manageIntSecond = 0
second = second + 1
If second > 59 Then
second = 0
gosub manageIntMinute
ENDIF
endif
if (PIR1.5==1) then
HSERIN 10, main, [wait ("GET /"), COMMAND]
'do something here
HSEROUT ["Here I am..."]
main:
endif
wend
end

disable
intManagement:
if (INTCON.2 == 1) then
ticks = ticks + 1 ' Count pieces of seconds
If ticks >= 15 Then
if (ACTIVITY_LED == 1) then
low ACTIVITY_LED
else
high ACTIVITY_LED
endif
ticks = 0 'One second elasped - update time
manageIntSecond = 1
endif
INTCON.2 = 0
endif
INTCON.7 = 1
resume

enable
manageIntMinute:
minute = minute + 1 ' manage time update
If minute > 59 Then
minute = 0
hour = hour + 1
If hour > 23 Then
hour = 0
day = day + 1
if day > 6 THEN day = 0
Endif
Endif
return

skimask
- 26th March 2008, 14:08
Timer 0 doesn't overflow 15 times per second...
Timer 0 overflows 15.2587890625 times per second.
15.2587890625 * 4 = 61.03515625
61.03515625 - 60 = 1.03515625 minutes in error.
That accounts for 1 minutes of error.
I think it would be fairly reasonable to say that another 1% or so could possibly be accounted for with the resonator, and maybe another fraction could be accounted for by using a stopwatch and watching an LED.

Do a search on the Olympic Timer here.
Either that or you could keep track of the extra .2587890625 ticks per second in different variables and add them into the 'ticks' variable as appropriate...


subtick var word
.......
......
disable
intManagement:
if (INTCON.2 == 1) then
subtick=subtick + 2587
if subtick => 10000 then
subtick=subtick-10000
ticks=ticks+1
endif
ticks = ticks + 1 ' Count pieces of seconds
..............
ticks = ticks - 15 (ticks = 0) 'One second elasped - update time <-just in case you miss one somewhere, don't zero it, just subtract 15
........
.......

That just might take care of .2587 of the .258790625 of the error.

mister_e
- 26th March 2008, 14:09
AND you have to add the ON INTERRUPT latency :o

Darrel's Elapsed timer? Why not? Or at very least DT Interrupts!

skimask
- 26th March 2008, 14:12
AND you have to add the ON INTERRUPT latency :o
But in this case, latency shouldn't really matter, 'cause the interrupt stubs isn't going to take anywhere near the total period of the interrupt (well, shouldn't anyways), T0 isn't being stopped by anything...

And yes, DT's INTs and Elapsed Timer...always a good plan...

boban
- 27th March 2008, 12:17
Hello Skimask,
thanks a lot for your help, I see.
I am thinking that the easyest way to solve my problem is to use

OPTION_REG = $d5

disable
intManagement:
if (INTCON.2 == 1) then
ticks = ticks + 1 ' Count pieces of seconds
If ticks >= 61 Then
if (ACTIVITY_LED == 1) then
low ACTIVITY_LED
else
high ACTIVITY_LED
endif
ticks = 0 'One second elasped - update time
manageIntSecond = 1
endif
INTCON.2 = 0
endif
INTCON.7 = 1
resume

skimask
- 27th March 2008, 12:51
All other things being equal, that would give you roughly 49-50 seconds of slow error per day. Add one second every half hour, and you'll practically be dead on.