Understanding Interrupts in PIC Microcontrollers
Using interrupts is a must in a professional microcontroller program. Basicly, when an event has occured, an interrupt flag will be set and program will branch to the interrupt address. By this method, you don't need to wait an event in a loop. Interrupts can occur on port change, timer overflow, USART(serial) communication, etc.

You will find a PORTB change interrupt example in the following part of this article.
For Timer0 (TMR0) overflow interrupt, check Timing - Timers page.
For USART (Serial Communication - RS232) interrupt, check Serial Communication page.
The interrupt address locates on 0x04 of program memory. At the beginning of our assembly code, we will point this address to a label.
GOTO main
ORG 0x04 ;On interrupt program will branch here
GOTO interrupt
main:
.instruction
.instruction
.instruction
.
interrupt:
.instruction
.instruction
.
END
When an interrupt occured, program will branch to the interrupt label. Note that, "main" and "interrupt" names are given by us. You can change label names, they are not meaningful for the compiler.
And also, when an interrupt has occured, an interrupt flag will be set. This flags located in INTCON and PIR registers.
Configuring interrupts
To use interrupts, we need to configure them first. There are some special registers (INTCON, PIE, PIR) that control interrupts.
INTCON Register :
INTCON is general interrupt control register. It contains interrupt configuration bits and interrupt result flags.

bit 7 <GIE> : Global interrupt enable bit
| 1 : Enable unmasked interrupts | |
| 0 : Disable interrupts |
bit 6 <PEIE> : Peripheral interrupt enable bit
| 1 : Enable unmasked peripheral interrupts | |
| 0 : Disable all peripheral interrupts |
bit 5 <T0IE> : Timer0 overflow interrupt bit
| 1 : Enable TMR0 interrupt | |
| 0 : Disable TMR0 interrupt |
bit 4 <INTE> : RB0/INT external interrupt enable bit
| 1 : Enable the RB0/INT external interrupt | |
| 0 : Disable the RB0/INT external interrupt |
bit 3 <RBIE> : RB Port change interrupt enable bit
| 1 : Enable the RB port change interrupt | |
| 0 : Disable the RB port change interrupt |
bit 2 <T0IF> : TMR0 overflow interrupt flag bit
| 1 : TMR0 register has overflowed(must be cleared in software) | |
| 0 : TMR0 register did not overflow |
bit 1 <INTF> : RB0/INT external interrupt flag bit
| 1 : The RB0/INT external interrupt occured (must be cleared in software) | |
| 0 : The RB0/INT external interrupt did not occured |
bit 0 <RBIF> : RB port change interrupt flag bit
| 1 : When at least one of the RB7:RB4 pins changed state (must be cleared in software) | |
| 0 : None of the RB7:RB4 pins changed state |
As you can see, several interupts can be controlled by using this register. Disabling an interrupt is called as masking. Non-used interrupts should be masked. Otherwise, they will cause unnecessary branchings to the interrupt label.
An example : Let's write a code which gets PORTB<7:4> change. When PORTB<7:4> changes it will turn-on and turn-off a LED which is connected to PORTA.0.
ORG 0x00
GOTO main
ORG 0x04
GOTO interrupt
main:
BSF STATUS,RP0 ;BANK1
MOVLW h'FF' ;binary = 11111111
MOVWF TRISB ;All bits as input
CLRF TRISA ;All bits as output
BCF STATUS,RP0 ;BANK0
MOVLW b'00001000' ;Prepare configuration byte
MOVWF INTCON ;Mask all interrupts except PORTB change
BSF INTCON,GIE ;Start general interrupts
LOOP: ;Continous loop
GOTO LOOP
interrupt:
BTFSS INTCON,RBIF ;Test if PORTB-change caused the interrupt
GOTO exit_int ;If not exit from the interrupt routine
MOVLW b'00000001' ;
XORWF PORTA,F ;Blink LED
BCF INTCON,RBIF ;We should clear RBIF flag to enable it again
exit_int: ;exit interrupt label
RETFIE ;Enable general interrupts and return
END
The PORTB<7:4> change interrupt is affected by change state on any of the PORTB bits between 4 and 7. When these bits change states, the RBIF bit of INTCON register will be set and program will flow to interrupt routine (label). With the RETFIE instruction program will flow back where it has branched to interrupt routine.
PORTB has two different interrupts. One of them is PORTB<7:4> change interrupt. The other one is RB0/INT pin interrupt. We used PORTB<7:4> change interrupt in the example.
RB0 interrupt
RB0/INT interrupt is affected by rising and falling transition on PORTB bit0. Transition can be configured from OPTION register INTEDG bit. Setting INTEDG bit to '1' will activate rising edge, to '0' will activate falling edge transition. This bit is adaptable for serial communications.

