📚 CMPE2250: Lesson — External Interrupts (EXTI) on STM32G0
🎯 Learning Objectives
By the end of this lesson, students should be able to:
-
Explain what an external interrupt is in the context of STM32 microcontrollers.
-
Describe the
EXTIperipheral and how it connects GPIO pins to theNVIC. -
Route GPIO pins to EXTI lines using SYSCFG_EXTICR.
-
Configure
EXTI lines forrising,falling, orboth-edgetriggering usingEXTI_RTSRandEXTI_FTSRregsters. -
Write a real interrupt handler in the vector table.
-
Validate interrupt behavior using LEDs and timestamps.
-
Understand debouncing and interrupt latency.
1️⃣ EXTI Architecture (Bare‑Metal View)
- EXTI is a peripheral that detects edges on input lines and generates interrupt requests.
flowchart LR
A[GPIO Pin] --> B[EXTI Line N
Port Selection in EXTI]
B --> C[EXTI Line N]
C --> D[Edge Detection
RTSR / FTSR]
D --> E[Interrupt Unmasking
IMR]
E --> F[Pending Flag Set
PR1]
F --> G[NVIC Interrupt Request]
G --> H[CPU ISR
EXTIx_y_IRQHandler]
Key ideas involved:
-
EXTIlines are numbered0–15, independent of GPIO ports. -
Each EXTI line can be driven by one GPIO pin, selected via EXTI’s own configuration registers (not SYSCFG).
-
Edge detection is controlled by
EXTI_RTSR1andEXTI_FTSR1. -
Interrupt unmasking is controlled by
EXTI_IMR1. -
Pending flags are reported in
EXTI_PR1and cleared by writing 1. -
NVIC interrupt vectors are grouped:
EXTI0_1,EXTI2_3,EXTI4_15.
You can then explicitly note:
-
On
STM32G0B1,EXTIis decoupled fromSYSCFG. -
Port selection for EXTI lines is handled inside the EXTI peripheral, unlike older STM32 families where SYSCFG_EXTICR performed this mapping.
2️⃣ Registers You Must Know About
These are the ones students must internalize:
| Register | Purpose |
|---|---|
EXTI_EXTICR[n] |
Selects which GPIO port connects to EXTI line |
EXTI_RTSR1 |
Rising‑edge trigger enable |
EXTI_FTSR1 |
Falling‑edge trigger enable |
EXTI_IMR1 |
Interrupt mask (enable/disable EXTI line) |
EXTI_PR1 |
Pending flag register (write 1 to clear) |
NVIC_ISER |
Enables interrupt in the NVIC |
EXTI_EXTICRx Register Grouping (STM32G0B1)
| EXTI Line | EXTICR Register | Bits (Field) | Notes - 8‑bit field for EXTI port selection |
|---|---|---|---|
| 0 | EXTICR1 | EXTICR1[7:0] | Select PA0 / PB0 / PC0 / PD0 / PE0 / PF0 |
| 1 | EXTICR1 | EXTICR1[15:8] | Select PA1 / PB1 / PC1 / PD1 / PE1 / PF1 |
| 2 | EXTICR1 | EXTICR1[23:16] | Select PA2 / PB2 / PC2 / PD2 / PE2 / PF2 |
| 3 | EXTICR1 | EXTICR1[31:24] | Select PA3 / PB3 / PC3 / PD3 / PE3 / PF3 |
| 4 | EXTICR2 | EXTICR2[7:0] | Select PA4 / PB4 / PC4 / PD4 / PE4 / PF4 |
| 5 | EXTICR2 | EXTICR2[15:8] | Select PA5 / PB5 / PC5 / PD5 / PE5 / PF5 |
| 6 | EXTICR2 | EXTICR2[23:16] | Select PA6 / PB6 / PC6 / PD6 / PE6 / PF6 |
| 7 | EXTICR2 | EXTICR2[31:24] | Select PA7 / PB7 / PC7 / PD7 / PE7 / PF7 |
| 8 | EXTICR3 | EXTICR3[7:0] | Select PA8 / PB8 / PC8 / PD8 / PE8 / PF8 |
| 9 | EXTICR3 | EXTICR3[15:8] | Select PA9 / PB9 / PC9 / PD9 / PE9 / PF9 |
| 10 | EXTICR3 | EXTICR3[23:16] | Select PA10 / PB10 / PC10 / PD10 / PE10 / PF10 |
| 11 | EXTICR3 | EXTICR3[31:24] | Select PA11 / PB11 / PC11 / PD11 / PE11 / PF11 |
| 12 | EXTICR4 | EXTICR4[7:0] | Select PA12 / PB12 / PC12 / PD12 / PE12 / PF12 |
| 13 | EXTICR4 | EXTICR4[15:8] | Select PA13 / PB13 / PC13 / PD13 / PE13 / PF13 |
| 14 | EXTICR4 | EXTICR4[23:16] | Select PA14 / PB14 / PC14 / PD14 / PE14 |
| 15 | EXTICR4 | EXTICR4[31:24] | Select PA15 / PB15 / PC15 / PD15 / PE15 |
Grouping Summary
| EXTICR Register | Covers EXTI Lines |
|---|---|
| EXTICR1 | 0–3 |
| EXTICR2 | 4–7 |
| EXTICR3 | 8–11 |
| EXTICR4 | 12–15 |
| BITs | PORT |
|---|---|
| 0b000 / 0x00 | PAx |
| 0b001 / 0x01 | PBx |
| 0b010 / 0x02 | PCx |
| 0b011 / 0x03 | PDx |
| 0b100 / 0x04 | PEx |
| 0b101 / 0x05 | PFx |
-
Each
EXTICRregister controls fourEXTIlines -
Each line gets its own
8‑bit field(not 4 bits like older families) -
Even though each line it’s
8-bits, only the lower3 bitsare used to select the port (PA–PF), and the upper bits are reserved and should be kept at 0. -
The register layout is 32 bits = 4 × 8‑bit field
3️⃣ Configuring EXTI
Let’s use PB1 → EXTI1 as the example.
Step 1 — Enable GPIOB and SYSCFG clocks
RCC->IOPENR |= RCC_IOPENR_GPIOBEN;
Step 2 — Configure PB1 as input
- If you have your GPIO library working as expected, you can use it here. Otherwise, you can directly manipulate the registers:
GPIOB->MODER &= ~(3U << (1 * 2)); // Input mode
GPIOB->PUPDR &= ~(3U << (1 * 2)); // Clear pull bits
GPIOB->PUPDR |= (1U << (1 * 2)); // internal Pull-up if necessary
Step 3 — Route PB1 to EXTI1
EXTI1is controlled byEXTICR1(lines 0-3).
EXTI->EXTICR[0] &= ~EXTI_EXTICR1_EXTI1; // Clear, Select PB1
EXTI->EXTICR[0] |= EXTI_EXTICR1_EXTI1_0; // write 0b001 to select PB1 (EXTI1)
EXTI->EXTICR[0] |= 0x01 << 8; // Equivalent to the line above, since EXTI1 is in bits 15:8 of EXTICR1
Step 4 — Configure edge detection
- Example: falling edge (button press)
EXTI->RTSR1 &= ~EXTI_RTSR1_RT1; // Disable rising
EXTI->FTSR1 |= EXTI_FTSR1_FT1; // Enable falling
Step 5 — Unmask (enable) interrupt
EXTI->IMR1 |= EXTI_IMR1_IM1;
Step 6 — Enable EXTI0_1 interrupt in NVIC
- This is the vector that handles EXTI lines 0 and 1.
NVIC_EnableIRQ(EXTI0_1_IRQn);
Step 7 — Implement the ISR
void EXTI0_1_IRQHandler(void)
{
if (EXTI->FPR1 & EXTI_FPR1_FPIF1)
{//An EXTI1 event occurred (falling edge on PB1)
EXTI->FPR1 = EXTI_FPR1_FPIF1; // Clear pending flag
// Your action: toggle LED, record timestamp, etc.
}
else if (EXTI->RPR1 & EXTI_RPR1_RPIF1)
{//An EXTI1 event occurred (rising edge on PB1)
EXTI->RPR1 = EXTI_RPR1_RPIF1; // Clear pending flag
// Your action: toggle LED, record timestamp, etc.
}
}
4️⃣ EXTI Interrupt Vector Mapping (STM32G0)
- On the STM32G0, EXTI lines do not each get their own interrupt vector. Instead, they are grouped into shared NVIC interrupt handlers.
This matters because:
-
Multiple
EXTIlines may trigger the sameISR -
Your
ISRmust check which line fired by readingEXTI_RPR1orEXTI_FPR1, or both if you enabled both edges. -
Priorities apply to the group, not the individual line
4.1 Interrupt Vector Table Entries for EXTI
| NVIC Interrupt Name | EXTI Lines Covered | Typical Use Cases |
|---|---|---|
EXTI0_1_IRQn |
EXTI0, EXTI1 | Buttons, sensors on PA0/PA1, PB0/PB1, etc. |
EXTI2_3_IRQn |
EXTI2, EXTI3 | Rotary encoders, dual-button inputs |
EXTI4_15_IRQn |
EXTI4–EXTI15 | Most GPIO interrupts, user buttons, sensors |
4.2 Corresponding ISR Function Names
These are the function names you must implement in your vector table or startup file:
| EXTI Group | ISR Function Name |
|---|---|
| EXTI0–1 | void EXTI0_1_IRQHandler(void) |
| EXTI2–3 | void EXTI2_3_IRQHandler(void) |
| EXTI4–15 | void EXTI4_15_IRQHandler(void) |
4.3 How to Handle Multiple Lines in One ISR
- Because each ISR may serve multiple EXTI lines, your handler must check the pending register:
void EXTI4_15_IRQHandler(void)
{
if (EXTI->RPR1 & EXTI_RPR1_RPIF4)
{
EXTI->RPR1 = EXTI_RPR1_RPIF4; // Clear pending
// Handle EXTI4 event
}
if (EXTI->FPR1 & EXTI_FPR1_FPIF7)
{
EXTI->FPR1 = EXTI_FPR1_FPIF7; // Clear pending
// Handle EXTI7 event
}
}
4.4 Why EXTI Lines Are Grouped?
Grouping reduces:
-
Vector table size
-
NVIC complexity
-
Silicon area
But it introduces an important design constraint: Your ISR must always check which EXTI line triggered the interrupt.
5️⃣ Validation Techniques
-
We should verify:
✔ LED toggles only once per press
✔ UART prints show stable timestamps
✔ No spurious triggers from bouncing
✔ Pending flag clears correctly