PIC16 10hz timer with timer1 and 1Mhz clock

I need to increment a stopwatch at 10hz using timer1 on a PIC16F628. The external clock is 1Mhz, supplied by a packaged oscillator (EPSON sg8002db). With no prescaler, the value to set the timer to (I think) should be:

clock ticks in 1 second: 1000000

timer ticks in 1 second: 250000 (clock/4)

timer ticks in 1/10th of a second: 25000 (timer ticks/10)

So: 65536 - 25000 = 40536

but I need to consider the latency from timer overflow to when the clock value is reset - this is the number of cycles it takes from the overflow occurring to when I set the timer value.

The IRQ code is:

    irq             movwf   w_temp          ; save state
swapf   STATUS, w
clrf    STATUS
movwf   status_temp
movf    PCLATH, w
movwf   pclath_temp
clrf    PCLATH

btfss   PIR1,TMR1IF     ; timer1 IRQ?
goto    notimer1
bcf     PIR1,TMR1IF     ; yes, clear it

movLw   T1SPEED >> 8    ; reset timer1
movwf   TMR1H
movLw   T1SPEED & 0xff
movwf   TMR1L           ; timer1 is off and running again

call    timer           ; increment clock

notimer1        btfss   INTCON, T0IF    ; timer0 IRQ?
goto    notimer0
bcf     INTCON, T0IF    ; yes, clear it

call    led_set         ; update display

btfss   PORTA,6         ; button pressed?
goto    nobut
clrf    digit0          ; yes, reset clock
clrf    digit1
clrf    digit2
clrf    digit3
nobut
notimer0        movf    pclath_temp, w  ; restore state
movwf   PCLATH
swapf   status_temp, w
movwf   STATUS
swapf   w_temp, f
swapf   w_temp, w
retfie


Which looks to me like somewhere in the region of about 14 cycles, so LATENCY = 14

65536 - (25000 - LATENCY) = 40550

but this gives a clock which is far too slow, losing multiple seconds per minute. If I change the LATENCY value to ~200 (eg set timer1 to 40736), it's close - within 1 second per minute, but still not accurate. In fact, with LATENCY = 199, it's too fast, and with LATENCY = 200, it's slow.

I can't see where these extra cycles are being spent - it sets the clock value first thing in the IRQ routine. I can't find anything in the datasheet about timer1 being stopped during an interrupt routine, but is it? If so, that would be a bummer, because the routine takes a variable number of cycles depending on which digits overflow.

Is it necessary to pick 2 different reload values and alternate between them in order to hit exactly 10hz?

• Ho much 'stuff' are you doing in your 'timer' & 'set_led' routines? – brhans Feb 8 '16 at 0:05
• Not much, but you're right that they're sort of causing the problem. I think it's because the timer0 (higher frequency) ISR is often running when timer1 wraps, causing the drift. – Charlie Skilbeck Feb 8 '16 at 8:28