This sort of delay scales inverse-linearly with clock speed as long as there are no interrupts or similar running (ie double clock speed and delay halves).
This is useful: PIC delay routines.
To work out how many loops are needed you 1st need to know the number of clock cycles or uS per instruction at the clock speed used.
For the PIC that you have shown, most instructions take one "unit" of time.
This will vary among processor families.
Some take 4 crystal clock cycles per instruction, some take 1, some take more.
Assume you know Tw = basic instruction unity time at W Mhz.
In a PIC most instructions execute in Tw at W Mhz where Tw is usually 4/F_Mhz in the older style 14 bit core PICs. .
BUT check this for the processor you are using.
In a PIC the decfsz skip instruction takes one time unit if the test fails (register is non zero) and two time units if the test passes - register is zero and skip occurs.
Some processors may take one unit if test fails or passes or two whether it fails or passes or ... - check what applies for the processor you are using.
So here the main timing loop is 3 time units usually and 4 units only when the register reaches zero.
Work out the basic unit time (say for nop instruction) at clock speed W Mhz and one mS then needs a count of x in 'movlw x' of
If a NOP took 0.5 uS then
- Count = 1000/0.5/4 = 500.
As an 8 bit counter can only count to 255, if variable_temp is 8 bit then this count is too large and a double byte counter is needed.
Something more like below.
Here you have inner and outer loops.
Each inner loop runs for K_Count_Low loops and the inner loop is called K_Count_High times. Counters are 8 bit so should be set to <= 255.
Maximum delay is ~~~= 255 x 255 x 3 x Tw.
Outer loop handling , included nops etc change this. Seeing exactly how is part of learning how this all works.
movlw K_count_High
movwf Counter_High
LoopOuter:
movlw K_count_Low
movwf Counter_Low
LoopInner:
nop
decfsz CounterLow, 1
goto LoopInner
decfsz CounterHigh, 1
goto LoopOuter
Call "We are the Borg of Pentium. Division is futile! We will approximate you"
E&OE.
The timers divide the clock down by integer values. The long term average coming out of a timer will be the same as that of the oscillator.
It sounds like your timer period is off by 1. Read the datasheet carefully. You probably didn't put the right value into the period register. Usually these timers work such that the period is the period register value plus 1.
Another possible source of error is how the crystal is driven. The accuracy is specified for a particular load capacitance, which can often be a bit unpredictable since stray capacitance can be a significant part of the total. However, that would not explain why two timers running from the same clock don't agree on elapsed time. Only a firmware bug can cause that.
Best Answer
I would use timer 2 to set up a periodic interrupt. 1 ms (1 kHz frequency) is usually a good interrupt period. At 4 MHz clock frequency you have 1 MHz instruction rate, so 1 ms interrupt would be every 1000 instructions. You should be in and out of the interrupt in a few 10s of instructions, so that is a small overall burden on the processor.
To get long delays, set up a counter that the interrupt routine decrements if it is not zero. When this counter reaches 0, the interrupt routine sets a global flag. If you need 1 ms resolution for your 1 s time, then you make this a 16 bit counter. If 5 ms is enough resolution, then you divide the 1 ms tick in the interrupt routine by 5 to make a 5 ms tick, and decrement a single byte counter every 5 ms. If you really only ever need 5 ms timing, then you can set up the periodic interrupt to be 5 ms (every 5000 instruction cycles).
To use the delay, the forground code clears the delay elapsed flag and writes the delay time in units of 5 ms to the counter. The foreground code checks the flag regularly to see if the delay has elapsed. In the mean time, it can perform other processing.