目录
[2、HAL 库配置](#2、HAL 库配置)
[(1)、 HAL 库中已经假定了优先级分组](#(1)、 HAL 库中已经假定了优先级分组)
前文在EXTI、TIM、SysTick,涉及到中断的都用到了NVIC,今天来进行一下介绍。
一、NVIC简介
内嵌向量中断控制器Nested Vectored Interrupt Controller (NVIC)。
1、中断配置基础
每个外部中断都在 NVIC 的下列寄存器中"挂号":
- 使能与除能寄存器
- 悬起与"解悬"寄存器
- 优先级寄存器
- 活动状态寄存器
另外,下列寄存器也对中断处理有重大影响
- 异常掩蔽寄存器(PRIMASK, FAULTMASK 以及 BASEPRI)
- 向量表偏移量寄存器
- 软件触发中断寄存器
- 优先级分组位段
2、中断的使能与除能/失能
**中断的使能与除能分别使用各自的寄存器来控制------这与传统的, 使用单一比特的两个状态来表达使能与除能是不同的。**CM3 中可以有 240 对使能位/除能位,每个中断拥有一对。这 240 个对子分布在 8 对 32 位寄存器中(最后一对没有用完)。**欲使能一个中断,你需要写 1 到对应 SETENA 的位中;欲除能一个中断,你需要写 1 到对应的 CLRENA 位中;如果往它们中写 0,不会有任何效果。**通过这种方式,使能/除能中断时只需把"当事位"写成1,其它的位可以全部为零。再也不用像以前那样,害怕有些位被写入 0 而破坏其对应的中断设置(写 0 没有效果),从而实现每个中断都可以自顾地设置,而互不侵犯------只需单一的写指令,不再需要读‐改‐写。
3、中断的悬起与解悬
如果中断发生时,正在处理同级或高优先级异常,或者被掩蔽,则**中断不能立即得到响应,此时中断被悬起。**中断的悬起状态可以通过"中断设置悬起寄存器(SETPEND)"和"中断悬起清除寄存器(CLRPEND)"来读取,还可以写它们来手工悬起中断。
悬起寄存器和"解悬"寄存器也可以有 8 对,其用法和用量都与前面介绍的使能/除能寄存器完全相同。
4、优先级
每个外部中断都有一个对应的优先级寄存器,每个寄存器占用 8 位,但是允许最少只使用最高 3 位。 4 个相临的优先级寄存器拼成一个 32 位寄存器。如前所述,根据优先级组设置,优先级可以被分为高低两个位段,分别是抢占优先级和亚优先级。优先级寄存器都可以按字节访问,当然也可以按半字/字来访问。
假设使用的是 优先级组 2,其中抢占优先级占用 3 位,亚优先级占用 5 位。优先级范围如下:
- 抢占优先级(x):可以在 0 到 7 之间配置(3 位)。
- 亚优先级(y):可以在 0 到 31 之间配置(5 位)。
cpp
#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */
所以不同的优先级分组对应的主优先级和次优先级位数并不一样,假如 x 为主优先级(抢占优先级),y 为次优先级(亚优先级),你可以通过以下方式配置它们:
- x 的范围:0 到 7。
- y 的范围:0 到 31。
二、基本的配置
如果应用程序储存在ROM中,并且不需要改变异常服务程序,则我们可以把整个向量表编码到ROM的起始区域(从0地址开始的那段)。在这种情况下,向量表的偏移量将一直为0,并且中断向量一直在ROM中,因此上例可以大大简化,只需3步:
- 建立优先级组
- 为该中断指定优先级
- 使能该中断
cpp
标准库:
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置NVIC为优先级组1 */
/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置中断源:按键1 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
/* 配置抢占优先级 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/* 配置子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
HAL库:
/* 配置 EXTI 中断源 到key1 引脚、配置中断优先级*/
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
/* 使能中断 */
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
如果在I/O密集型系统中,软件需要控制大量的硬件设备,则可能必须要考虑如下因素:
- 该芯片支持的中断数。
- 该芯片中表达优先级的位数。
1、标准库配置
在标准库代码中,首先会通过 NVIC_PriorityGroupConfig()
配置优先级分组,例如:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
这一行的作用是配置 抢占优先级 和 亚优先级 的位数分配方式。NVIC_PriorityGroup_1
表示将 1 位 用于抢占优先级,剩余的 3 位 用于亚优先级。这个配置是全局生效的,即在整个项目中只需要调用一次。
然后,标准库会通过 NVIC_Init()
来配置特定的中断源、抢占优先级、亚优先级以及是否使能该中断。
2、HAL 库配置
在 HAL 库中,配置优先级和使能中断的代码是:
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
HAL 库实际上没有显式地调用 NVIC_PriorityGroupConfig()
来配置优先级分组。为什么呢?这与 HAL 库的设计思路有关。
(1)、 HAL 库中已经假定了优先级分组
HAL 库在底层的初始化过程中,会通过 STM32 HAL 驱动的启动代码或系统配置来默认配置优先级分组。通常,优先级分组的设置会在系统初始化时完成,这意味着你无需在每个外设的中断配置中手动设置优先级分组。
在某些情况下,HAL 库的代码假定你已经在启动代码(如 system_stm32f4xx.c
)中设置了合适的优先级分组。所以在 HAL_NVIC_SetPriority()
调用时,它默认会遵循启动时配置的优先级分组,而不需要再在每次中断初始化时显式地设置。
(2)、统一配置
对于 HAL 库,优先级的配置已经封装在 HAL_NVIC_SetPriority()
和 HAL_NVIC_EnableIRQ()
等函数内部。你只需要关心设置具体的优先级和使能,而不需要关心优先级分组的设置。这种设计使得 HAL 库的接口更加简洁易用。
- 在 标准库 中,你需要显式地调用
NVIC_PriorityGroupConfig()
来设置优先级分组,然后配置抢占优先级和亚优先级。 - 在 HAL 库 中,优先级分组的配置通常已经隐式处理,默认会使用启动代码中设置的分组。你只需要调用
HAL_NVIC_SetPriority()
来配置中断的优先级。
如果你使用 HAL 库,并且对优先级分组有特定要求(例如在一些特殊的项目中需要修改分组),可以通过调用 NVIC_PriorityGroupConfig()
来显式设置优先级分组,但通常情况下,HAL 库已经做了隐式配置,简化了这一步骤。
三、中断向量
cpp
/*!< Interrupt Number Definition */
typedef enum
{
/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/
NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
HardFault_IRQn = -13, /*!< 3 Cortex-M3 Hard Fault Interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */
/****** STM32 specific Interrupt Numbers *********************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
TAMPER_IRQn = 2, /*!< Tamper Interrupt */
RTC_IRQn = 3, /*!< RTC global Interrupt */
FLASH_IRQn = 4, /*!< FLASH global Interrupt */
RCC_IRQn = 5, /*!< RCC global Interrupt */
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */
DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt */
DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt */
DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt */
DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt */
DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt */
DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt */
ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */
USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */
USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */
CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */
TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */
TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */
TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
USART1_IRQn = 37, /*!< USART1 global Interrupt */
USART2_IRQn = 38, /*!< USART2 global Interrupt */
USART3_IRQn = 39, /*!< USART3 global Interrupt */
EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
RTC_Alarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */
USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */
TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */
TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */
TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */
TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */
ADC3_IRQn = 47, /*!< ADC3 global Interrupt */
FSMC_IRQn = 48, /*!< FSMC global Interrupt */
SDIO_IRQn = 49, /*!< SDIO global Interrupt */
TIM5_IRQn = 50, /*!< TIM5 global Interrupt */
SPI3_IRQn = 51, /*!< SPI3 global Interrupt */
UART4_IRQn = 52, /*!< UART4 global Interrupt */
UART5_IRQn = 53, /*!< UART5 global Interrupt */
TIM6_IRQn = 54, /*!< TIM6 global Interrupt */
TIM7_IRQn = 55, /*!< TIM7 global Interrupt */
DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */
DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */
DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */
DMA2_Channel4_5_IRQn = 59, /*!< DMA2 Channel 4 and Channel 5 global Interrupt */
} IRQn_Type;