# Using the PORTB interrupt on change feature with XC8

I'm making a program to learn to use portB interrupts on the PIC18F2520.

I made a program, but it's not working correctly. The program is all time lighting and turning off the led, and the led's state should be changed only with a change in some portB pin (I'm using a button).

I show you my code:

#include <xc.h>

#define _XTAL_FREQ 4000000
#define LED_OUTPUT LATC3

unsigned char config1;
unsigned int timer_value;
unsigned int tpr;
int counter=0;

void main(void) {

TRISCbits.RC3 = 0;

EnablePullups();

INTCONbits.RBIE = 1;
//RCONbits.IPEN = 0;
//INTCONbits.PEIE = 1; // Enable Perpherial Interrupt

INTCONbits.GIE = 1; // Enable Global Interrupt

INTCON2bits.INTEDG0 = 1;
//INTCON2bits.NOT_RBPU = 0;
//TMR1 = 0;
while(1)
{
__delay_ms(100);

}
}

void interrupt isr(void) {

// Was it the port B interrupt on change?
if (INTCONbits.RBIF) {
// Dummy read of the port, as per datasheet

// Use XOR to toggle the pin, saving a variable
LED_OUTPUT ^= 1;

// Reset the interrupt flag
INTCONbits.RBIF = 0;

if (PORTB) {
asm("nop");
}
__delay_ms(100);
}

}


Here I show you my schematic in Proteus!.

The button is attached to pin RB0. Let's see if we can find the mistakes together.

Greetings!

• A schematic would help to ensure there are no obvious errors. Include a note if you're using internal pullups. – David Nov 29 '14 at 18:20
• Thank you for remembering me the schematic, I forgot it! I have uploaded the post with it. – user58401 Nov 29 '14 at 23:28
• Note that if you are using Proteus instead of real hardware (or the MPLAB simulator) then results may be different. Hardware is definitely the way to go. – David Nov 30 '14 at 9:09

One thing that would help (and is definitely best practise) would be to check what is causing the interrupt. You should also be writing to the latch (LATx register) on an 18F and you need to disable analogue functions.

Sample code for this might look like:

#define LED_OUTPUT LATC3

void main(void) {
// .. other setup here ..

// Disable analogue functions

// .. interrupt setup ..
}

void interrupt isr(void) {

// Was it the port B interrupt on change?
if (INTCONbits.RBIF) {
// Dummy read of the port, as per datasheet
if (PORTB) {
asm("nop");
}

// Use XOR to toggle the pin, saving a variable
LED_OUTPUT ^= 1;

// Reset the interrupt flag
INTCONbits.RBIF = 0;
}
}


To use the interrupt-on-change feature with a specific input pin is a bit harder, but you could check in the ISR whether the current input value is 0 or 1 (whichever is most relevant). Don't forget to debounce your switch also, either in hardware or in software.

## PORTB mismatch condition

Note this cryptic portion of the datasheet in section 9.1 "INTCON Registers" regarding the RBIF bit:

Note 1: A mismatch condition will continue to set this bit. Reading PORTB will end the mismatch condition and allow the bit to be cleared.

In order to stop your code getting stuck in the ISR (repeatedly setting RBIF) you should read the port in your ISR even if you do nothing with the value. The code sample above shows this.

## Errata

The errata for your device is another excellent place to go. Here's the errata for the 18F2520, however in this case there don't seem to be any relating to interrupts or the data ports.

## Special functions

You might also need to disable other special functions on PORTB. Specifically the analogue functions, which always need to be disabled if you are using the pin as a digital input.

// Make just AN12 (on pin B0) a digital input

// Make all external pins digital inputs


## Alternative interrupt mechanisms

Note that on the 18F2520 you have three dedicated external interrupts available as INT0 to INT2. These map to pins B0 to B2. See section 9.6 "INTx Pin Interrupts" where we find the following:

External interrupts on the RB0/INT0, RB1/INT1 and RB2/INT2 pins are edge-triggered. If the corresponding INTEDGx bit in the INTCON2 register is set (= 1), the interrupt is triggered by a rising edge; if the bit is clear, the trigger is on the falling edge.

You could use the INT0 interrupt instead, which means you are not depending on the values from all of PORTB. Because they are edge triggered you will get less spurious interrupts, but you will still need to debounce the switch.

• Oh sorry! my device is PIC18F2520! – user58401 Nov 29 '14 at 17:57
• I have run your code but as a result the led is all time lighting and seems to do not respond to button's clicks. How could I disable other special functions on PortB? I would be grateful if you could tell me how could I know what the current input value is in the ISR. Thank you very much!. – user58401 Nov 29 '14 at 23:47
• Answer updated. – David Nov 30 '14 at 9:08
• First of all, thank you very much for making an effort for helping me. I thought i couln't just check 1 bit, but all 0 to 3 from portB. I took a look to your explanation I guess it will succeed. I will update news as soon as I try that!. Greetings!. – user58401 Nov 30 '14 at 19:45
• I have updated the post with some changes. First thing is that I have been able to blink a Led when there is some rising/falling edge in portB, the problem is that if you see my proteus picture, i have to connect the whole portB, why could be this?I tried the ADCON1 |= 0b0011; but it didn't work. Second, the aim of this program is changing the led state when you push, and change it again when you stop pushing, but the program is actually lighting the led, waiting 100ms and then turning it off again. FIXED led blinking: it worked when I cleared the flag after the portB reading as you mentioned. – user58401 Nov 30 '14 at 23:00