5.14 NVIC 使用说明
本章说明 N32G41x 标准外设库中 NVIC 驱动的文件组成与 5 个公开函数,内容包括中断优先级分组与中断通道配置、向量表重定位、低功耗模式条件选择以及 SysTick 时钟源选择;示例部分说明 NVIC_IRQ_Mask、NVIC_IRQ_Priority、NVIC_VectorTable_Relocation 三个示例。中断通道、向量表、优先级分组与 SysTick 校准值请配合用户手册的“中断和事件”章节阅读,芯片已知问题以勘误手册为准。
5.14.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32g41x_std_periph_driver/inc/misc.h |
定义 NVIC_InitType 结构体、优先级分组与抢占、子优先级取值宏、向量表基址与低功耗模式宏,并按 NVIC 配置、SysTick 配置两个区段声明 5 个公开函数 |
firmware/n32g41x_std_periph_driver/src/misc.c |
实现上述 5 个函数:写 SCB->AIRCR 设置优先级分组,按分组把两级优先级压成 8 位写 NVIC->IP 并写 NVIC->ISER、NVIC->ICER 使能或关闭通道,写 SCB->VTOR 重定位向量表,改 SCB->SCR 与 SysTick->CTRL |
projects/n32g41x_EVAL/examples/NVIC/ |
NVIC 示例根目录,含 NVIC_IRQ_Mask、NVIC_IRQ_Priority、NVIC_VectorTable_Relocation 3 个示例 |
projects/n32g41x_EVAL/examples/NVIC/NVIC_IRQ_Mask/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32g41x_EVAL/bsp/ |
三个示例共用的板级支持文件:评估板按键宏与串口日志接口 |
5.14.2 API 参考
以下 5 个公开函数按 misc.h 的声明顺序分为 2 组;每个条目给出功能摘要与 misc.c 中该函数正上方的注释原文。
NVIC 配置
NVIC_PriorityGroupConfig、NVIC_Init、NVIC_SetVectorTable、NVIC_SystemLPConfig
SysTick 配置
SysTick_CLKSourceConfig
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)配置优先级分组:抢占优先级与子优先级。
name NVIC_PriorityGroupConfig
fun Configures the priority grouping: pre-emption priority and subpriority.
param NVIC_PriorityGroup :
- NVIC_PriorityGroup_0 0 bits for pre-emption priority 4 bits for subpriority
- NVIC_PriorityGroup_1 1 bits for pre-emption priority 3 bits for subpriority
- NVIC_PriorityGroup_2 2 bits for pre-emption priority 2 bits for subpriority
- NVIC_PriorityGroup_3 3 bits for pre-emption priority 1 bits for subpriority
- NVIC_PriorityGroup_4 4 bits for pre-emption priority 0 bits for subpriority
return none
使用提示: 实现把 NVIC_PriorityGroup 与访问键 0x05FA0000 按位或后写入 SCB->AIRCR,一次写操作同时给出优先级分组。
void NVIC_Init(const NVIC_InitType *NVIC_InitStruct)按结构体初始化参数中指定的参数初始化 NVIC 外设。
name NVIC_Init
fun Initializes the NVIC peripheral according to the specified parameters in the NVIC_structure_initializes.
param NVIC_InitStruct :
- NVIC_IRQChannel :
- IRQn_Type Interrupt Number Definition
if NVIC_PriorityGroup is NVIC_PriorityGroup_0 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0
- NVIC_SUB_PRIORITY_1
- NVIC_SUB_PRIORITY_2
- NVIC_SUB_PRIORITY_3
- NVIC_SUB_PRIORITY_4
- NVIC_SUB_PRIORITY_5
- NVIC_SUB_PRIORITY_6
- NVIC_SUB_PRIORITY_7
- NVIC_SUB_PRIORITY_8
- NVIC_SUB_PRIORITY_9
- NVIC_SUB_PRIORITY_10
- NVIC_SUB_PRIORITY_11
- NVIC_SUB_PRIORITY_12
- NVIC_SUB_PRIORITY_13
- NVIC_SUB_PRIORITY_14
- NVIC_SUB_PRIORITY_15
if NVIC_PriorityGroup is NVIC_PriorityGroup_1 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0
- NVIC_PRE_PRIORITY_1
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0 to NVIC_SUB_PRIORITY_7
if NVIC_PriorityGroup is NVIC_PriorityGroup_2 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0 to NVIC_PRE_PRIORITY_3
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0 to NVIC_SUB_PRIORITY_3
if NVIC_PriorityGroup is NVIC_PriorityGroup_3 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0 to NVIC_PRE_PRIORITY_7
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0
- NVIC_SUB_PRIORITY_1
if NVIC_PriorityGroup is NVIC_PriorityGroup_4 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0
- NVIC_PRE_PRIORITY_1
- NVIC_PRE_PRIORITY_2
- NVIC_PRE_PRIORITY_3
- NVIC_PRE_PRIORITY_4
- NVIC_PRE_PRIORITY_5
- NVIC_PRE_PRIORITY_6
- NVIC_PRE_PRIORITY_7
- NVIC_PRE_PRIORITY_8
- NVIC_PRE_PRIORITY_9
- NVIC_PRE_PRIORITY_10
- NVIC_PRE_PRIORITY_11
- NVIC_PRE_PRIORITY_12
- NVIC_PRE_PRIORITY_13
- NVIC_PRE_PRIORITY_14
- NVIC_PRE_PRIORITY_15
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0
- NVIC_IRQChannelCmd :
- ENABLE
- DISABLE
return none
使用提示: 实现按当前 SCB->AIRCR 中的优先级分组把抢占优先级与子优先级合成 8 位优先级写入 NVIC->IP;NVIC_IRQChannelCmd 为 DISABLE 时只写 NVIC->ICER 关闭通道,不更新优先级。
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)设置向量表的位置与偏移。
name NVIC_SetVectorTable
fun Sets the vector table location and Offset.
param NVIC_vecter_table specifies if the vector table is in RAM or FLASH memory.
This parameter can be one of the following values:
- NVIC_VectTab_RAM
- NVIC_VectTab_FLASH
param offset Vector Table base offset field. This value must be a multiple of 0x200.
return none
使用提示: 实现把 NVIC_VectTab 与 Offset 按 0x1FFFFF00 掩码相与的结果写入 SCB->VTOR,即只清零低 8 位;注释要求的 0x200 整数倍对齐并未在实现中强制。
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState Cmd)选择系统进入低功耗模式的条件。
name NVIC_SystemLPConfig
fun Selects the condition for the system to enter low power mode.
param LowPowerMode Specifies the new mode for the system to enter low power mode.
This parameter can be one of the following values:
- NVIC_LP_SEVONPEND
- NVIC_LP_SLEEPDEEP
- NVIC_LP_SLEEPONEXIT
param - Cmd :
- ENABLE
- DISABLE
return none
使用提示:Cmd 不等于 DISABLE 时把 LowPowerMode 按位或写入 SCB->SCR,否则按位清除;一次可传入多个 NVIC_LP_* 宏的按位或。
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)配置 SysTick 时钟源。
name SysTick_CLKSourceConfig
fun Configures the SysTick clock source.
param SysTick_CLKSource :
- SysTick_CLKSource_HCLK_Div8 External clock selected as SysTick clock source.
- SysTick_CLKSource_HCLK AHB clock selected as SysTick clock source.
return none
使用提示: 实现只在入参等于 SysTick_CLKSource_HCLK 时置位 SysTick->CTRL 的 CLKSOURCE 位,其余取值都清零该位选择 HCLK/8。
5.14.3 Demo 示例说明
本节说明 NVIC 的 3 个示例:NVIC_IRQ_Mask 用全局中断开关演示中断屏蔽对定时器与按键中断的影响,NVIC_IRQ_Priority 通过按键中断与 SysTick 中断的抢占关系演示优先级的配置与动态调整,NVIC_VectorTable_Relocation 把中断向量表从 FLASH 重定位到 SRAM。
5.14.3.1 NVIC_IRQ_Mask 中断屏蔽
工程目录: projects/n32g41x_EVAL/examples/NVIC/NVIC_IRQ_Mask/
使能 GPIO 与 GTIM3 时钟并配置按键外部中断→设置 GTIM3 与按键中断的抢占、子优先级→主循环等待按键中断翻转使能标志→屏蔽全局中断后定时器中断打印停止→按键按下并释放后恢复全局中断
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 __disable_irq 与 __enable_irq 控制全局中断开关,演示中断屏蔽对 GTIM3 周期中断与按键 EXTI4 中断的影响。 |
| 源码组成 | src/main.c:main、KeyInputExtiInit、TIM_Config 与 Key_Status 定义;inc/main.h:按键引脚与 EXTI4 通道宏、函数声明;src/n32g41x_it.c、inc/n32g41x_it.h:EXTI4_IRQHandler、GTIM3_IRQHandler 等中断服务函数;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 时钟源 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz; KEY1 为 PA4, GPIO_MODE_INPUT、GPIO_PULL_UP,对应 EXTI_LINE4,下降沿触发;优先级分组为 NVIC_PriorityGroup_2;EXTI4_IRQn 抢占优先级 1、子优先级 0;GTIM3_IRQn 抢占优先级 0、子优先级 0。GTIM3 的周期寄存值为 49999,预分频值取 RCC_Clocks.HclkFreq / 50000 - 1,计数器向上计数。 |
| 程序与数据路径 | main 调用 log_init 后打印 NVIC IRQ Mask,先设优先级分组,再配置 GTIM3 与按键 EXTI;GTIM3_IRQHandler 在每次更新中断打印 GTIM3 IRQHandler;按键触发 EXTI4_IRQHandler 时 Key_Status 在 ENABLE 与 DISABLE 之间翻转,主循环读到 ENABLE 后调用 __disable_irq,等按键再次按下并释放后调用 __enable_irq。 |
| 主要 API | NVIC_PriorityGroupConfig、NVIC_Init、GPIO_InitPeripheral、GPIO_ConfigEXTILine、EXTI_InitPeripheral、TIM_InitTimeBase、TIM_ConfigPrescaler、TIM_ConfigInt、TIM_Enable |
| 验证方法 | 复位运行后串口周期性打印 GTIM3 IRQHandler;按下 KEY1 后串口打印 EXTI4 IRQHandler Start、EXTI4 IRQHandler End 与 Disable irq,定时器打印停止;再次按下并释放 KEY1 后打印 enable irq,定时器打印恢复。 |
| 使用提示 | 支持 N32G412、N32G415 芯片; 开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1; 按键为评估板 KEY1(PA4),按下电平为 Bit_RESET。 |
核心代码解读与移植
示例先固定优先级分组,再分别给按键与 GTIM3 配置中断优先级,最后由主循环用全局中断开关验证屏蔽效果。
数据路径: main → NVIC_PriorityGroupConfig 写 SCB->AIRCR → NVIC_Init 写 NVIC->IP 与 NVIC->ISER → EXTI4_IRQHandler 翻转 Key_Status → 主循环调用 __disable_irq、__enable_irq → 串口打印停止与恢复
1. 先固定优先级分组。 NVIC_PriorityGroupConfig 写 SCB->AIRCR 的 PRIGROUP 位,本示例取 NVIC_PriorityGroup_2,即抢占优先级与子优先级各 2 位;后续 NVIC_Init 按该分组把两级优先级压成 8 位,分组改了要同步复核各中断的取值。
/* Configure two bits for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
2. 按键中断用较低的抢占优先级。 KeyInputExtiInit 填好 NVIC_InitType 后调用 NVIC_Init,把 EXTI4_IRQn 设为抢占 1、子 0 并打开通道。
/* Set key input interrupt priority */
NVIC_InitStructure.NVIC_IRQChannel = KEY_INPUT_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRE_PRIORITY_1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SUB_PRIORITY_0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
换按键引脚或中断线时,NVIC_IRQChannel 要与 inc/main.h 中的 KEY_INPUT_IRQn 保持一致。
3. GTIM3 用更高的抢占优先级。 TIM_Config 把 GTIM3_IRQn 设为抢占 0、子 0,使其抢占优先级高于按键中断。
/* Set GTIM3 interrupt priority */
NVIC_InitStructure.NVIC_IRQChannel = GTIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRE_PRIORITY_0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SUB_PRIORITY_0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
调整两个中断的抢占次序时改这两处的 NVIC_PRE_PRIORITY_*,不要只改其中一处。
4. 主循环用全局中断开关验证屏蔽。 主循环等到 Key_Status 被 EXTI4_IRQHandler 置位后调用 __disable_irq,等按键再按下并释放后调用 __enable_irq,期间 GTIM3 中断不再打印。
while (1)
{
while (Key_Status == DISABLE) {}
log_info("Disable irq \r\n");
__disable_irq();
while(GPIO_ReadInputDataBit(KEY_INPUT_PORT, KEY_INPUT_PIN) == RESET) {}
while(GPIO_ReadInputDataBit(KEY_INPUT_PORT, KEY_INPUT_PIN) == SET) {}
log_info("enable irq \r\n");
__enable_irq();
}
__disable_irq、__enable_irq 只操作 PRIMASK,屏蔽期间已挂起的中断在开中断后仍会响应;退出屏蔽的条件是按键先按下再释放。
对应源码: src/main.c、inc/main.h
5.14.3.2 NVIC_IRQ_Priority 中断优先级
工程目录: projects/n32g41x_EVAL/examples/NVIC/NVIC_IRQ_Priority/
使能 GPIO 时钟并把 KEY1 配为输入、外部中断下降沿触发→设置按键 EXTI4 抢占优先级 1、SysTick 抢占优先级 0→按键触发 EXTI4 中断时挂起 SysTick 异常→SysTick 中断读取 EXTI4 的活动状态并置位 Key_Status→主循环把 SysTick 抢占优先级改为 2
| 项目 | 源码解读 |
|---|---|
| 功能 | 用按键外部中断与 SysTick 中断的抢占关系演示中断优先级的配置与动态调整:按键中断挂起 SysTick 异常,SysTick_Handler 用 NVIC_GetActive 判断按键中断是否被抢占,主循环据此修改 SysTick 的抢占优先级。 |
| 源码组成 | src/main.c:main、KeyInputExtiInit、Delay 与 PreemptionPriorityValue、Key_Status 定义;inc/main.h:按键引脚与 EXTI4 通道宏、函数声明;src/n32g41x_it.c、inc/n32g41x_it.h:SysTick_Handler、EXTI4_IRQHandler 等中断服务函数;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 时钟源 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz; KEY1 为 PA4, GPIO_MODE_INPUT、GPIO_PULL_UP,对应 EXTI_LINE4,下降沿触发;优先级分组为 NVIC_PriorityGroup_2;EXTI4_IRQn 抢占优先级 1、子优先级 0;SysTick_IRQn 初始抢占优先级 0,Key_Status 为 1 时改为抢占优先级 2;主循环用 Delay(0x8FFFFF) 插入延时。 |
| 程序与数据路径 | main 打印 NVIC IRQ Priority 后调用 KeyInputExtiInit,再用 NVIC_SetPriority 把 SysTick_IRQn 抢占优先级设为 0;主循环按 Key_Status 打印对应状态,为 1 时把 SysTick 抢占优先级改为 PreemptionPriorityValue + 2;EXTI4_IRQHandler 写 SCB->ICSR 的 PENDSTSET 位挂起 SysTick 异常,SysTick_Handler 用 NVIC_GetActive 判断 EXTI4 是否仍在执行,是则置 Key_Status 为 1,否则置 2。 |
| 主要 API | NVIC_PriorityGroupConfig、NVIC_Init、NVIC_SetPriority、NVIC_EncodePriority、NVIC_GetPriorityGrouping、NVIC_GetActive、GPIO_InitPeripheral、GPIO_ConfigEXTILine、EXTI_InitPeripheral |
| 验证方法 | 运行后串口打印 SysTick 中断信息与 Key_Status 状态;按下 KEY1 触发外部中断后,EXTI4 与 SysTick 同时触发时把 SysTick 的抢占优先级改为 2,串口打印 Key_Status = 1 与 NVIC IRQ Priority Test End。 |
| 使用提示 | 支持 N32G412、N32G415 芯片; 开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1; 按键为评估板 KEY1(PA4)。 |
核心代码解读与移植
示例把优先级分组放在按键初始化里,再用 CMSIS 的优先级接口为 SysTick 设定抢占优先级,最后在中断与主循环之间用状态变量传递抢占结果。
数据路径: KeyInputExtiInit → NVIC_PriorityGroupConfig 与 NVIC_Init 设定按键优先级 → NVIC_SetPriority 用 NVIC_EncodePriority、NVIC_GetPriorityGrouping 写 SysTick 优先级 → EXTI4_IRQHandler 写 SCB->ICSR 挂起 SysTick → SysTick_Handler 用 NVIC_GetActive 采样并置 Key_Status → 主循环按状态再次调用 NVIC_SetPriority
1. 用 CMSIS 接口设定 SysTick 优先级。 main 初始化按键中断后,用 NVIC_GetPriorityGrouping 读当前分组,再用 NVIC_EncodePriority 把抢占优先级与子优先级编码后交给 NVIC_SetPriority;抢占优先级变量初值为 0。
KeyInputExtiInit(KEY_INPUT_PORT, KEY_INPUT_PIN);
/* Configure the SysTick Handler Priority: Preemption priority(0) and subpriority */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), PreemptionPriorityValue, 0));
NVIC_GetPriorityGrouping 与 NVIC_EncodePriority 保证编码方式与实际分组一致;以 NVIC_SetPriority 改优先级前,分组不应再被别的初始化改写。
2. 按状态动态抬高 SysTick 的抢占优先级。 主循环每轮重新读取 Key_Status:为 1 时把 SysTick 抢占优先级改为 PreemptionPriorityValue + 2,为 2 时打印测试结束,其余情况打印 Key_Status = 0。
while (1)
{
if (Key_Status == 1)
{
log_info("Key_Status = 1 \r\n");
/* Configure the SysTick Handler Priority: Preemption priority(2) and subpriority */
NVIC_SetPriority(SysTick_IRQn,
NVIC_EncodePriority(NVIC_GetPriorityGrouping(), PreemptionPriorityValue + 2, 0));
}
else if (Key_Status == 2)
{
log_info("NVIC IRQ Priority Test End \r\n");
}
else
{
log_info("Key_Status = 0 \r\n");
}
/* Insert delay Time */
Delay(0x8FFFFF);
}
抢占优先级数值越小优先级越高,+ 2 是把 SysTick 的抢占优先级调低;子优先级固定传 0,因为按键中断的子优先级也是 0。
3. 按键中断里挂起 SysTick 异常。 EXTI4_IRQHandler 在确认 EXTI4 挂起后写 SCB->ICSR 的 PENDSTSET 位,让 SysTick 异常在按键中断退出前被挂起,再清除 EXTI 挂起位。
if (EXTI_GetStatusFlag(KEY_INPUT_EXTI_LINE) != RESET)
{
/* Generate SysTick exception */
SCB->ICSR |= 0x04000000;
/* Clears the SEL Button EXTI line pending bits */
EXTI_ClrStatusFlag(KEY_INPUT_EXTI_LINE);
}
写 SCB->ICSR 的 PENDSTSET 位只是把 SysTick 异常置为挂起,是否立即抢占取决于 SysTick 与当前中断的抢占优先级。
4. 在 SysTick 中断里判断抢占关系。 SysTick_Handler 用 NVIC_GetActive 读按键中断是否仍处于活动状态:仍在活动说明按键中断被 SysTick 抢占,置 Key_Status 为 1;否则置 2,供主循环读取。
/* If the EXTI4 IRQ Handler was preempted by SysTick Handler */
if (NVIC_GetActive(KEY_INPUT_IRQn) != 0)
{
Key_Status = 1;
}
else
{
Key_Status = 2;
}
NVIC_GetActive 读的是中断的活跃状态而不是挂起状态;在主循环里长期轮询该值会被实时变化的抢占关系干扰,所以示例只用中断里的采样结果。
对应源码: src/main.c、src/n32g41x_it.c、inc/main.h
5.14.3.3 NVIC_VectorTable_Relocation 向量表重定位
工程目录: projects/n32g41x_EVAL/examples/NVIC/NVIC_VectorTable_Relocation/
使能 GPIO 时钟并配置按键外部中断→设置 EXTI4 抢占优先级与子优先级→按键触发外部中断并置位 Key_Status→主循环把中断向量表从 FLASH 重定位到 SRAM→重定位后停在空循环等待
| 项目 | 源码解读 |
|---|---|
| 功能 | 演示中断向量表重定位:按键触发 PA4 的外部中断后,把中断向量表从 FLASH 重定位到 SRAM,并用串口打印重定位提示。 |
| 源码组成 | src/main.c:main、KeyInputExtiInit 与 Key_Status 定义;inc/main.h:VECT_TAB_FLASH_OFFSET、VECT_TAB_SRAM_OFFSET 与按键引脚、EXTI4 通道宏;src/n32g41x_it.c、inc/n32g41x_it.h:EXTI4_IRQHandler 等中断服务函数;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 时钟源 HSI+PLL,N32G412 为 80 MHz、N32G415 为 96 MHz; KEY1 为 PA4, GPIO_MODE_INPUT、GPIO_PULL_UP,对应 EXTI_LINE4,下降沿触发;优先级分组为 NVIC_PriorityGroup_2;EXTI4_IRQn 抢占优先级 0、子优先级 0;重定位目标是 NVIC_VectTab_RAM 加偏移 VECT_TAB_SRAM_OFFSET(0x00010000)。 |
| 程序与数据路径 | main 打印 NVIC VectorTable Relocation 并调用 KeyInputExtiInit 配置按键 EXTI 与中断优先级;主循环轮询 Key_Status,其变为 ENABLE 后打印提示并调用 NVIC_SetVectorTable 写 SCB->VTOR,再进入 while(1) 空循环;EXTI4_IRQHandler 在工程未定义 VECT_TAB_SRAM 时把 Key_Status 置为 ENABLE 并清除 EXTI 挂起位。 |
| 主要 API | NVIC_PriorityGroupConfig、NVIC_Init、NVIC_SetVectorTable、GPIO_InitPeripheral、GPIO_ConfigEXTILine、EXTI_InitPeripheral |
| 验证方法 | 复位运行后初始状态下中断向量表位于 FLASH; 按下 KEY1 触发外部中断后,串口打印 EXTI4 IRQHandler Start、EXTI4 IRQHandler is executed(FLASH)(工程未定义 VECT_TAB_SRAM)与 Sets the vector table location and Offset,随后程序停在空循环。 |
| 使用提示 | 支持 N32G412、N32G415 芯片; 开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1; 按键为评估板 KEY1(PA4)。 |
核心代码解读与移植
示例用按键中断作为触发条件,在主循环里调用向量表重定位接口,并据此停在一个空循环中不再返回。
数据路径: main → KeyInputExtiInit 配置按键 EXTI 与优先级 → EXTI4_IRQHandler 置 Key_Status 为 ENABLE → 主循环调用 NVIC_SetVectorTable 写 SCB->VTOR → 进入空循环
1. 先读偏移宏。 inc/main.h 用两个宏给出 FLASH 与 SRAM 两种向量表偏移,示例只用其中的 SRAM 偏移,取值 0x00010000。
#define VECT_TAB_FLASH_OFFSET ((uint32_t)0x00000000)
#define VECT_TAB_SRAM_OFFSET ((uint32_t)0x00010000)
2. 按键中断的优先级在初始化里给出。 KeyInputExtiInit 先配置分组,再把 EXTI4_IRQn 设为抢占 0、子 0。
/* Configure two bits for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Set key input interrupt priority */
NVIC_InitStructure.NVIC_IRQChannel = KEY_INPUT_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRE_PRIORITY_0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SUB_PRIORITY_0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
3. 中断服务函数按宏区分两条分支。 EXTI4_IRQHandler 用条件编译按是否定义 VECT_TAB_SRAM 给出不同行为:定义时只打印 SRAM 分支提示,未定义时把 Key_Status 置为 ENABLE;工程的目标宏为 N32G412 与 N32G415,未定义 VECT_TAB_SRAM,因此走 #else 分支。
#ifdef VECT_TAB_SRAM
log_info("EXTI4 IRQHandler is executed(SRAM) \r\n");
#else
/* Key_Status */
Key_Status = ENABLE;
log_info("EXTI4 IRQHandler is executed(FLASH) \r\n");
#endif
4. 主循环完成重定位并停住。 main 等到 Key_Status 变为 ENABLE 后调用 NVIC_SetVectorTable,把向量表基址改为 NVIC_VectTab_RAM 加偏移,随后进入不再返回的空循环。
if (Key_Status == ENABLE)
{
log_info("Sets the vector table location and Offset \r\n");
/* Vector Table Relocation in Internal SRAM. */
NVIC_SetVectorTable(NVIC_VectTab_RAM, VECT_TAB_SRAM_OFFSET);
while (1)
;
}
重定位后中断向量改由 SRAM 中的表提供,NVIC_SetVectorTable 的偏移参数按注释要求应为 0x200 的整数倍,换偏移值时注意对齐。
对应源码: src/main.c、src/n32g41x_it.c、inc/main.h
5.14.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| 外设中断未响应 | 用户手册的中断和事件章节写明要产生中断,必须配置并打开对应中断线的 NVIC 中断通道; 只置位外设或 EXTI 的中断使能位而不打开对应的 NVIC 通道,中断不会被内核响应。 |
用户手册 |
| 中断通道与优先级取值范围 | 用户手册写明本芯片有 47 个可屏蔽中断通道(不包括 16 个 ARM Cortex-M4 的中断),支持 16 个中断优先级; 选择 NVIC_IRQChannel 与抢占、子优先级取值时须落在此范围内。 |
用户手册 |
| 固定的内核异常优先级 | 用户手册的向量表列出 Reset、NMI、HardFault 的优先级类型为固定(优先级依次为 -3、-2、-1),不受优先级分组设置与 NVIC_Init 影响。 |
用户手册 |
| SysTick 的 1 ms 时间基准 | 用户手册写明系统嘀嗒校准值固定为 8000,当系统嘀嗒时钟设定为 8 MHz(HCLK/8 的最大值)时产生 1 ms 时间基准; 以 SysTick 计时或配置其周期时按该组合换算。 |
用户手册 |
N32G412、N32G415 系列勘误手册 V1.0.0(2026.6.30)的勘误列表只列出一行 A 版芯片条目,勘误链接列为空,未列出 NVIC 相关条目。