Interrupt-on-Change occurs twice on PIC16F1825

I am having a mind blowing misbehaviour in my PIC16F1825. Basically I am using pin 3 (RA4) to toggle a LED using an Interrupt-on-Change. Problem is, although it toggles the led whenever I press the button (which is externally pulled up), after a second, the LED toggles again, which means that the interrupt routine was called again, and it shouldn't, even though I clear the interrput flag.

Here's the code (only function IOconfig_portA() and toggleLed() matter for the problem at hand:

#include <stdio.h>
#include <stdlib.h>
#include <PIC16F1825.h>

char temp=0;

void interrupt toggleLed() {

INTCONbits.GIE = 0;

if(INTCONbits.IOCIF == 1) {
temp = PORTA;
INTCONbits.IOCIF = 0;
LATC3 = ~LATC3;
for (int i = 0; i < 100000; i++);
}

INTCONbits.GIE = 1;
}

void CLOCKconfig() {
OSCCON  = 0x6A; //Sets the internal oscillator fosc = 4 MHz
OSCSTAT = 0x00;
OSCTUNE = 0x00;
}

void IOconfig_portA() {
ANSELA = 0x00;           // All ports set as DIGITAL
TRISAbits.TRISA4 = 1;    // Set as input
//OPTION_REG &= 0x7F;    // Clear bit 7, to enable the weak pull-up
//WPUA |= (1<<2);        // Enable the WPU for RA2
CM1CON0 = 0x00;
CM1CON1 = 0x00;
IOCANbits.IOCAN4  = 1;  // Generate Interrupt on Negedge
IOCAP  = 0x00;  // Disable Interrupt on Posedge
INTCON = 0x88;    // Enable GIE and IoC interrupts
}

int main(int argc, char** argv) {

TRISC = 0;

CLOCKconfig();

ANSELC &= 0x00; // All bits on port C are set to Digital I/O's
TRISC  &= 0x00; // All bits on Port C are set to Outputs
APFCON0 |= (1<<5); // Don't use special features on Pin RC3
APFCON1 |= (1<<2); // Don't use special features on Pin RC3

IOconfig_portA();

LATC |= (1<<3);

while(1) {

}

return (EXIT_SUCCESS);
}


• Have you got any debouncing on the switch? Is it possible as you are holding the button pressed you release it slightly which causes a change? You could test this by replacing the button with a voltage source. – Tom Carpenter Nov 22 '15 at 16:43
• The for loop is supposed to be for debouncing. Plus, the same thing happens when I simulate this firmware on Proteus, where you have ideal switches – José Fonseca Nov 22 '15 at 16:45
• Fair enough. Another thing, I feel I should point out that for (int i = 0; i < 100000; i++); that has no place in an ISR! First rule of interrupts is the handlers should finish as quickly as possible and not block for long periods of time. Also the INTCONbits.GIE = ... lines are unnecessary (it is done internally by the PIC when entering and exiting the ISR). – Tom Carpenter Nov 22 '15 at 16:48
• I know. That line for sanity check purposes. Otherwise, the led would double toggle so quickly that I couldn't even see the led blinking – José Fonseca Nov 22 '15 at 16:51
• Most likely your issue is that you aren't clearing the interrupt flag. From the datasheet: "The IOCIF Flag bit is read-only and cleared when all the Interrupt-on-Change flags in the IOCxF register have been cleared by software.". So writing to the IOCIF bit is pointless. You need to instead write to the IOCAF bit. – Tom Carpenter Nov 22 '15 at 16:52

According to Page 92 of the PIC16F1825 Datasheet:

Note 1: The IOCIF Flag bit is read-only and cleared when all the Interrupt-on-Change flags in the IOCxF register have been cleared by software

So basically when you do this in your code:

INTCONbits.IOCIF = 0;


It actually does nothing at all - the interrupt flag is not cleared because that bit is read-only.

In order to clear the interrupt flag for Interrupt-on-Change sources, you need to write to the IOCAF register to clear the flags you want. If you want to clear them all, you can do:

IOCAF = 0x0;


If you only want to clear certain bits, like IOCAF4 in your case, you can do:

IOCAFbits.IOCAF4 = 0;

• Good point, you are right! But it didn't solve the problem. Notice that, although the flag was not being cleared properly, the problem is that the Interrupt routine is being called twice per push, and the Interrupt routine call happens regardless of the status of the flags. – José Fonseca Nov 22 '15 at 18:18
• found the problem. Check my answer. – José Fonseca Nov 22 '15 at 21:37

Found the answer! It is quite obscure, I must say. Basically, the PIC has a Watchdog timer that resets the device every 4 seconds. Oddly enough, it comes enabled by default (does that even make sense?). To disable the watchdog timer, you need to add the following pre-compiler directive:

#pragma config WDTE = OFF

• Seems feasible. Though the claim was you weren't detecting extra resets. If there is a standard software timer that is always loaded (to handle the WDT clearing), then doing long delays in another interrupt might unexpectedly expire the WDT too. – Nedd Nov 22 '15 at 23:59
• I didn't detect it because my unique led blinking pattern was longer than the watchdog period. – José Fonseca Nov 23 '15 at 0:00
• OK then,,, Good Luck... – Nedd Nov 23 '15 at 0:04

Is it possible the circuit resets due to multiple interrupts (eg. stack over flow).

On a reset you would repeat all of the code and re-enable the interrupt. (As in the other answer there could be switch bounce that causes this to.)

The counter delay of 100k may be too short, try increasing this to something humanly measurable like a total delay of 500ms if possible.

Also to test for an unwanted reset give yourself a one time power up routine to flash a unique code on the LED, that will tell you if the whole thing recycled.

• Did that, and no, it does not seem to overflow. – José Fonseca Nov 22 '15 at 18:16
• Found the problem. Check my answer :) – José Fonseca Nov 22 '15 at 21:38