Interrupt on change 16f877A using XC8 - Page 1
Forum section: electronics / general

author: djsb
author_rank: Super Contributor
timestamp: August 20, 2021, 10:41:43 am
content: I'm trying to get an interrupt on change example working on an 16F877A using XC8.Code:/** File: Main.c* Author: David Briscoe* PIC: 16F877A w/Ext OSC @ 8MHz, 5v* Program: 11_IOC* Compiler:XC8 (v2.31, MPLAX X v5.45)* Program Version: 1.0*** Program Description: This Program Allows PIC16F877A to toggle an LED based* on the state of a IOC** Hardware Description: An LED is connected via a 220R resistor to PIN D1 and* a switch is connected to PIN B0** Created August 20th, 2021*//********************************************************************************Includes and defines******************************************************************************/#define _XTAL_FREQ 8000000#define BUTTON PORTBbits.RB0 // pushbutton#include "16F877A_Internal.h"/******************************************************************************** Function: void initMain()** Returns: Nothing** Description: Contains initializations for main** Usage: initMain()******************************************************************************/void initMain(){/////////////////////////// Configure Ports////////////////////////// Set PIN D1 as outputTRISDbits.TRISD1 = 0;// Set PIN D2 as outputTRISDbits.TRISD2 = 0;// Set PIN B0 as inputTRISBbits.TRISB0 = 1;OPTION_REGbits.nRBPU = 0; // turn on pull-up// On 16F877A ALL of PORT B is pulled HI. NO INDIVIDUAL BITS CAN BE SET.// On 16F887 INDIVIDUAL PORTB pins can have weak pull-ups enabled.// Turn LED's ONRD1 = 1;RD2 = 1;__delay_ms(500);// Wait 0.5s then turn them offRD1 = 0;RD2 = 0;//////////////////////////// Configure Interrupts////////////////////////// enable detection of falling edges on RB0//IOCBNbits.IOCBN0 = 1;INTCONbits.RBIE = 1; // enable interrupt on change// enable IOC interruptOPTION_REGbits.INTEDG = 0;INTCONbits.GIE = 1; // interrupt global// Enable global interrupt//ei();}/******************************************************************************** Function: Main** Returns: Nothing** Description: Program entry point******************************************************************************/void main(void) {initMain();while(1){; // do nothing}return;}/******************************************************************************** Function: void interrupt isr(void)** Returns: Nothing** Description: Service port change interrupt which is triggered on any valid* transition on IOC-enables input pin caused by external button* press. Toggles LED on every enabled transition (high -> low)******************************************************************************/__interrupt() void isr(void){// clear IOC flags on PORTB//INTCONbits.RBIF = 0;//IOCBF = 0;// toggle LED//if (INTCONbits.RBIF){if (!BUTTON){RD2 = ~RD2;}INTCONbits.RBIF = 0;}}The code compiles OK with no errors or warnings. I just can't get it to work.Everything is wired up correctly and the switch works OK (I made a for() loop in main() to flash the LED's 5 times on a button press). I can't get the code above to work. I'm not really understanding how the interrupt logic works. Where am I going wrong? Thanks.David.

author: mikerj
author_rank: Super Contributor
timestamp: August 20, 2021, 11:47:32 am
content: The PICs interrupt on change peripheral internally stores a copy of the port's input pin values which is compared against the live input pin values in order to detect a change. After a change is detected you have to do two things in this order: 1) Read or write the port. This saves the new pin state for the next comparison. 2) Reset the interrupt flag.If you don't read/write the port then the mismatch condition remains and RBIF will immediately get set after you clear it.

author: Ian.M
author_rank: Super Contributor
timestamp: August 20, 2021, 11:51:37 am
content: PIC16F877A only has IOC on RB4-7. Your button on RB0 is *NOT* on an IOC capable pin (though it is on an INT pin).Also, Microchip's older 8 bit PICs' IOC implementation is known to be buggy! BEWARE of theRACE CONDITION!You must read the whole port*with the active IOC pins, and attempt to clear its IOC interrupt flag at the beginning of your ISR immediately after confirming the interrupt source, and store the port value for the ISR to decode. DO NOT re-read the port later on in the ISR or read it in your main program (apart from ONCE immediately before enabling GIE) or you *WILL* loose some interrupts.The remaining port B pins can be safely used as outputs, but only if you write the *whole* port without using an instruction that performs a RMW (read modify write) operation. *DO* *NOT* attempt direct use of individual pins of the port.* Due to the race condition bug, you must use a very specific sequence to read the port and clear the IOC interrupt flag without missing pin transitions. See RACE CONDITION link above.