Cortex-M3 has NVIC (Nested Vector Interrupt Controller), a kind of peripheral, to support up to 255 interrupts.
Other than that, it also has WIC (Wakeup Interrupt Controller), which similar to NVIC, but it doesn’t need any clock to run. Hence it can be used to detect any interrupt during sleep mode without burning energy (no need to have any clock running on system at all).
Since WIC is only available on rev2 Cortex-M3 (currently only LPC17xx that have it), this post will only discuss the NVIC control.
Configurable Priority vs Unconfigurable Priority Interrupt
On Cortex-M3, the exceptions / interrupts are divided into two categories, based on their priority: configurable and unconfigurable. The unconfigurable priority interrupts have fixed priority, and consist of 3 interrupts:
- Reset. Invoked on power on and reset
- Non Maskable Interrupt. An external interrupt (activated by external pin) that is unstoppable except by reset.
- Hard fault. Any fault that is not handled by other fault handlers (memory management fault, bus fault, usage fault) will activate this interrupt. The interrupt is also activated when the other fault handlers are turned off
Those interrupts / exceptions are the basic interrupts that are expected to be handled by the application (read: MUST be implemented by application). The priority is started at -3 for Reset, -2 for NMI, and -1 for Hard fault.
The rest of 252 interrupts are priority configurable, starting at 0 (the highest priority) until 252 (the lowest priority).
Global Interrupt Enable / Disable
During the initial reset, NVIC is turned off. Therefore, the processor cannot receive any interrupts (except for NMI, Reset interrupt, and hard fault). To turn on the interrupts with configurable priority:
asm volatile ("cpsie i");
“CPSIE I” is a assembly instruction to enable the priority configurable interrupts. Actually, it’s a shortcut to this longer procedure
asm volatile ("MOVS r0, #0\n\ MSR PRIMASK, r0");
To turn off the priority configurable interrupts:
asm volatile ("cpsid i");
Or, taking the longer non-atomic procedure:
asm volatile ("MOVS r0, #1\n\ MSR PRIMASK, r0");
Actually, if it is needed, the hard fault interrupt can be turned off as well (along with the priority configurable interrupts) with this command:
asm volatile ("cpsid f");
Or, using longer non-atomic procedure
asm volatile ("MOVS r0, #1\n\ MSR FAULTMASK, r0");
To turn on the hard fault interrupt with the other priority configurable interrupts, use:
asm volatile ("cpsie f");
Or, using longer non-atomic procedure
asm volatile ("MOVS r0, #0\n\ MSR FAULTMASK, r0");
Enabling / Disabling Group of Interrupts
Since you have up to 255 interrupts, it’s odd to have just single global interrupt on/off. Hence, ARM created a register to enable/disable group of interrupts, based on their priority, the BASEPRI. If you set the BASEPRI, the interrupts with the same priority level or lower (ie. the priority number is bigger than BASEPRI) will be masked.
Here’s how to set it:
asm volatile ("MOVS r0, #5\n\ MSR BASEPRI, r0");
The above code will mask the interrupts with priority 5 or below. To reset/disable the BASEPRI, just replace #5 with #0.
Enabling / Disabling Specific Interrupts
This one is very easy. If you look at the Cortex-M3 technical manual, the only thing that you need to do is to set IRQ X ‘Set Enable Register’ (ISER) or ‘Clear Enable Register’ (ICER). For example, to turn on TIMER0 interrupts (Match0, Match1, Capture0, Capture1) on LPC1766, you can use:
NVIC->ISER = (1 << 1); // Or if you have downloaded CMSIS library, you can use: NVIC_EnableIRQ(TIMER0_IRQn);
The NVIC_EnableIRQ() is in core_cm3.h file at CMSIS library source code. Don’t forget to include “LPC17xx.h” too, before using these functions from core_cm3.h