STM32 gotchas
91.Some interrupts cannot be disabled, and attempts to do so may lead to HardFault.

Interrupts in STM32 are controlled by NVIC, a module which is part of the Cortex-Mx processor. So, while individual interrupts are listed in the Interrupt chapter of RM, the internal working of the NVIC module is documented in the Programming Manual to the given Cortex-M core, and in ARM's materials.

Besides interrupts for individual peripheralsin STM32, there are also "system" interrupts (or, in ARM's lingo, exceptions), related to the processor's internals. These include the Non-Maskable Interrupt (NMI), Faults (including HardFault), Debug interrupt, and also the SysTick interrupt, as SysTick is also part of the processor core.

There are 16 positions reserved for "system" interrupts, after which the "normal" interrupts follow. Not all of the 16 are used, though, the actually implemented "system" interrupts vary between various Cortex-M cores, but ARM tends to maintain the numbers for the analogous interrupts between the variants.

Confusingly, ARM uses two kinds of numbering for interrupts. One is "natural", starting with 0, so the "system" interrupts are all between 0 and 15. This is reflected e.g. in the Vector Table, and also in indicating current interrupt in processor's core Program Status register PSR (IPSR), and also processor's System Control Block register SCB_ICSR in VECTPENDING and VECTACTIVE fields.

The second type of interrupt numbering is shifted so, that "system" interrupts are negative (from -16 to -1). This reflects the fact that the system interrupts are controlled directly from processor's SCB registers (whereas "normal" interrupts are controlled in a uniform way using NVIC registers). Most of the "system" interrupts cannot be disabled, and some have fixed priority. In particular, only Faults (except HardFault) can be disabled in SCB_SHCSR1,2. Priority of some of the "system" interrupts can be set in SCB_SHPRx; NMI has fixed priority of -2 and HardFault has fixed priority of -1 (smaller number means higher priority)3.

For user convenience, ARM-provided CMSIS (in the core_cmX.h headers) offers several functions to access NVIC registers, including among others also the often used NVIC_Enable() and NVIC_Disable(). As a parameter, these functions generally take the given interrupt's number, in its second type (CMSIS requires the manufacturer to provide in the device header a list of symbols related to individual peripherals - e.g. WWDG_IRQn - mapped to the interrupt numbers, to be used with these functions). As the "system" interrupts are not controlled by NVIC, these functions generally don't take negative interrupt numbers. This is usually documented in doxygen-formatted comments to the given functions, but often overlooked by users.

So, system interrupts such as SysTick, cannot be disabled by calling NVIC_Disable() (and most of them cannot be disabled at all, anyway). As the negative parameter is unexpected by these functions, such calls may even result in surprising HardFault.

This issue has been brought up in this thread on the STM32 forum.


1. Cortex-M0/M0+ does not have individual faults so this does not apply to them.

2. In addition to individual interrupts enable/disable, all interrupts except NMI can be disabled at once through single bit of the PRIMASK system register. CMSIS provides the __disable_irq()/__enable_irq() function pair to facilitate this.

3. ARM has this strange fixation on negative values in numbering things. These values are "virtual", though, they are not used in any register nor in any function call, they just express the fact that some interrupts have higher priority than any other interrupt. In some respects, even Reset is considered to be an interrupt (exception) with priority -3 (i.e. highest of all).