Chapter 9: External Interrupts (EXTI)

📚 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 EXTI peripheral and how it connects GPIO pins to the NVIC.

  • Route GPIO pins to EXTI lines using SYSCFG_EXTICR.

  • Configure EXTI lines for rising, falling, or both-edge triggering using EXTI_RTSR and EXTI_FTSR regsters.

  • 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:

  • EXTI lines are numbered 0–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_RTSR1 and EXTI_FTSR1.

  • Interrupt unmasking is controlled by EXTI_IMR1.

  • Pending flags are reported in EXTI_PR1 and cleared by writing 1.

  • NVIC interrupt vectors are grouped: EXTI0_1, EXTI2_3, EXTI4_15.

You can then explicitly note:

  • On STM32G0B1, EXTI is decoupled from SYSCFG.

  • 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 EXTICR register controls four EXTI lines

  • Each line gets its own 8‑bit field (not 4 bits like older families)

  • Even though each line it’s 8-bits, only the lower 3 bits are 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

  • EXTI1 is controlled by EXTICR1 (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 EXTI lines may trigger the same ISR

  • Your ISR must check which line fired by reading EXTI_RPR1 or EXTI_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