External Interrupts in STM32 Microcontrollers

An interrupt is a mechanism that temporarily suspends the main program execution when a specific trigger condition occurs (interrupt source). The CPU then jumps to handle the interrupt service routine (ISR) and returns to the paused location after completion. Interrupt priority determines the order of handling when multiple interrupts occur simultaneously, with nested interrupts allowing higher-priority interrupts to preempt lower-priority ones.

STM32 Interrupt Architecture

The STM32 features 68 maskable interrupt channels including EXTI, TIM, ADC, USART, SPI, I2C, and RTC peripherals. The Nested Vectored Interrupt Controller (NVIC) manages all interrupts, providing 16 programmable priority levels that can be grouped into preeemption and sub-priority bits for flexible priority handling.

NVIC Structure

The NVIC is a core peripheral that acts as the CPU's interrupt management assistant. It receives interrupt requests from multiple sources and determines execution order based on configured priorities. Key NVIC functions include:

void NVIC_PriorityGroupConfig(uint32_t PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
void NVIC_SetVectorTable(uint32_t VectTab, uint32_t Offset);
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

Priority Grouping

NVIC uses 4 priority bits (0-15) that can be divided between preemption priority (higher bits) and sub-priority (lower bits). Higher preemption priority enables interrupt nesting, while higher sub-priority determines queue order when preemption priorities are equal.

External Interrupt Controller (EXTI)

The EXTI monitors specified GPIO pins for level changes and triggers NVIC interrupts when configured conditions occur. It supports:

  • Trigger modes: rising edge, falling edge, both edges, software trigger
  • All GPIO pins (same pin numbers cannot trigger simultaneously)
  • 16 GPIO lines plus PVD, RTC alarm, USB wakeup, Ethernet wakeup
  • Response modes: interrupt (to CPU) or event (to other peripherals)

Key EXTI functions include:

void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

AFIO and Pin Configuration

The Alternate Functon I/O (AFIO) peripheral manages pin remapping and interrupt line selection. Essential AFIO functions:

void GPIO_AFIODeInit(void);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

Rotary Encoder Implementation

A rotary encoder measures position, speed, and rotation direction by outputting square waves corresponding to shaft rotation. The following example demonstrates interrupt-based counting:

#include "stm32f10x.h"

volatile uint16_t pulseCounter;

void PulseCounter_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    
    GPIO_InitTypeDef gpioConfig;
    gpioConfig.GPIO_Mode = GPIO_Mode_IPU;
    gpioConfig.GPIO_Speed = GPIO_Speed_50MHz;
    gpioConfig.GPIO_Pin = GPIO_Pin_14;
    GPIO_Init(GPIOB, &gpioConfig);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
    
    EXTI_InitTypeDef extiConfig;
    extiConfig.EXTI_Line = EXTI_Line14;
    extiConfig.EXTI_LineCmd = ENABLE;
    extiConfig.EXTI_Mode = EXTI_Mode_Interrupt;
    extiConfig.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&extiConfig);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitTypeDef nvicConfig;
    nvicConfig.NVIC_IRQChannel = EXTI15_10_IRQn;
    nvicConfig.NVIC_IRQChannelCmd = ENABLE;
    nvicConfig.NVIC_IRQChannelPreemptionPriority = 1;
    nvicConfig.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&nvicConfig);
}

uint16_t PulseCounter_Read(void)
{
    return pulseCounter;
}

void EXTI15_10_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line14) == SET) {
        pulseCounter++;
        EXTI_ClearITPendingBit(EXTI_Line14);
    }
}

Quadrature Encoder Implementation

This example shows bidirectional counting using a quadrature encoder with two phase signals:

#include "stm32f10x.h"

volatile int16_t encoderDelta;

void QuadratureEncoder_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    
    GPIO_InitTypeDef gpioConfig;
    gpioConfig.GPIO_Mode = GPIO_Mode_IPU;
    gpioConfig.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    gpioConfig.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpioConfig);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
    
    EXTI_InitTypeDef extiConfig;
    extiConfig.EXTI_Line = EXTI_Line0 | EXTI_Line1;
    extiConfig.EXTI_LineCmd = ENABLE;
    extiConfig.EXTI_Mode = EXTI_Mode_Interrupt;
    extiConfig.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&extiConfig);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    NVIC_InitTypeDef nvicConfig;
    nvicConfig.NVIC_IRQChannel = EXTI0_IRQn;
    nvicConfig.NVIC_IRQChannelCmd = ENABLE;
    nvicConfig.NVIC_IRQChannelPreemptionPriority = 1;
    nvicConfig.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&nvicConfig);
    
    nvicConfig.NVIC_IRQChannel = EXTI1_IRQn;
    nvicConfig.NVIC_IRQChannelSubPriority = 2;
    NVIC_Init(&nvicConfig);
}

int16_t QuadratureEncoder_GetDelta(void)
{
    int16_t delta = encoderDelta;
    encoderDelta = 0;
    return delta;
}

void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) == SET) {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) {
                encoderDelta--;
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

void EXTI1_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line1) == SET) {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) {
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
                encoderDelta++;
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

Tags: STM32 NVIC EXTI interrupts gpio

Posted on Tue, 29 Sep 2026 16:41:55 +0000 by AdamBrill