dsPIC33A Interrupt and Exception Usage
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.
Amongst other things, this header file defines an ISR declaration macro, simplifying ISR declaration for basic ISRs, for example
- _ISR _T1Interrupt(void);
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:
2
3
4
5
6
{
// 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:
To name the ISR, we must use the pre-defined ISR names found in the Interrupt Controller chapter in the device datasheet:
Example
In this example, we define a Timer 1 ISR function _T1Interrupt() that toggles a user LED every invocation:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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);
}
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:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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);
}
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:
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.
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
// 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);
}
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.
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
// 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.