Timer0 overflow interrupt
Timer0 overflow interrrupt can be configured and read from INTCON register. You can find how to use Timer0 interrupt from Timer Interrupts page.
Peripheral interrupts
Peripheral interrupts contains internal EEPROM, Serial Communication (USART, SPI/I2C), timers, analog/digital converting,etc.
Peripheral interrupts are configured by using PIE1 registers. (In some microcontrollers there is also PIE2.) The INTCON register has one bit which is called as PIE, to enable/disable all unmasked peripherals.
PIE1 :
bit 7 <EEIE> : EEPROM write complete interrupt
| 1 : Enables the EE write complete interrupt | |
| 0 : Disables the EE write complete interrupt |
bit 6 <CMIE> : Comparator interrupt enable bit
| 1 : Enables the comparator interrupt | |
| 0 : Disables the comparator interrupt |
bit 5 <RCIE> : USART receive interrupt enable bit
| 1 : Enables the USART receive interrupt | |
| 0 : Disables the USART receive interrupt |
bit 4 <TXIE> : USART transmit interrupt enable bit
| 1 : Enables the USART transmit interrupt | |
| 0 : Disables the USART transmit interrupt |
bit 3 <Unimplemented> : Read as '0'
bit 2 <CCP1IE> : CCP1 (capture/compare/pwm)int. enable
| 1 : Enables the CCP1 interrupt | |
| 0 : Disables the CCP1 interrupt |
bit 1 <TMR2IE> : TMR2 to PR2 match interrupt enable bit
| 1 : Enables the TMR2 to PR2 match interrupt | |
| 0 : Disables the TMR2 to PR2 match interrupt |
bit 0 <TMR1IE> : TMR1 overflow interrupt enable bit
| 1 : Enables the TMR1 overflow interrupt | |
| 0 : Disables the TMR1 overflow interrupt |
The peripheral interrupt flags are in PIR1, peripheral interrupt register. (Again in some microcontrollers there is also PIR2 register.)
PIR1 :
bit 7 <EEIF> : EEPROM write operation interrupt flag bit
| 1 : The write operation completed (must be cleared in software) | |
| 0 : The write operation has not completed or has not been started |
bit 6 <CMIF> : Comparator interrupt flag bit
| 1 : Comparator output has changed | |
| 0 : Comparator output has not changed |
bit 5 <RCIF> : USART receive interrupt flag bit
| 1 : USART receive buffer is full | |
| 0 : USART receive buffer is empty |
bit 4 <TXIF> : USART transmit interrupt flag bit
| 1 : USART transmit buffer is full | |
| 0 : USART transmit buffer is empty |
bit 3 <Unimplemented> : Read as '0'
bit 2 <CCP1IF> : CCP1 interrupt flag bit(capture/compare)
| 1 : TMR1 register capture occured (must be cleared in software) | |
| 0 : TMR1 register capture did not occur |
| 1 : TMR1 register compare occured (must be cleared in software) | |
| 0 : TMR1 register compare did not occur |
bit 1 <TMR2IF> : TMR2 to PR2 match interrupt flag bit
| 1 : TMR2 to PR2 match occured (must be cleared in software) | |
| 0 : TMR2 to PR2 match did not occur |
bit 0 <TMR1IF> : TMR1 overflow interrupt flag bit
| 1 : TMR1 overflowed (must be cleared in software) | |
| 0 : TMR1 did not overflow |
We discussed general interrupts so far. Later, we will study details such as timers, eeprom, serial communication.