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)
    TRISA=0b00111000;       //RA3 always reads HI; RA4 and RA5 read HI in HS mode

    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

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


1 Answer 1


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.

  • \$\begingroup\$ THANK YOU! Yes, embarrassingly obvious, as I was afraid of...! I haven't been using PICs that long, but I've been programming for years. But I guess we all make mistakes like this every now and then, and having a second eye helps catch them. Thanks again. \$\endgroup\$ Commented Aug 15, 2015 at 3:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.