STM32单片机芯片与内部19 NVIC-简介-配置中断分组、优先级等标准库 HAL库

目录

一、NVIC简介

1、中断配置基础

2、中断的使能与除能/失能

3、中断的悬起与解悬

4、优先级

二、基本的配置

1、标准库配置

[2、HAL 库配置](#2、HAL 库配置)

[(1)、 HAL 库中已经假定了优先级分组](#(1)、 HAL 库中已经假定了优先级分组)

(2)、统一配置


前文在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步:

  1. 建立优先级组
  2. 为该中断指定优先级
  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;
相关推荐
嵌入式大圣6 分钟前
单片机TCP无线数据透传
单片机·嵌入式硬件·tcp/ip
qq_459730031 小时前
1-15 GD32ARM存储器
c语言·arm开发·单片机
奇文怪式1 小时前
VSCode编辑+GCC for ARM交叉编译工具链+CMake构建+OpenOCD调试(基于STM32的标准库/HAL库)
arm开发·vscode·stm32
1101 11011 小时前
STM32-笔记1-点亮led灯
笔记·stm32·嵌入式硬件
一只爱做笔记的码农2 小时前
【调试】如何使用vscode搭建openocd调试环境,通过jlink连接芯片进行调试
stm32·嵌入式硬件
Crossoads4 小时前
【汇编语言】内中断(三) —— 中断探险:从do0到特殊响应的奇妙旅程
android·开发语言·javascript·网络·汇编·单片机·机器学习
夜间去看海10 小时前
66 基于单片机的太阳能充电、温度检测、档位PWM调速系统
单片机·嵌入式硬件·太阳能充电·温度检测·pwm调速
野蛮的大西瓜11 小时前
如何持续优化呼叫中心大模型呼出机器人的性能?
java·人工智能·语言模型·自然语言处理·机器人·信息与通信
小猪写代码13 小时前
STM32 进阶SPI案例1:软件模拟SPI读写FLASH
stm32·单片机·嵌入式硬件