文章目录
- 一、NVIC简介
-
- [ NVIC 做什么?](# NVIC 做什么?)
- 二、NVIC模块详解
-
- [ 1、NVIC 寄存器](# 1、NVIC 寄存器)
- [ 2、优先级的定义](# 2、优先级的定义)
-
- [ 1.优先级寄存器NVIC_IPRx](# 1.优先级寄存器NVIC_IPRx)
- [ 2.优先级分组](# 2.优先级分组)
- [ 3、NVIC 工作完整流程](# 3、NVIC 工作完整流程)
- [ 4、F103中断向量表](# 4、F103中断向量表)
-
- [ 1.内核异常向量(固定,所有 CM3 通用)](# 1.内核异常向量(固定,所有 CM3 通用))
- [ 2.外部中断向量(STM32F103 专用)](# 2.外部中断向量(STM32F103 专用))
- 三、CudeMx配置NVIC
-
- [ 1、NVIC配置界面](# 1、NVIC配置界面)
- [ 2、打开NVIC界面](# 2、打开NVIC界面)
- [ 3、NVIC 页面里有什么?](# 3、NVIC 页面里有什么?)
- [ 4、最重要一步:选择 优先级分组](# 4、最重要一步:选择 优先级分组)
- 四、NVIC模块组成
-
- [ 1、数据结构](# 1、数据结构)
- [ 2、句柄](# 2、句柄)
- [ 3、API](# 3、API)
-
- [ 1. 设置中断优先级](# 1. 设置中断优先级)
- [ 2. 使能中断](# 2. 使能中断)
- [ 3. 禁止中断](# 3. 禁止中断)
- [ 4. 设置优先级分组](# 4. 设置优先级分组)
- [ 5. 挂起中断(手动触发)](# 5. 挂起中断(手动触发))
- [ 6. 清除挂起](# 6. 清除挂起)
- [ 7. 获取中断活动状态](# 7. 获取中断活动状态)
- [ 4、状态/错误](# 4、状态/错误)
-
- [ 1. 使能状态](# 1. 使能状态)
- [ 2. 优先级状态](# 2. 优先级状态)
- [ 3. 挂起状态(Pending)](# 3. 挂起状态(Pending))
- [ 4. 活动状态(Active)](# 4. 活动状态(Active))
- 五、NVIC模块通用模版
-
- [ 1、NVIC配置](# 1、NVIC配置)
- [ 2、开启更新中断](# 2、开启更新中断)
- [ 3、中断服务(stm32f1xx_it.c)](# 3、中断服务(stm32f1xx_it.c))
- [ 4、中断回调函数(处理数据)](# 4、中断回调函数(处理数据))
一、NVIC简介
NVIC:Nested Vectored Interrupt Controller嵌套向量中断控制器,整个芯片的中断管理员。
NVIC 是 Cortex‑M3 内置的中断硬件管理器。
NVIC 做什么?
哪个中断可以触发。
中断优先级谁高谁低。
高优先级中断能不能打断低优先级(嵌套)。
中断发生时,CPU 跳去执行哪个函数。
所有中断(串口、定时器、EXTI、DMA...)都必须经过 NVIC 配置才能用。
二、NVIC模块详解
1、NVIC 寄存器
NVIC 寄存器定义在 core_cm3.h 文件中,CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256 级的可编程中断设置。
STM32F103xE芯片有 70 个中断,包括 10 个内核中断和 60 个可屏蔽中断,具有 16 级可编程的中断优先级,我们常用的就是这 60 个可屏蔽中断。
c
typedef struct {
__IO uint32_t ISER[8]; // 中断使能寄存器
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; // 中断清除寄存器
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; // 中断使能悬起寄存器
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; // 中断清除悬起寄存器
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; // 中断有效位寄存器
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; // 中断优先级寄存器(8Bit wide)
uint32_t RESERVED5[644];
__O uint32_t STIR; // 软件触发中断寄存器
} NVIC_Type;
一般使用ISER、ICER和IP 三个寄存器,IESR用来使能中断,ICER用来失能中断,IP用来设置中断优先级。
ISER[8] :全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。
CM3 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。
但是 STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]),总共可以表示 64 个中断。
ICER[8] :全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。
ISPR[8] :全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。
ICPR[8] :全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。
IABR[8] :全称是:Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。
IP[240] :全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。
IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。
而 STM32 只用到了其中的前 60 个。IP[59]~IP[0]分别对应中断 59~0。
2、优先级的定义
1.优先级寄存器NVIC_IPRx

用于表达优先级的4bit,又被分组成抢占优先级和子优先级。
如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,
如果抢占优先级相同,就比较子优先级。
如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。
2.优先级分组

设置优先级分组可调用库函数NVIC_PriorityGroupConfig()实现,有关NVIC中断相关的库函数都在库文件misc.c和misc.h中。
中断优先级分组库函数NVIC_PriorityGroupConfig()
c
/**
* 配置中断优先级分组:抢占优先级和子优先级
* 形参如下:
* @arg NVIC_PriorityGroup_0: 0bit for抢占优先级
* 4 bits for 子优先级
* @arg NVIC_PriorityGroup_1: 1 bit for抢占优先级
* 3 bits for 子优先级
* @arg NVIC_PriorityGroup_2: 2 bit for抢占优先级
* 2 bits for 子优先级
* @arg NVIC_PriorityGroup_3: 3 bit for抢占优先级
* 1 bits for 子优先级
* @arg NVIC_PriorityGroup_4: 4 bit for抢占优先级
* 0 bits for 子优先级
* @注意 如果优先级分组为0,则抢占优先级就不存在,优先级就全部由子优先级控制
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
// 设置优先级分组
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
3、NVIC 工作完整流程
外部 / 外设产生中断信号。
中断挂起标志置 1。
NVIC 读取该中断优先级。
NVIC 比较:
若比当前运行中断抢占优先级更高 → 打断!
若相同 → 排队,按子优先级决定顺序。
CPU 自动保存现场(入栈)。
跳转到对应的 中断服务函数(ISR)。
执行完后恢复现场,回到原来程序。
4、F103中断向量表
1.内核异常向量(固定,所有 CM3 通用)

2.外部中断向量(STM32F103 专用)

三、CudeMx配置NVIC
1、NVIC配置界面

2、打开NVIC界面
在 CubeMX 界面:
左边 System Core
点击 NVIC
进入中断配置页面
3、NVIC 页面里有什么?
主要 3 块:
Enable:是否打开中断(打勾 = 使能)
Preemption Priority:抢占优先级
Sub Priority:子优先级
4、最重要一步:选择 优先级分组
在 NVIC 页面上方:
Priority Group (优先级分组)
STM32F103 最常用、最稳妥的是:
NVIC_PriorityGroup_2
2 位抢占优先级(0~3)
2 位子优先级(0~3)
数字越小,优先级越高!
四、NVIC模块组成
1、数据结构
c
typedef struct
{
__IO uint32_t ISER[8]; // 中断使能
__IO uint32_t ICER[8]; // 中断禁止
__IO uint32_t ISPR[8]; // 挂起置位
__IO uint32_t ICPR[8]; // 挂起清除
__IO uint32_t IABR[8]; // 活动标志
__IO uint8_t IP[240]; // 优先级寄存器(最重要)
} NVIC_Type;
2、句柄
NVIC 没有句柄!
3、API

1. 设置中断优先级
HAL_NVIC_SetPriority(IRQn, PreemptPriority, SubPriority);
示例:
HAL_NVIC_SetPriority(USART1_IRQn, 1, 0);
2. 使能中断
HAL_NVIC_EnableIRQ(IRQn);
3. 禁止中断
HAL_NVIC_DisableIRQ(IRQn);
4. 设置优先级分组
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
F103 最常用:GROUP_2(2 位抢占 + 2 位子优先级)
5. 挂起中断(手动触发)
NVIC_SetPendingIRQ(IRQn);
6. 清除挂起
NVIC_ClearPendingIRQ(IRQn);
7. 获取中断活动状态
uint32_t status = NVIC_GetActive(IRQn);
4、状态/错误
NVIC 有 4 种状态:
1. 使能状态
已使能|已禁止
中断是否已使能:
uint32_t en = NVIC_GetEnableIRQ(IRQn);
2. 优先级状态
抢占优先级|子优先级
获取当前优先级分组:
uint32_t pg = HAL_NVIC_GetPriorityGrouping();
3. 挂起状态(Pending)
中断触发了,但还没来得及处理。
中断是否挂起:
uint32_t pending = NVIC_GetPendingIRQ(IRQn);
4. 活动状态(Active)
正在执行中断服务函数。
中断是否正在执行:
uint32_t active = NVIC_GetActive(IRQn);
五、NVIC模块通用模版
NVIC应用实例都与具体外设有关,请查看外设对应实例。
1、NVIC配置
c
// 1. 分组(系统只一次)
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
// 2. 设置优先级
HAL_NVIC_SetPriority(XXX_IRQn, 抢占优先级, 子优先级);
// 3. 使能中断
HAL_NVIC_EnableIRQ(XXX_IRQn);
2、开启更新中断
c
// 4. 开启外设中断
__HAL_XXX_ENABLE_IT(...);
3、中断服务(stm32f1xx_it.c)
c
//5.中断服务
void XXX_IRQHandler(void)
{
HAL_XXX_IRQHandler(&hxxx);
}
4、中断回调函数(处理数据)
c
//6.中断回调函数
void HAL_XXX_Callback(XXXT_HandleTypeDef *hxxx)
{
if(hxxx->Instance == ...)
{
// 接收到数据,在这里处理
}
}