2. STM32中断系统与NVIC优先级工程
在实时控制系统中,中断是连接外设事件与CPU处理逻辑的核心机制。STM32的中断系统由中断向量表、嵌套向量中断控制器(NVIC)以及外设中断使能逻辑共同构成。理解这三者的协作关系,是设计确定性响应系统的前提。本节从向量表结构出发,逐步深入到NVIC优先级分组、抢占与子优先级的工程配置,并通过外部中断与GPIO响应的实战案例,建立可复现的中断延迟测量方法。
中断向量表与异常入口
STM32的中断向量表位于Flash起始地址(通常为0x08000000),表中每个表项为4字节,存放对应异常或中断服务函数(ISR)的入口地址。前16个表项为内核异常,包括复位、NMI、HardFault、SysTick等;从第16个表项开始为外设中断,如EXTI0、TIM2、USART1等。以STM32F407为例,EXTI0_IRQHandler位于向量表偏移0x0058处,对应中断号6。
工程中通常使用启动文件(startup_stm32f407xx.s)定义向量表,并通过弱符号(WEAK)提供默认处理函数。当用户在C代码中重新定义同名函数时,链接器会用用户实现覆盖弱符号。因此,编写ISR时函数名必须与向量表中的符号完全一致,否则中断触发后将跳转到默认死循环。
NVIC优先级分组与抢占机制
NVIC为每个中断分配一个8位优先级寄存器,但STM32实际只使用高4位,因此优先级范围为0~15,数值越小优先级越高。这4位被划分为抢占优先级和子优先级两部分,划分方式由NVIC_PriorityGroupConfig()决定,共有5种分组:
| 分组 | 抢占优先级位数 | 子优先级位数 | 抢占级数 | 子优先级级数 |
|---|---|---|---|---|
| Group0 | 0 | 4 | 1 | 16 |
| Group1 | 1 | 3 | 2 | 8 |
| Group2 | 2 | 2 | 4 | 4 |
| Group3 | 3 | 1 | 8 | 2 |
| Group4 | 4 | 0 | 16 | 1 |
抢占优先级决定中断能否嵌套:高抢占优先级的中断可以打断正在执行的低抢占优先级ISR。子优先级仅在多个中断同时挂起时决定响应顺序,不影响嵌套。例如,Group2下,中断A抢占优先级为1、子优先级为0,中断B抢占优先级为2、子优先级为3,则A可以打断B,反之不行。
工程配置时,先调用NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2),再为每个中断调用NVIC_Init(),在NVIC_InitTypeDef中指定NVIC_IRQChannel、NVIC_IRQChannelPreemptionPriority和NVIC_IRQChannelSubPriority。注意:同一工程中所有中断必须使用相同的分组,否则优先级行为不可预测。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef nvic;
nvic.NVIC_IRQChannel = EXTI0_IRQn;
nvic.NVIC_IRQChannelPreemptionPriority = 1;
nvic.NVIC_IRQChannelSubPriority = 0;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
中断嵌套与延迟测量
中断嵌套的深度受抢占优先级级数限制。在Group2下最多支持4级嵌套。嵌套发生时,CPU自动将当前上下文压栈,嵌套ISR返回后恢复。需要注意的是,嵌套会增加栈空间消耗,每个嵌套层至少占用32字节(8个寄存器)加上ISR局部变量。在实时性要求高的场景中,应合理规划优先级,避免过深嵌套导致栈溢出。
中断延迟指从中断事件发生到ISR第一条指令执行的时间。它由三部分组成:外设到NVIC的传播延迟、NVIC仲裁延迟、CPU压栈与取向量延迟。在STM32F407@168MHz下,典型中断延迟约为12个时钟周期(约71ns),但若存在更高优先级ISR正在执行,则需等待其完成或嵌套。测量方法:在GPIO中断触发时翻转一个空闲GPIO,用示波器观察触发信号与翻转信号之间的时间差。示例中,将PA0配置为上升沿触发,PA1配置为推挽输出,在EXTI0_IRQHandler中执行GPIO_SetBits(GPIOA, GPIO_Pin_1),示波器测得延迟约0.3μs(含GPIO翻转时间)。
外部中断与GPIO响应实战
以按键触发外部中断为例,硬件连接:按键一端接PA0,另一端接GND,PA0配置为上拉输入。当按键按下时,PA0产生下降沿。配置步骤如下:使能GPIOA时钟和SYSCFG时钟;配置PA0为输入模式并启用上拉;将EXTI0映射到PA0(SYSCFG_EXTILineConfig);配置EXTI0为下降沿触发并使能中断;配置NVIC中EXTI0_IRQn的抢占优先级和子优先级;使能EXTI0中断。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_0;
gpio.GPIO_Mode = GPIO_Mode_IN;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &gpio);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
EXTI_InitTypeDef exti;
exti.EXTI_Line = EXTI_Line0;
exti.EXTI_Mode = EXTI_Mode_Interrupt;
exti.EXTI_Trigger = EXTI_Trigger_Falling;
exti.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef nvic;
nvic.NVIC_IRQChannel = EXTI0_IRQn;
nvic.NVIC_IRQChannelPreemptionPriority = 2;
nvic.NVIC_IRQChannelSubPriority = 1;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
ISR中必须清除EXTI挂起标志,否则中断会反复触发。同时建议加入软件消抖或使用定时器辅助消抖,避免机械按键抖动导致多次中断。在实时控制中,若按键中断与电机控制中断共存,应将电机控制中断设为更高抢占优先级,确保控制周期不被按键处理阻塞。
总结本节要点:向量表决定入口地址,NVIC分组决定优先级结构,抢占优先级决定嵌套能力,子优先级决定同抢断时的响应顺序。工程中应统一分组、合理分配优先级、测量实际延迟,并在ISR中遵循“快进快出”原则,将耗时处理交给主循环或RTOS任务。