dsPIC/PIC24 Interrupts (using XC16 and MPLAB X)

Moving from 8-bit PIC to 16-bit, the interrupt system is a little bit different. In 8-bit we had only 2 interrupt priority vectors. So when an interrupt happens and if it is from peripheral assigned as high priority, it goes to the high priority address. The address points to the interrupt function address which will contain a bunch of “if” conditions to find who created the interrupt. Assume we have 2 or 3 peripherals set for interrupt (still I’m talking about 18F’s). When an interrupt happens, we have to go through the peripherals interrupt flags to find which peripheral created the interrupt. The more the number of peripherals, the bigger the interrupt function is going to be. If too many interrupts that are assigned as high or low then we might end up with latency in attending the new interrupts while handling the current task. Not to say that 16 bits don’t have that problem, but they give us more priority levels.

dsPIC and PIC24 gives up to 8 levels interrupt priority levels (which you have to check the datasheet of the part number to makes sure 8 or less). Each peripheral has its own vector address. So when an interrupt happens, no need to go through a bunch of “if” conditions to find which peripheral created the interrupt. Because of the individual address for each peripheral interrupt, the code will look much cleaner and easier to debug. The time taken to attend the interrupts will also be more efficient (largely depended on the best assignment of the priorities). The interrupt address table also has a mirror copy called the Alternate Vector Interrupt Table (AVIT) address which is very useful for debugging.

We do also have software trap. Which means when the firmware crashes, an interrupt will happen. This feature can be used as a try-catch block to handle firmware crashes (Which should not happen in a well written code, but during debugging).

 

Documents to read :

http://ww1.microchip.com/downloads/en/DeviceDoc/70000600d.pdf

http://microchip.com/mplabxc16guide (compiler documentation)

The document linked above only gives an outline. Please read the interrupt section in the datasheet of the device under use.

Please read the Interrupt chapter in the compiler documentation. It explains various options available in the interrupt system.

 

Example: GPIO interrupt

Lets see an example by setting a GPIO interrupt. The device I have is dsPIC33FJ128GP202.

The part has 3 external interrupt pins. How did I know this? From the Pin out table given in the datasheet.

 

image

 

Only one pin (pin 16) has INT0 labeled. The other interrupts can be enabled with the help of peripheral pin select feature and assign a pin to it.

image

 

Interrupt example

In order to define an interrupt in xc8 we use the keyword “interrupt”. In XC16 it is like the code snippet given below

void __attribute__((__interrupt__, __auto_psv__)) _INT0Interrupt() { //interrupt code here }

We do have the interrupt keyword and a new keyword “auto_psv”. XC16 compiler documentation and the interrupt documentation gives a detailed explanation of what it is than I can possibly explain here. For the examples I have here, I would leave it up to the compiler for context saving. The interrupt line looks pretty long. But XC8 gives us a macro that simplifies the interrupt setup.

#define _ISR __attribute__((interrupt))

Using the above macro given by the compiler, we would have interrupt setup to very simple as

void _ISR _INT0Interrupt() { //interrupt code here }

But the compiler will throw a warning during compiling as

elf-cc1.exe: warning: _INT0Interrupt PSV model not specified for ‘_INT0Interrupt’;

assuming ‘auto_psv’ this may affect latency

The warning can be eliminated by specifying your own macro like

#define _ISR_PSV __attribute__((__interrupt__, __auto_psv__))

 

Now in order to set the interrupt configuration we can do it two ways. One is by going in and directly setting the register values or using the library given in ports.h header file. I’ll use the library option.

Peripheral library documentation can be found in your local machine following the installation of the compiler under “C:\Program Files (x86)\Microchip\xc16\<version>\docs\periph_libs” folder.

Please check the ports.h header file and the documentation for more information.

 

XC16 Interrupt code sample

 

/* * File: dsPICtest.c * Author: Singular Engineer * * Created on June 29, 2014 */ #define FCY 40000000ULL //Fcy = (Fosc/2) Fosc = 80MHz #define _ISR_PSV __attribute__((__interrupt__, __auto_psv__)) #include <xc.h> #include "config.h" #include <libpic30.h> #include <ports.h> int i = 0; void SetupClock(void); int main(int argc, char** argv) { SetupClock(); TRISBbits.TRISB0 = 0; //RB0 as output TRISBbits.TRISB7 = 1; //RB7 as input LATBbits.LATB0 = 1; //Turn on the led/RB0 //Setup INTO ConfigINT0(FALLING_EDGE_INT & EXT_INT_PRI_5 & EXT_INT_ENABLE & GLOBAL_INT_ENABLE); while(1) { } return 0; } void _ISR_PSV _INT0Interrupt() { LATBbits.LATB0 = ~LATBbits.LATB0; //toggle RB0 pin __delay_ms(200); //software debounce -sort of IFS0bits.INT0IF = 0; //clear the flag for next interrupt } void SetupClock() { //Setting N1 to 4 (N1 = n+2); n = N1-2 CLKDIVbits.PLLPRE0 = 0; CLKDIVbits.PLLPRE1 = 1; CLKDIVbits.PLLPRE2 = 0; CLKDIVbits.PLLPRE3 = 0; CLKDIVbits.PLLPRE4 = 0; //Setting M to 32 (M = m+2); m = M-2 PLLFBDbits.PLLDIV0 = 0; PLLFBDbits.PLLDIV1 = 1; PLLFBDbits.PLLDIV2 = 1; PLLFBDbits.PLLDIV3 = 1; PLLFBDbits.PLLDIV4 = 1; PLLFBDbits.PLLDIV5 = 0; PLLFBDbits.PLLDIV6 = 0; PLLFBDbits.PLLDIV7 = 0; PLLFBDbits.PLLDIV8 = 0; //Setting N2 to 2 CLKDIVbits.PLLPOST0 = 0; CLKDIVbits.PLLPOST1 = 0; }