Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Interrupt Priority & Preemption
NVIC IPR registers, priority grouping via AIRCR, the preemption chain where TIM2 ISR pends I2C1, and how lower priority-value = higher urgency.
1. Overview
The 010_interrupt_priority project demonstrates interrupt priority and preemption:
- Configure TIM2 (IRQ28) with priority 0x80 and I2C1 (IRQ31) with priority 0x70
- Pend TIM2 (lower priority = less urgent), enable both IRQs
- TIM2 ISR fires, then manually pends I2C1
- I2C1 (higher priority = 0x70 < 0x80) preempts TIM2 ISR
- I2C1 ISR runs, returns, TIM2 ISR resumes (stuck in while(1) in this example)
2. Source Code — Annotated
#define IRQNO_TIMER2 28 /* TIM2 global interrupt = IRQ28 */
#define IRQNO_I2C1 31 /* I2C1 event interrupt = IRQ31 */
#include <stdint.h>
#include <stdio.h>
/* NVIC base addresses */
uint32_t *pNVIC_IPRBase = (uint32_t*)0xE000E400; /* Priority registers */
uint32_t *pNVIC_ISERBase = (uint32_t*)0xE000E100; /* Enable registers */
uint32_t *pNVIC_ISPRBase = (uint32_t*)0xE000E200; /* Pending registers */
/* Set priority for a given IRQ number.
* priority_value: 8-bit value, but STM32F407 only uses top 4 bits (bits[7:4]).
* So effective priority levels are 0x00, 0x10, 0x20...0xF0 (16 levels).
*/
void configure_priority_for_irqs(uint8_t irq_no, uint8_t priority_value)
{
/* 1. Calculate which IPRx register holds this IRQ's priority.
* Each IPR register holds 4 IRQ priorities (8-bit each).
* iprx = which IPR register (0–59) */
uint8_t iprx = irq_no / 4; /* IPR index */
uint32_t *ipr = pNVIC_IPRBase + iprx; /* pointer to the IPR register */
/* 2. Calculate bit position within that register.
* Each IRQ gets 8 bits: irq%4 * 8 gives the bit offset */
uint8_t pos = (irq_no % 4) * 8;
/* 3. Write the priority value to the 8 bits for this IRQ */
*ipr &= ~(0xFF << pos); /* clear existing priority */
*ipr |= (priority_value << pos); /* set new priority */
}
int main(void)
{
/* Set TIM2 priority = 0x80 (lower urgency since higher value) */
configure_priority_for_irqs(IRQNO_TIMER2, 0x80);
/* Set I2C1 priority = 0x70 (higher urgency since lower value) */
configure_priority_for_irqs(IRQNO_I2C1, 0x70);
/* Pend TIM2 — it will fire when enabled */
*pNVIC_ISPRBase |= (1 << IRQNO_TIMER2);
/* Enable I2C1 first (not pending yet — nothing happens) */
*pNVIC_ISERBase |= (1 << IRQNO_I2C1);
/* Enable TIM2 — already pending → fires immediately */
*pNVIC_ISERBase |= (1 << IRQNO_TIMER2);
}
void TIM2_IRQHandler(void)
{
printf("[TIM2_IRQHandler]\n");
/* Manually pend I2C1 interrupt from within TIM2 ISR.
* Since I2C1 priority (0x70) < TIM2 priority (0x80)
* [lower value = higher urgency], I2C1 preempts TIM2 immediately. */
*pNVIC_ISPRBase |= (1 << IRQNO_I2C1);
while(1); /* TIM2 ISR blocks here after I2C1 returns */
}
void I2C1_EV_IRQHandler(void)
{
/* This preempts TIM2_IRQHandler because 0x70 < 0x80 */
printf("[I2C1_EV_IRQHandler]\n");
}
3. IPR Registers — Deep Dive
The NVIC has 60 IPR (Interrupt Priority Register) registers (IPR0–IPR59), each 32 bits wide. Each register holds priority values for 4 consecutive IRQs, one 8-bit field per IRQ.
/* IPR Register Layout (one 32-bit register per 4 IRQs): */
/*
* IPRx [31:24] = Priority of IRQ (x*4)+3
* IPRx [23:16] = Priority of IRQ (x*4)+2
* IPRx [15: 8] = Priority of IRQ (x*4)+1
* IPRx [ 7: 0] = Priority of IRQ (x*4)+0
*
* Example: IRQ28 (TIM2) and IRQ31 (I2C1):
* IRQ28 → IPR7 [7:0] (28/4=7, 28%4=0 → bits [7:0])
* IRQ29 → IPR7 [15:8]
* IRQ30 → IPR7 [23:16]
* IRQ31 → IPR7 [31:24] (31/4=7, 31%4=3 → bits [31:24])
*
* STM32F407 only implements the TOP 4 bits of each 8-bit field.
* Bits [3:0] of each field always read as 0.
* This gives 16 configurable priority levels: 0x00, 0x10, 0x20...0xF0
*/
/* Example: Read priority of TIM2 (IRQ28) */
uint32_t *ipr7 = (uint32_t*)0xE000E400 + 7; /* IPR7 */
uint8_t tim2_priority = (*ipr7 >> 0) & 0xFF; /* bits [7:0] */
/* On STM32F407, only bits [7:4] are meaningful */
STM32F407 Priority Levels
Priority values: 0x00=highest, 0x10, 0x20, 0x30…0xF0=lowest
| Priority Value (full byte) | Effective Priority (top nibble) | Urgency |
|---|---|---|
| 0x00 | 0 | Highest — preempts everything |
| 0x10 | 1 | Very high |
| 0x70 | 7 | Medium (I2C1 in this example) |
| 0x80 | 8 | Medium-low (TIM2 in this example) |
| 0xF0 | 15 | Lowest |
4. Priority Grouping — AIRCR
ARM Cortex-M4 supports dividing the 4 priority bits into preemption priority and sub-priority using the PRIGROUP field in AIRCR (Application Interrupt and Reset Control Register).
| PRIGROUP | Preempt Priority Bits | Sub-Priority Bits | Preempt Levels | Sub Levels |
|---|---|---|---|---|
| 0 (default) | 4 bits [7:4] | 0 bits | 16 | 1 |
| 4 | 3 bits [7:5] | 1 bit [4] | 8 | 2 |
| 5 | 2 bits [7:6] | 2 bits [5:4] | 4 | 4 |
| 6 | 1 bit [7] | 3 bits [6:4] | 2 | 8 |
| 7 | 0 bits | 4 bits [7:4] | 1 | 16 |
/* Setting PRIGROUP in AIRCR */
/* AIRCR = Application Interrupt and Reset Control Register */
#define AIRCR (*(volatile uint32_t*)0xE000ED0C)
/* AIRCR write key: must write 0x05FA to bits [31:16] */
/* PRIGROUP is in bits [10:8] */
void NVIC_SetPriorityGrouping(uint8_t prigroup)
{
uint32_t reg = AIRCR;
reg &= ~(0xFFFF0000 | (0x7 << 8)); /* clear write key and PRIGROUP */
reg |= (0x05FA0000 | ((prigroup & 7) << 8));
AIRCR = reg;
}
/* Example: Use 3 preemption bits, 1 sub-priority bit */
NVIC_SetPriorityGrouping(4);
5. Preemption — What Happens Step by Step
/* Timeline of the example:
*
* t=0: main() runs in Thread Mode
* TIM2 priority = 0x80, I2C1 priority = 0x70
*
* t=1: TIM2 pending bit set (ISPR bit28)
*
* t=2: I2C1 enabled (ISER bit31) — not pending, nothing happens
*
* t=3: TIM2 enabled (ISER bit28) → immediately taken (was pending)
* Hardware saves {R0-R3,R12,LR,PC,xPSR} to MSP (Thread mode was on MSP)
* IPSR = 28+16 = 44 (TIM2 exception number)
* TIM2_IRQHandler begins executing
*
* t=4: TIM2_IRQHandler pends I2C1 (ISPR bit31)
* NVIC checks: I2C1 priority (0x70) < TIM2 priority (0x80)
* → I2C1 PREEMPTS TIM2 immediately!
* Hardware saves a NEW stack frame to MSP on top of TIM2's frame
* IPSR = 31+16 = 47 (I2C1 exception number)
*
* t=5: I2C1_EV_IRQHandler runs
* Prints "[I2C1_EV_IRQHandler]"
*
* t=6: I2C1_EV_IRQHandler returns (BX LR with EXC_RETURN 0xFFFFFFF1)
* Hardware pops I2C1 stack frame from MSP
* IPSR = 44 (back to TIM2)
* TIM2_IRQHandler resumes at the while(1) line
*
* t=7: TIM2_IRQHandler loops forever in while(1) */
6. Priority Configuration Calculator
IPR Register Calculator
7. Interview Questions & Answers
Q1. How are interrupt priorities configured in ARM Cortex-M4 NVIC?
STM32F407 implements only 4 priority bits (top nibble: bits [7:4]). Effective values are 0x00 (highest), 0x10, 0x20…0xF0 (lowest) — 16 levels.
Formula:
IPR_register_index = irq / 4, bit_position = (irq % 4) × 8.Write:
IPR[irq/4] = (priority_value << ((irq%4)*8)).Q2. Why does lower priority VALUE mean higher urgency?
Preemption occurs when a pending interrupt has a numerically smaller priority value than the currently executing interrupt. In the example: I2C1 at 0x70 preempts TIM2 at 0x80 because 0x70 < 0x80.
This convention is consistent across ARM Cortex-M3/M4/M7, FreeRTOS configuration, and most ARM-based RTOS systems. In FreeRTOS specifically,
configMAX_SYSCALL_INTERRUPT_PRIORITY sets the boundary — higher priority (lower value) ISRs cannot use FreeRTOS API.Q3. What is priority grouping and how does it affect interrupt behaviour?
• Preemption priority: Determines if this interrupt can preempt a running ISR. An interrupt with higher preemption priority (lower value) WILL preempt a lower preemption priority ISR.
• Sub-priority: When two interrupts with the same preemption priority are pending simultaneously, the one with lower sub-priority value is taken first. Sub-priority does NOT cause preemption.
Default (PRIGROUP=0): All 4 bits are preemption priority, no sub-priority. Most embedded systems leave PRIGROUP=0 for simplicity.
Q4. What are the system exception priorities (HardFault, SysTick etc.)?
Fixed priority (not configurable):
• Reset: -3 (highest possible)
• NMI: -2
• HardFault: -1 (always takes exceptions escalated from lower priority faults)
Configurable (use SCB registers, not NVIC IPR):
• MemManage: SCB_SHPR1[7:0]
• BusFault: SCB_SHPR1[15:8]
• UsageFault: SCB_SHPR1[23:16]
• SVCall: SCB_SHPR2[31:24]
• PendSV: SCB_SHPR3[23:16] — typically set to lowest priority (0xF0) in RTOS
• SysTick: SCB_SHPR3[31:24] — typically low priority in RTOS
Base addresses: SCB_SHPR1=0xE000ED18, SCB_SHPR2=0xE000ED1C, SCB_SHPR3=0xE000ED20
Q5. What is priority inversion and how does it relate to interrupt priorities?
Example: High-priority ISR A needs a shared resource (e.g., a buffer). Low-priority ISR B holds that resource. A medium-priority ISR C preempts B (since C > B). Now A waits for B, but B can’t run because C is running. A (highest) is stuck behind C (medium), inverting their intended priorities.
Solutions include:
• Priority inheritance: temporarily boost B’s priority to A’s level while B holds the resource
• Priority ceiling: assign a “ceiling priority” to the resource equal to the highest-priority user
• Avoiding shared resources between ISRs of different priorities
In embedded systems, this is most relevant in RTOS task scheduling, but the concept applies to any preemptive interrupt system.
Q6. In this example, TIM2 priority=0x80 and I2C1 priority=0x70. On STM32F407 which only uses top 4 bits, what are the effective priorities?
Priority 0x80 = 0b10000000 → top 4 bits = 0b1000 = decimal 8 → effective priority = 8
Priority 0x70 = 0b01110000 → top 4 bits = 0b0111 = decimal 7 → effective priority = 7
Since 7 < 8 (lower number = higher urgency), I2C1 (priority 7) CAN preempt TIM2 (priority 8). The difference is just 1 priority level on the effective 16-level scale.
If both had priority 0x80 (effective 8), no preemption would occur — same preemption priority level. If they had the same preemption priority and both pended simultaneously, sub-priority would decide order (but in default PRIGROUP=0 there are no sub-priority bits, so IRQ number order decides).
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments