I'm trying to write a software serial implementation and am having trouble with timing. I'm using Timer0 on a PIC18F242 and for some reason it does not seem to be very accurate.
I've been trying to debug the problem by toggling a pin on the microcontroller and watching the frequency on my oscilloscope. The oscilloscope shows that the period is greater than it should be, and I don't know why.
With my full code the period was something like 10us too long, if I remember correctly, but even with a very simple example, such as the one below, the timing is off.
Here is an example:
#include <p18f242.h>
// fuse configurations
#pragma config OSC = HSPLL
#pragma config OSCS = OFF
#pragma config PWRT = OFF
#pragma config BOR = OFF
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config CP0 = OFF
// CALCULATIONS:
// 8Mhz crystal with PLL enabled
// FOSC = 4*8Mhz = 32MHz
// TOSC = 1/FOSC = 31.25ns
// TCY = 4*TOSC = 125 ns
// timer has 1:8 prescaler, therefore timer increments every 8*TOSC = 1us.
main(void)
{
// port configuration
TRISBbits.RB1 = 0; // make RB1 an output
// configure Timer0
T0CONbits.TMR0ON = 0; // disable (stop) the timer
T0CONbits.T08BIT = 0; // 16-bit timer
T0CONbits.T0CS = 0; // use internal instruction clock
T0CONbits.PSA = 0; // use prescaler (prescaler assigned below)
T0CONbits.T0PS0 = 0; // 010 = 1:8 prescaler
T0CONbits.T0PS1 = 1;
T0CONbits.T0PS2 = 0;
T0CONbits.TMR0ON = 1; // enable (start) the timer
while(1)
{
if(TMR0L >= 100)
{
PORTBbits.RB1 ^= 1;
// reset the timer
TMR0H = 0; // MUST SET TMR0H FIRST
TMR0L = 0;
}
}
}
My oscilloscope says the period of this signal is 204.0us, which means that the pin is toggling every 102.0us. If my oscilloscope is correct, then the pin is toggling 2us too late every time.
Interestingly, changing if(TMR0L >= 100)
to if(TMR0L >= 102)
results in the same period, but going below 100
or above 102
results in the period decreasing and increasing, respectively.
Why is this happening?
Also, as I mentioned, having additional code in the main loop seems to exacerbate the problem. Why would that be?
Best Answer
If you want accurate timing, use interrupts (i.e. reset the timer value in it's interrupt routine).
As you have it it only checks the timer value once per loop, so if you add code into the loop the checks become spread out.
The interrupt will switch context as soon as the timer overflows, so it doesn;t matter how much code is in your loop then.