0
\$\begingroup\$

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?

\$\endgroup\$
2
  • \$\begingroup\$ Ho much 'stuff' are you doing in your 'timer' & 'set_led' routines? \$\endgroup\$ Commented Feb 8, 2016 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\$ Commented Feb 8, 2016 at 8:28

2 Answers 2

6
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\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\$ Commented Feb 8, 2016 at 8:24
0
\$\begingroup\$

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

\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.