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
    notimer0        movf    pclath_temp, w  ; restore state
                    movwf   PCLATH
                    swapf   status_temp, w
                    movwf   STATUS
                    swapf   w_temp, f
                    swapf   w_temp, w

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?

  • \$\begingroup\$ Ho much 'stuff' are you doing in your 'timer' & 'set_led' routines? \$\endgroup\$ – brhans Feb 8 '16 at 0:05
  • \$\begingroup\$ 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. \$\endgroup\$ – Charlie Skilbeck Feb 8 '16 at 8:28

Instead of using the 16-bit timer 1, which requires you to reload the value in code, use the 8-bit timer 2, which has a preset register PR2. Load PR2 with 250, with a prescaler value of 1:1. It will then interrupt every 1 ms and reload it self automatically so no latency problems.

The inside your interrupt, just increment a single byte counter. In your base routine, use a while loop and test whenever the counter is equal to 100 (i.e. 1/10 of a second has elapsed). Then reset the counter and do your clock/led updating and resetting there in the base level, not in your interrupt routine.

  • \$\begingroup\$ Perfect, works a treat, thanks. I used prescale=10 and count to 10 before incrementing the digit. Also, I had to use 249 for the reload value, which seems to be bang on accurate. I think the problem with using timer1 is that the timer0 (higher frequency) ISR is often running when timer1 wraps, causing the drift. \$\endgroup\$ – Charlie Skilbeck Feb 8 '16 at 8:24

instead of resetting timer 1 just update the alarm to be 0.1s further in the future


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.