Electronic – Precise timing with a PIC18 microcontroller

crystaloscillatorpicserialtimer

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.