4. 中断
📌 本节学习目标
- 理解 Cortex-M3 的异常与中断机制
- 掌握 NVIC 中断控制器的使用
- 学会配置外部中断 EXTI
- 理解中断优先级分组的概念
4.1 中断概述
Cortex-M3 内核内置了 NVIC(嵌套向量中断控制器),支持 240 个中断源,具有 16 级可编程优先级。STM32F103 使用了其中的 43 个中断通道。
4.2 NVIC 中断优先级
Cortex-M3 的中断优先级分为 抢占优先级 和 响应优先级(子优先级)。通过 SCB->AIRCR 寄存器设置优先级分组:
| 分组 | 抢占位数 | 子优先级位数 | 描述 |
|---|---|---|---|
| NVIC_PriorityGroup_0 | 0 | 4 | 所有中断同抢占比,仅有子优先级 |
| NVIC_PriorityGroup_1 | 1 | 3 | 2 级抢占,8 级子优先级 |
| NVIC_PriorityGroup_2 | 2 | 2 | 4 级抢占,4 级子优先级 |
| NVIC_PriorityGroup_3 | 3 | 1 | 8 级抢占,2 级子优先级 |
| NVIC_PriorityGroup_4 | 4 | 0 | 16 级抢占,无子优先级 |
4.3 EXTI 外部中断
STM32 的 EXTI(外部中断/事件控制器)支持 19 个事件/中断线,每个 GPIO 端口对应的引脚共享同一个 EXTI 线(如 PA0、PB0、PC0 共享 EXTI0)。
配置外部中断的一般步骤:
- 使能 GPIO 时钟和 AFIO 时钟
- 配置 GPIO 为输入模式
- 配置 GPIO 引脚与 EXTI 线的映射
- 配置 EXTI 中断线(触发方式、使能)
- 配置 NVIC(优先级、使能)
- 编写中断服务函数
4.4 外部中断编程示例
C 代码(外部中断配置)
#include "stm32f10x.h"
// PA0 按键检测(外部中断,下降沿触发)
void EXTI0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 2. PA0 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 映射 PA0 到 EXTI0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// 4. EXTI0 配置:下降沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 5. NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// 中断服务函数
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 按键处理
GPIO_ToggleBits(GPIOB, GPIO_Pin_0);
EXTI_ClearITPendingBit(EXTI_Line0); // 清中断标志
}
}💡 这段代码怎么用?
- 完整流程就是 6 步:时钟 → GPIO → 映射 → EXTI → NVIC → 服务函数,实验 09/10 按此模板扩展
- 中断服务函数要短小精悍,不要在中断里做长时间延时,否则会阻塞主程序
- 用
EXTI_GetITStatus()判断是否本线中断,处理完务必EXTI_ClearITPendingBit()清标志,否则会反复进入