Timer on PIC18 not getting close to desired interrupt period time

interruptsmicrochippictimer

I tried to setup Timer0 on a Microchip PIC18F46K22 so that it will trigger an interrupt and toggle an LED. However, it takes at least 42 µs for the LED to toggle.

At an increment rate of Fosc/4 (= 8 MHz/4 = 2 MHz), a TMR0 preload of 0xFF and a two cycle loss when writing to TMR0 I expect to get an interrupt every 1.5 µs, at least on paper. Even if I factor into some instruction cycles for my LED operations, I can't figure out why it takes 42 µs. What's wrong here?

Here's my code:

volatile unsigned char ledStatus;


void greenLEDon(void)
{
    LATCbits.LATC5 = 0;
}

void greenLEDoff(void)
{
    LATCbits.LATC5 = 1;
}


void interrupt isr(void)
{
    if(TMR0IE && TMR0IF)
    {
        if(ledStatus == 0)
        {
            ledStatus = 1;
            greenLEDon();
        }
        else
        {
            ledStatus = 0;
            greenLEDoff();
        }

        TMR0IF = 0;            // Clear interrupt flag
        TMR0 = 0xFF;           // preset for timer register
    }
}


void setup(void)
{
    OSCCON = 0b01100010;    // 8 MHz, internal

    // General outputs
    TRISCbits.TRISC5 = 0;   // RC5 (green LED)

    INTCON = 0b01000000;    // PEIE on (Peripheral Interrupt Enable)
    INTCONbits.GIE = 1;     // General Interrupt Enable


    // Timer0 Registers
    T0CONbits.T0CS = 0;   // TMR0 Clock Source Select bit: 0 = Internal Clock (CLKO)
    T0CONbits.T0SE = 0;   // TMR0 Source Edge Select bit: 0 = low/high
    T0CONbits.PSA  = 1;   // Prescaler Assignment bit: 0 = No Prescaler is assigned to the Timer0
    TMR0 = 0xFF;           // preset for timer register
    T0CONbits.TMR0ON = 1;
    T0CONbits.T08BIT = 1;
    INTCONbits.TMR0IE = 1;
}


void main(void)
{
    setup();

    while (1)
    {
    }
}

enter image description here

Best Answer

Stop and think about what it means to set timer 0 to FFh. That means it will overflow and set the interrupt condition on the very next clock. The clock is stopped for a couple of cycles when TMR0 is written, so this will occur 3 cycles (if I remember right) after the write. How can you possibly expect the interrupt mechanism to repond that fast. The return from interrupt (RETFIE) takes 2 cycles alone, and the entry into the interrupt routine at least another 2 cycles (it's essentially a call), then there is the code to save and restore context. This is all overhead even if your application code does nothing.

Expecting a PIC 18 to interrupt every 3 cycles is rediculous, as even a cursory look at the datasheet should have made obvious. Using a compiler on top of that only makes the minimum interrupt frequency that can actually be handled worse.

If you want to set up a periodic interrupt, start with timer 2, not timer 0. That timer has a period register for exactly this purpose.

Even if you are stuck with timer 0 to create a periodic interrupt, you should be adding a value into TMR0, not setting it to a fixed value. That way there is no accumulating error and the number of cycles from the timer 0 overflow to the instruction that writes it to a new value doesn't matter.