Hello, this short video explains two topics.
First, how can we to enable or disable an interrupt?
Second, what is the interrupt priority?
A Cortex M microcontroller supports up to 256 interrupts.
Each interrupt, except the interrupt reset, has an interrupt number.
The first 16 interrupts are system interrupts, also called system exceptions.
Exceptions are the interrupts that come from the core.
CMSIS defines all system exceptions by using negative values.
CMSIS stands for Cortex Microcontroller Software Interface Standard.
The rest 240 interrupts are peripheral interrupts, also called non-system exceptions.
The peripheral interrupt number starts with 0.
This number scheme allows software to easily distinguish system exceptions and peripheral
interrupts.
Peripheral interrupts are defined by chip manufacturers.
The total number of peripheral interrupts supported varies among chips.
Several CMSIS functions use the interrupt number as an input parameter, such as the
function of NVIC disable interrupt, and NVIC enable interrupt.
NVIC stands for Nested Vector Interrupt Controller.
On the other hand, when an interrupt is serviced, the current interrupt or exception number
is recorded in the program status register (PSR).
However, the definition of interrupt numbers in PSR is different from the definition of
interrupt number in CMSIS functions.
Specifically, interrupt number of PSR equals 16 plus the interrupt number for CMSIS.
In this video, when we say interrupt number, we mean the interrupt number defined for CMSIS.
For a given microprocessor, the definition of the interrupt numbers is usually given
in its device header file.
This is the interrupt number definition for STM32L4 Cortex-M4 microprocessors.
It includes negative values for system exceptions, and non-negative values for peripheral interrupts.
Enabling a system exception is different from enabling a peripheral interrupt.
There is no centralized enabling/disabling registers for system exceptions.
Some system exceptions, such as reset and hard fault, cannot be disabled.
They are always enabled.
The other system exceptions can be enabled or disabled by their corresponding module,
such as system timer.
On the other hand, the enabling and disabling all peripheral interrupts are implemented
by modifying two sets of registers: ISER registers and ICER registers.
Specifically, ISER registers are used to enable peripheral interrupts.
ICER registers are used to disable peripheral interrupts
We can enable a peripheral interrupt by writing 1 to the corresponding bit of the ISER register.
ISER stands for interrupt set enable register.
For example, we will show how to enable the interrupt Timer 7.
The interrupt number of Timer 7 is 44 for STM32L1 processor.
ISER0 enables interrupts 0 to 31.
ISER1 enables interrupts 32 to 63.
To enable interrupt 44, we need to set bit 12 of ISER1 to 1.
Note that, write 0 to a bit in a ISER register does not disable the corresponding interrupt.
Similarly, we can disable a peripheral interrupt by writing 1 to the corresponding bit of the
ICER register.
ICER stands for the interrupt set disable register.
The interrupt number of Timer 7 is 44.
To enable interrupt 44, we need to set bit 12 of ICER1 to 1.
Note that writing 1 to a bit in ICER disables the corresponding interrupt.
Writing 0 to ICER has no impacts.
Separating enable bits and disable bits in two separate sets of registers, ICER and ISER,
provides great convenience for software programmers.
It allows us to enable or disable an interrupt flexibly, without worrying about writing zero
to the other bits in the target register.
In this slide, we will summarize how to enable or disable a peripheral interrupt.
As discussed previously, the interrupt number of all peripherals is larger than or equal
to zero.
There are two approaches to enable or disable a peripheral interrupt.
In the first approach, we can use the NVIC enable and disable functions.
These two functions are provided in the ARM Cortex core header file.
In the second approach, we can directly set the corresponding bit in the ISER or ICER
register.
To enable a given peripheral interrupt IRQn, we can divide it by 32 to find out in which
ISER register the target enable bit is located.
This is because each ISER register has 32 bits.
Thus, each ISER register can enable 32 interrupts.
The bit offset within the target ISER register is determined by the result of IRQn mod 32.
A better solution is to use the shift and bit-wise logic & operation, instead of the
division and modulus operations.
This new approach tends to run faster.
Similarly, we can directly set the corresponding bit in the target ICER register to disable
interrupt IRQn.
What should the processor do if multiple interrupts arrive at the same time?
ARM processors allow software to set priority levels for almost every interrupt.
Accordingly, interrupts that needs to be responded more urgently than others, will be serviced
quickly.
For ARM Cortex processors, numerically low priority values are used to specify logically
high interrupt priorities.
In other words, a lower priority value represents a higher urgency.
This is very counterintuitive.
For example, if the priority value of interrupt A is 5, and the priority value of interrupt
B is 2, then B has a higher urgency than A. Therefore, the processor will service B first
if interrupt A and B arrive at the same time.
The priority of some interrupts are fixed.
Specifically, the priority of three system exceptions, including reset, non-masktable
interrupt (NMI), and hard fault, are fixed.
Their priority levels are negative, and they have the highest urgency.
The priority level of all the other interrupts are adjustable.
ARM Cortex-M processors use a byte to represent the priority level.
Therefore, it allows a maximum of 256 different priority levels, ranging from priority 0 to
priority 255.
However, the total number of available priority levels depends on the manufacturer.
For example, STM32L4 processors implement four priority bits, which are placed as the
most significant bits of the priority byte.
Therefore, the NVIC module of STM32L4 supports up to 16 interrupt priority levels for peripherals.
In addition, the priority bits are divided into two parts, including preempt priority
and sub priority.
By default, the preempt priority has the upper two bits, and the sub-priority has the lower
two bits.
However, the number of bits assigned to each part is configurable.
The preempt priority defines whether an interrupt can preempt an already executing interrupt.
In other words, preempt priority determines if one interrupt can preempt another.
The sub priority determines which interrupt will be handled first, when two interrupts
of the same preempt priority arrive at the same time.
That is to say, the sub priority is used, only when two interrupts with the same preempt
priority value are pending.
Specifically, the interrupt with the lower sub priority value will be handled first.
Again, the priority is stored as the most significant four bits of the interrupt priority
byte.
For example, the statement NVICSetPriority (7, 6) will set the priority byte of interrupt
7 to 01100000 in binary, or 96 in decimal.
In the NVIC data structure, the interrupt priority registers are mapped to a byte array.
Each register holds four priority bytes.
The NVIC Set Priority statement is equivalent to the following C statement.
First, we shift left, the priority value four bits, and then store the result in the corresponding
interrupt priority byte.
Within an interrupt priority byte, the number of bits in the preemption priority field and
the sub-priority field can be configured by software.
For STM32L4 processors, there are two bits in each field.
However, we can use the function NVIC_SetPriorityGrouping() to change the size of each
field.
This function will change the AIRCR register, after performing a sequence of unlocking process.
AIRCR stands for Application Interrupt and Reset Control Register.
For example, if the input n is 4, we set all 4 bits to the preemption priority.
As result, the preemption priority has a total of 16 priority levels.
In embedded systems, we often have to perform some critical operations, in which data should
not be corrupted by other interrupts.
Therefore, we need to disable all interrupts with less urgency to ensure that the execution
of the critical code will not be interrupts by other interrupts.
We can use the Base Priority Mask Register (BASEPRI) to achieve the protection of critical code.
In this example, we disable all interrupts with a priority value of 5 or higher.
After executing the critical code, we can remove the interrupt masking by resetting
the BASEPRI register to zero.
Please visit the book website for more examples and tutorials.