dsPIC33A Interrupt and Exception Usage

Last modified by Microchip on 2025/02/03 13:52

Overview

The dsPIC33A Interrupt and Exception operation must be carefully initialized by the application developer. This page summarizes the key initialization and usage steps required for both interrupt exceptions and general exceptions.

Further information regarding interrupt and exception usage is provided by the MPLAB® XC-DSC C Compiler User's Guide (DS50003589).

Initialization

dsPIC33A Interrupt Controller registers are initialized by hardware and MPLAB XC-DSC Compiler startup-up code placing the CPU in the following state upon entry to your main() function:

  • IVTBASE = 0x800000 (IVT Start Address)
  • INTCON1.GIE = 1 (All Interrupt Exceptions Enabled)
  • IVTCREG.IVTC = 0 (Interrupt Vector Table Collapse Disabled)
  • SR.IPL = 0 (CPU Priority Level = 0, Level 1 through 7 interrupts enabled)
  • IPCx.IP = 4 (All peripheral interrupt priority levels set to Level 4 - interrupts serviced based on natural priority)

Using Interrupts

The following steps must be taken in the application code for proper interrupt exception initialization and usage. A simple 500 ms LED toggle example using a Timer 1 Interrupt Service Routine (ISR) is illustrated.

 #include  Standard Headers

The application must include header file xc.h as shown.

1
#include <xc.h>            

Amongst other things, this header file defines an ISR declaration macro, simplifying ISR declaration for basic ISRs, for example

  • _ISR _T1Interrupt(void);

 

Back to Top


Provide an Interrupt Service Routine (ISR)

An interrupt handler function is different from an ordinary function in that it handles the context save and restore to ensure that, upon return from interrupt, the program context is maintained. A different code sequence is used to return from these functions as well.

Several function  _attributes_  are provided to allow the application developer to define ISR handlers so that the compiler generates the correct code for an ISR. See the XC-DSC C compiler user guide for details on all attribute options. A basic ISR function definition is shown here:

1
2
3
4
5
6
void __attribute__((interrupt)) isrName(void)
{    
   // Clear the cause of the interrupt (if required)
   // Clear the interrupt flag
   // Execute ISR-specific program logic
}           

To declare a C function as an interrupt handler, tag the function with the interrupt attribute keyword:

__attribute__((interrupt))

To name the ISR, we must use the pre-defined ISR names found in the Interrupt Controller chapter in the device datasheet:

interrupt exception usage isr name table

Example

In this example, we define a Timer 1 ISR function _T1Interrupt() that toggles a user LED every invocation: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <xc.h>

// Config bits
#pragma config FWDT_WDTEN = SW  // sw-controlled WDT

// ISR function definition
void __attribute__((interrupt)) _T1Interrupt(void)
{
    IFS1bits.T1IF = 0;      // acknowledge/clear interrupt flag
   LATCbits.LATC2 ^= 1;    // toggle LED pin
}

int main(void)
{
   // Initialization
   
   // Main loop
   while(1);
}       

Back to Top


Disable All Peripheral Interrupts

On dsPIC33A, the global interrupt enable flag is set by default on reset (INTCON1bits.GIE = 1).

Ensure that you disable this flag first thing in your initialization code, before setting up your peripherals:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <xc.h>

// Config bits
#pragma config FWDT_WDTEN = SW  // sw-controlled WDT

// ISR function definition
void __attribute__((interrupt)) _T1Interrupt(void)
{
    IFS1bits.T1IF = 0;      // acknowledge/clear interrupt flag
   LATCbits.LATC2 ^= 1;    // toggle LED pin
}

int main(void)
{
   // Initialization

   // Disable all peripheral interrupts until initialization completed
   INTCON1bits.GIE = 0;
   
   // Main loop
   while(1);
}       

Back to Top


Configure the Peripheral and Interrupt Controller Settings

Next, you must configure the peripheral to generate interrupt request events.

For example, the dsPIC33A contains a Timer 1 peripheral module. The module has a period register (PR1) that, when properly initialized, will periodically trigger a Timer 1  interrupt request signal in an IFS register as shown:

Timer1

In this example, we will initialize Timer 1 to generate priority level 1 interrupt requests every 500 ms, given a default Timer 1 Standard Peripheral Bus clock of 2 MHz. We also need to initialize the output pin that drives the LED. 

Finally, we also initialize the interrupt priority, and enable and flag register bits needed to support Timer 1 interrupt operation:

  • Timer 1 Interrupt priority is set to 1
  • Timer 1 Interrupt flag is cleared
  • Timer 1 Interrupt is enabled

Finally, we turn on the peripheral.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <xc.h>

// Config bits
#pragma config FWDT_WDTEN = SW  // sw-controlled WDT

// ISR function definition
void __attribute__((interrupt)) _T1Interrupt(void)
{
    IFS1bits.T1IF = 0;      // acknowledge/clear interrupt flag
   LATCbits.LATC2 ^= 1;    // toggle LED pin
}

int main(void)
{
   // Initialization

   // Disable all peripheral interrupts until initialization completed
   INTCON1bits.GIE = 0;

   // Initialize the LED pin as a digital output
   TRISCbits.TRISC2 = 0;

   // Initialize Timer 1 to generate priority level 1 interrupts every 500 mS
   T1CON = 0x0;
    TMR1 = 0x0;
    PR1 = 2000000;          // set interrupt period = 500 mS
   IPC6bits.T1IP = 1;      // set interrupt priority = 1
   IFS1bits.T1IF = 0;      // clear interrupt flag
   IEC1bits.T1IE = 1;      // enable Timer1 interrupts
   T1CONbits.ON = 1;       // turn on Timer1
   
   // Main loop
   while(1);
}       

Back to Top


Re-enable Peripheral Interrupt Exceptions

After all peripherals are initialized and running, we can re-enable all peripheral interrupt requests by setting the Global Interrupt Enable flag as shown here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <xc.h>

// Config bits
#pragma config FWDT_WDTEN = SW  // sw-controlled WDT

// ISR function definition
void __attribute__((interrupt)) _T1Interrupt(void)
{
    IFS1bits.T1IF = 0;      // acknowledge/clear interrupt flag
   LATCbits.LATC2 ^= 1;    // toggle LED pin
}

int main(void)
{
   // Initialization

   // Disable all peripheral interrupts until initialization completed
   INTCON1bits.GIE = 0;

   // Initialize the LED pin as a digital output
   TRISCbits.TRISC2 = 0;

   // Initialize Timer 1 to generate priority level 1 interrupts every 500 mS
   T1CON = 0x0;
    TMR1 = 0x0;
    PR1 = 2000000;          // set interrupt period = 500 mS
   IPC6bits.T1IP = 1;      // set interrupt priority = 1
   IFS1bits.T1IF = 0;      // clear interrupt flag
   IEC1bits.T1IE = 1;      // enable Timer1 interrupts
   T1CONbits.ON = 1;       // turn on Timer1

   // Enable all peripheral interrupts
   INTCON1bits.GIE = 1;
   
   // Main loop
   while(1);
}       

We now have a complete example for using interrupts on the dsPIC33A.

Back to Top