Electronic – PIC16F684 Timer 2 interrupts occurring much more quickly than expected

cmicrocontrollerpic

TL;DR: Timer 2 interrupts (1:4 postscaler, PR2=0x3F) occurring at about 139 kHz instead of expected 19.5 kHz. Changing postscaler to 1:1 has no effect.


I am using a PIC16F684 microcontroller for a project and have been struggling to get it to work. In order to debug, I have stripped my code down to almost nothing: using the interrupt of Timer 2 to toggle a pin.

I am using no prescaler (1:1), a 1:4 postscaler, and a period value of PR2=0x3F. I am also using a 20 MHz crystal. Therefore, I would expected the interrupt frequency to be:

(FOSC/4) / (PR2+1) / postscaler = (20 MHz / 4) ÷ (0x3F + 1) ÷ 4 = 19.5 kHz

Also, the period would be the inverse of that, or 51.2 us.

In my ISR, I set a pin high, add in some delay time (by setting some variable equal to zero), and clear the pin. So, I would expect to see these pulses every 51.2 us.

Instead, my oscilloscope shows pulses every 7.2 us. Taking the inverse, that corresponds to a frequency of about 139 kHz.

Another interesting fact is that if I change T2CON such that the postscaler is 1:1 (i.e., T2CON=0b00000000) instead of 1:4, the result is exactly the same: pulses every 7.2 us. Redoing my above calculation, I would expect a frequency of 78.125 kHz, or a period of 12.8 us.

Just to eliminate this possibility, I have run other programs on this PIC chip, and they have behaved as expected, with correct timing. But none of those other programs used Timer 2. So, the chip itself and the crystal probably aren't the problem.

Any suggestions would be appreciated. I'm afraid it's going to be something embarrassingly obvious, but I've looked over the data sheet several times and can't seem to find any obvious problems.

Here's my code:

#include <xc.h>

#pragma config FOSC = HS        //oscillator selection; external crystal on pins 15 and 16
#pragma config WDTE = OFF       //disable watchdog Timer
#pragma config PWRTE = OFF      //power-up timer disabled
#pragma config MCLRE = OFF      //use MCLR pin (internally tied to Vdd) as a digital input
#pragma config CP = OFF         //code protection disabled
#pragma config CPD = OFF        //data code protection disabled
#pragma config BOREN = OFF      //disable Brown-Out Reset
#pragma config IESO = OFF       //disable Internal External Switchover
#pragma config FCMEN = OFF      //disable Fail-safe Clock Monitor

#define TestPin RA0
char k;

void main (void)
{
    //ground unused pins (as outputs)
    TRISC=0b00000000;
    PORTC=0b00000000;
    TRISA=0b00111000;       //RA3 always reads HI; RA4 and RA5 read HI in HS mode
    PORTA=0b00111000;

    PR2=0x3F;               //Timer 2 period
    T2CON=0b00011000;       //Timer 2 off; 1:1 prescaler, 1:4 postscaler

    PIR1bits.TMR2IF=0;      //clear Timer 2 interrupt flag
    INTCONbits.PEIE=1;      //enable peripheral interrupts (e.g., PWM)
    PIE1bits.T2IE=1;        //enable Timer 2 interrupt
    T2CONbits.TMR2ON=1;     //turn on Timer 2
    INTCONbits.GIE=1;       //turn on all interrupts
    while(1);               //hang up, wait for interrupts
}

void interrupt ISR()
{
    if (PIR1bits.TMR2IF==1)
    {
        PIR1bits.TMR2IF==0;     //clear interrupt flag

        TestPin=1;              //toggle pin
        k=0;
        k=0;
        k=0;
        k=0;
        k=0;
        TestPin=0;
    }
}

Equipment used:
– PIC16F684 w/PICkit 3
– MPLAB X IDE v3.00, XC8 compiler
– Dell Inspiron 6400 laptop, Linux Mint 17 Xfce

Best Answer

You have a bug in the ISR. Where you think the interrupt flag is cleared it is actually compared to zero. Consequently, the ISR gets called again immediately after completion. Change the line to

PIR1bits.TMR2IF=0;     //clear interrupt flag

All should be well after that.