Interrupts
The PIC32MX generates interrupt requests in response to interrupt events from peripheral modules. The Interrupt module exists external to the CPU logic and prioritizes the interrupt events before presenting them to the CPU.
The PIC32MX Interrupts module includes the following features :
- Up to 96 interrupt sources.
- Up to 64 interrupt vectors.
- Single and Multi-Vector mode operations.
- Five external interrupts with edge polarity control.
- Interrupt proximity timer.
- Module freeze in Debug mode.
- Seven user-selectable priority levels for each vector.
- Four user-selectable subpriority levels within each priority.
- User-configurable shadow set based on priority level (this feature is not available on all devices; refer to the specific device data sheet for availability).
- Software can generate any interrupt.
- User-configurable interrupt vector table location.
ISRs are organized in IVT. ISR is defined as a standard function but with the iv
directive afterwards which connects the function with specific interrupt vector. For more information on IVT refer to the PIC32 Family Reference Manual.
Configuring Interrupts
The PIC32MX interrupt controller can be configured to operate in one of two modes :
- Single Vector mode - all interrupt requests will be serviced at one vector address (mode out of reset).
- Multi-Vector mode - interrupt requests will be serviced at the calculated vector address.
Single Vector Mode
In this mode, the CPU always vectors to the same address . This means that only one ISR can be defined.
The Single Vector mode address is calculated by using the Exception Base (EBase) address (its address default is 0x9FC01000E). The exact formula for Single Vector mode is as follows : Single Vector Address = EBase + 0x200.Multi Vector Mode
In this mode, the CPU vectors to the unique address for each vector number. Each vector is located at a specific offset, with respect to a base address specified by the EBase register in the CPU.
The individual vector address offset is determined by the following equation : EBase + (Vector_Number x Vector_Space) + 0x200.
By default, the compiler configures interrupts in the Multi Vector mode, with the EBase address set to 0x9FC01000 and vector spacing of 32.
Configuring the Interrupt operating mode is performed in the Edit Project window.
Interrupt Priorities
In the Multi Vector Mode, the user is able to assign a group priority and group subpriority level to each of the interrupt vectors. The user-selectable priority levels range from 1 (the lowest priority) to 7 (the highest).
If an interrupt priority is set to zero, the interrupt vector is disabled for both interrupt and wake-up purposes. Interrupt vectors with a higher priority level preempt lower priority interrupts.
The subpriority will cause that when two interrupts with the same priority are pending, the interrupt with the highest subpriority will be handled first. The user-selectable subpriority levels range from 0 (the lowest subpriority) to 3 (the highest).
Interrupts and Register Sets
The PIC32MX family of devices employs two register sets, a primary register set for normal program execution and a shadow register set for highest priority interrupt processing.
Register Set Selection in Single Vector Mode
In Single Vector mode, you can select which register set will be used. By default, the interrupt controller will instruct the CPU to use the first register set. This can be changed later in the code.
Register Set Selection in Multi-Vector Mode
When a priority level interrupt matches a shadow set priority, the interrupt controller instructs the CPU to use the shadow set. For all other interrupt priorities, the interrupt controller instructs the CPU to use the primary register set.
Interrupt Coding Requirements
In order to correctly utilize interrupts and correctly write the ISR code, the user will need to take care of these things :
- Write the Interrupt Service Routine. You may use Interrupt Assistant to easily write this routine.
- Initialize the module which will generate an interrupt.
- Set the correct priority and subpriority for the used module according to the priorities set in the Interrupt Service Routine.
- Enable Interrupts.
Interrupt Service Routine
Interrupt service routine is defined in this way :
void interrupt() iv IVT_ADC ilevel 7 ics ICS_SOFT { // Interrupt service routine code }
where :
iv
- reserved word that inform the compiler that it is an interrupt service routine.IVT_ADC
- appropriate Interrupt Vector.ilevel
7 - Interrupt priority level 7.ics
Interrupt Context Saving; Interrupt Context Saving can be performed in several ways :ICS_SOFT
- Context saving is carried out by the software.ICS_SRS
- Shadow Register set is use for context saving.ICS_OFF
- No context savingICS_AUTO
- Compiler chooses whether theICS_SOFT
orICS_SRS
will be used.
User can explicitly declare starting interrupt routine address using org
directive :
void interrupt() org 0x9D000000 iv IVT_ADC ilevel 7 ics ICS_SOFT { // Interrupt service routine code }
Function Calls from Interrupt
Calling functions from within the interrupt routine is possible. The compiler takes care about the registers being used, both in "interrupt" and in "main" thread, and performs "smart" context-switching between two of them, saving only the registers that have been used in both threads. It is not recommended to use a function call from interrupt. In case of doing that take care of stack depth.
Disable Context Saving
Use the #pragma disablecontextsaving
to instruct the compiler not to automatically perform context-switching.
This means that no register will be saved/restored by the compiler on entrance/exit from interrupt service routine, except STATUS, WREG and BSR registers in high priority interrupt ('Fast Register Stack').
This exception can be overrided by placing an asm RETFIE, 0
instruction at the end of the high priority interrupt routine (with redirecting all routine exits to this instruction).
Thus, #pragma disablecontextsaving
pragma enables the user to manually write code for saving registers upon entrance and to restore them before exit from interrupt.
Interrupt Example
Here is a simple example of handling the interrupts from Timer1
(if no other interrupts are allowed):
void Timer1_interrupt() iv IVT_TIMER_1 ilevel 7 ics ICS_SRS {
T1IF_bit = 0; // Clear T1IF
LATB = ~ PORTB; // Invert PORTB
}
void main() {
AD1PCFG = 0xFFFF; // Initialize AN pins as digital
TRISB = 0; // initialize PORTB as output
LATB = 0xAAAA; // Initialize PORTB value
TMR1 = 0; // reset timer value to zero
PR1 = 65535; // Load period register
T1IP0_bit = 1; // set interrupt
T1IP1_bit = 1; // priority
T1IP2_bit = 1; // to 7
TCKPS0_bit = 1; // Set Timer Input Clock
TCKPS1_bit = 1; // Prescale value to 1:256
EnableInterrupts(); // Enable all interrupts
T1IE_bit = 1; // Enable Timer1 Interrupt
ON__T1CON_bit = 1; // Enable Timer1
}
What do you think about this topic ? Send us feedback!