中断:打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行。
文章目录
1 中断类型
F429 在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。其中系统异常有 10 个,外部中断有 91 个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。
- 外部中断清单可查看中文参考手册的中断篇章
- 系统中断清单:

2 NVIC
NVIC(Nested Vectored Interrupt Controller)是嵌套向量中断控制器的缩写,负责管理微控制器中的中断请求。
- 控制中断的优先级,内核中的中断数量很多,当同时出现多个中断时,优先处理哪个中断?以及那些中断不处理等,都要靠NVIC 进行控制。
- 支持嵌套中断,即高优先级的中断可以打断低优先级的中断,main函数的优先级最低。
- 提供中断向量表,便于 CPU 确定中断处理程序的位置。
- 定义一块固定的内存,以4字节对齐(32位),用于存放终端服务函数的首地址,系统已经将中断服务函数定义好了,放在中断向量表中,我们只需要进行调用即可,如上图F429 系统异常清单。
- NVIC 与 Cortex-M4 内核密切耦合,是内核的一个重要组成部分。它直接影响中断处理的效率和响应时间。
- 虽然 NVIC 是内核的一部分,但它也可以被视为一个外设,提供了对中断的控制和管理功能。
- 同芯片厂商在设计时会根据特定需求对 Cortex-M4 内核中的 NVIC 进行裁剪, 因此,STM32 的 NVIC 可以被视为 Cortex-M4 NVIC 的一个子集,包含了基本的中断管理功能,但可能缺少某些高级功能。
2.1 NVIC 工作原理

- 当外部事件触发中断时,系统首先需要识别并控制这些中断。
- ICER(Interrupt Clear Enable Register): 用于清除对应中断的使能位,通常在处理中断时使用。
- ISER(Interrupt Set Enable Register): 用于设置和使能对应的中断。这两个寄存器共同控制中断的使能状态。
- IPR(Interrupt Priority Register): 经过 ISER 使能的中断会进入 IPR 寄存器进行优先级判断。IPR 寄存器的优先级配置由 SCB 的 AIRCR(Application Interrupt and Reset Control Register)寄存器中的 PRIGROUP 位控制。
- SHPR(System Handler Priority Register): 用于控制内核中断(例如系统异常)的优先级。SHPR 寄存器与 IPR 寄存器处于同一级别,意味着它们在优先级控制方面具有相同的重要性。
- 根据中断的优先级,系统将按照优先级高低的顺序依次进入 CPU 执行处理。
2.2 NVIC 寄存器
// core_cm4.h /** * \brief 访问嵌套向量中断控制器 (NVIC) 的结构类型 */ typedef struct { __IO uint32_t ISER[8]; /*!< 偏移: 0x000 (R/W) 中断使能寄存器 */ uint32_t RESERVED0[24]; /*!< 保留区域 */ __IO uint32_t ICER[8]; /*!< 偏移: 0x080 (R/W) 中断清除使能寄存器 */ uint32_t RESERVED1[24]; /*!< 保留区域 */ __IO uint32_t ISPR[8]; /*!< 偏移: 0x100 (R/W) 中断设置挂起寄存器 */ uint32_t RESERVED2[24]; /*!< 保留区域 */ __IO uint32_t ICPR[8]; /*!< 偏移: 0x180 (R/W) 中断清除挂起寄存器 */ uint32_t RESERVED3[24]; /*!< 保留区域 */ __IO uint32_t IABR[8]; /*!< 偏移: 0x200 (R/W) 中断活动位寄存器 */ uint32_t RESERVED4[56]; /*!< 保留区域 */ __IO uint8_t IP[240]; /*!< 偏移: 0x300 (R/W) 中断优先级寄存器(8位宽) */ uint32_t RESERVED5[644]; /*!< 保留区域 */ __O uint32_t STIR; /*!< 偏移: 0xE00 ( /W) 软件触发中断寄存器 */ } NVIC_Type;
2.3 NVIC 中断函数
固件库文件 core_cm4.h 提供了 NVIC 的一些函数,这些函数遵循 CMSI 规则,只要是Cortex-M4 的处理器都可以使用:

3 优先级的定义
NVIC(Nested Vectored Interrupt Controller)中有一个重要的寄存器,用于配置外部中断的优先级。
3.1 寄存器定义
- 中断优先级寄存器: NVIC_IPRx 寄存器用于配置外部中断的优先级。
- 在 STM32F429 中,寄存器的编号为 x,从 0 到 90,表示可以配置最多 91 个外部中断的优先级。
- 宽度: 每个 IPR 寄存器的宽度为 8 位(8 bits),用于存储中断的优先级设定。
3.2 优先级配置
- 优先级范围: 理论上,每个外部中断可配置的优先级范围为 0 到 255,数值越小,优先级越高。
- 实际支持的优先级: 由于大多数 Cortex-M4 芯片的设计精简,实际上支持的优先级数会减少。以 STM32F429 为例,寄存器只使用了高 4 位(0-15),这意味着实际可配置的优先级数量只有 16 个。

3.3 抢占优先级与子优先级
- 抢占优先级: 在 NVIC 中,配置的高 4 位通常用于表示抢占优先级。抢占优先级高的中断可以打断低优先级的中断,确保高优先级的任务能优先执行。
- 子优先级: 如果多个中断同时响应,并且它们的抢占优先级相同,则会进一步比较子优先级。
- 硬件中断编号: 如果抢占优先级和子优先级都相同,NVIC 会根据中断的硬件编号进行比较,编号越小的中断优先级越高。
3.4 优先级分组
在 STM32F429 微控制器中,中断优先级的分组由内核外设 SCB(系统控制块)的应用程序中断及复位控制寄存器 AIRCR(Application Interrupt and Reset Control Register)中的 PRIGROUP[10:8] 位决定,F429 分为了 5 组。
- AIRCR 寄存器在 Cortex-M4权威指南 文件中
设置优先级分组可调用库函数 NVIC_PriorityGroupConfig() 实现,有关 NVIC 中断相关的库函数都在库文件 misc.c 和 misc.h 中。
/** * @brief 配置优先级分组:抢占优先级和子优先级。 * @param NVIC_PriorityGroup: 指定优先级分组位长度。 * 此参数可以是以下值之一: * @arg NVIC_PriorityGroup_0: 0 位用于抢占优先级 * 4 位用于子优先级 * @arg NVIC_PriorityGroup_1: 1 位用于抢占优先级 * 3 位用于子优先级 * @arg NVIC_PriorityGroup_2: 2 位用于抢占优先级 * 2 位用于子优先级 * @arg NVIC_PriorityGroup_3: 3 位用于抢占优先级 * 1 位用于子优先级 * @arg NVIC_PriorityGroup_4: 4 位用于抢占优先级 * 0 位用于子优先级 * @note 当选择 NVIC_PriorityGroup_0 时,抢占中断不再可能。 * 挂起的 IRQ 优先级将仅由子优先级管理。 * @retval 无 */ void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { /* 检查参数 */ assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); /* 根据 NVIC_PriorityGroup 值设置 PRIGROUP[10:8] 位 */ SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }


4 中断程序
- 使能外设中断
- 每个外设的中断使能由特定的控制寄存器中的中断使能位来控制。例如,串口外设可能有发送完成中断和接收完成中断,这两个中断的使能位在串口控制寄存器中进行配置。确保正确设置这些寄存器以使能所需的中断。
- 初始化 NVIC_InitTypeDef 结构体:用于配置中断优先级和使能中断请求
- NVIC_IRQChannel: 设置中断源。每个中断的源是唯一的,并且必须正确配置。若配置错误,程序不会报错,但会导致中断失效。中断源在stm32f4xx.h 头文件中的 IRQn_Type 结构体定义
- NVIC_IRQChannelPreemptionPriority: 设置抢占优先级,具体值需根据优先级分组进行确定,参考优先级分组真值表。
- NVIC_IRQChannelSubPriority: 设置子优先级,具体值同样需根据优先级分组进行确定,参考优先级分组真值表。
- NVIC_IRQChannelCmd: 控制中断使能(ENABLE)或失能(DISABLE)。该操作会影响 NVIC_ISER 和 NVIC_ICER 这两个寄存器。
/** * @brief NVIC 初始化结构体定义 */ typedef struct { uint8_t NVIC_IRQChannel; /*!< 指定要使能或禁用的 IRQ 通道。 此参数可以是 @ref IRQn_Type 枚举的一个枚举值 (有关完整的 STM32 设备 IRQ 通道列表,请参阅 stm32f4xx.h 文件) */ uint8_t NVIC_IRQChannelPreemptionPriority; /*!< 指定所述 IRQ 通道的抢占优先级 此参数可以是 0 到 15 之间的值,如 @ref MISC_NVIC_Priority_Table 表所示 较低的优先级值表示较高的优先级 */ uint8_t NVIC_IRQChannelSubPriority; /*!< 指定所述 IRQ 通道的子优先级级别 此参数可以是 0 到 15 之间的值,如 @ref MISC_NVIC_Priority_Table 表所示 较低的优先级值表示较高的优先级 */ FunctionalState NVIC_IRQChannelCmd; /*!< 指定在 NVIC_IRQChannel 中定义的 IRQ 通道 是启用还是禁用。 此参数可以设置为 ENABLE 或 DISABLE */ } NVIC_InitTypeDef;
- 编写中断服务函数
- 在启动文件 startup_stm32f429_439xx.s 中,系统预设了每个中断的服务函数,但这些函数通常为空,目的是初始化中断向量表。实际的中断服务函数需要开发者自行编写,通常放在 stm32f4xx_it.c 文件中。
- 函数名要求: 中断服务函数的名称必须与启动文件中预定义的名称一致。如果名称不正确,系统将在中断向量表中找不到正确的中断服务函数入口,结果将直接跳转到启动文件中的空函数,并在其中进入无限循环,这将导致中断无法正常处理。
5 中断优先级设置
在 STM32F429 的外设配置过程中,设置中断优先级是一个重要步骤。
5.1 中断优先级分组配置
- 在配置每个外设的中断时,首先需要调用 NVIC_PriorityGroupConfig() 函数来设置中断优先级分组。这一步骤只需在程序中执行一次,确保整个程序使用统一的优先级分组设置。
- 例如,如果你选择了 NVIC_PriorityGroup_0 或 NVIC_PriorityGroup_4,那么所有外设的中断优先级都将基于这个设置进行解析
5.2 中断向量和优先级配置
- 设置好优先级分组后,使用 NVIC_Init(&NVIC_InitStructure) 函数为每个外设配置中断向量和其优先级。此时,NVIC_InitStructure 结构体的 NVIC_IRQChannelPreemptionPriority 和 NVIC_IRQChannelSubPriority 字段应根据已经设置的优先级分组进行适当配置。
- 如果在程序中已经定义了优先级分组,则在给多个外设的中断向量填充优先级时,确保这些优先级的配置与分组设置相符合。
5.3 示例
static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 配置NVIC为优先级组1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置中断源:按键1 */ NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ; /* 配置抢占优先级:1 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 配置子优先级:1 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中断通道 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }