【嵌套向量中断控制器(NVIC)

NVIC

NVIC 是一种片上控制器,可为 ARM Cortex-M MCU 中的中断驱动事件提供快速和低延迟的响应。在本教程中,我们将解释嵌套向量中断控制器(NVIC) 在 ARM Cortex-M 微控制器的中断处理请求中的作用。

*中断介绍

中断可以定义为系统异常或外围中断,它们可以导致程序流跳转到不同的位置。顾名思义,中断会妨碍正常的程序执行。硬件(即外部输入、复位按钮或其他外围设备)和软件(即内部输入或内核中断)都可以产生中断。如今,几乎每个微控制器都支持中断功能。

发生中断时会发生什么?

每当发生硬件或软件异常时,特定外设或外部/内部输入都需要对该中断作出响应。作为响应,会发生函数调用,并以称为服务例程 (SR) 或中断服务例程 (ISR) 的一段代码的形式执行所需的响应。在服务中的那组指令之后,执行例程,控制转移回发生中断的主程序。具体来说,当发生中断时,会执行以下一组步骤。
在这里插入图片描述

Cortex-M3 内核有一个专门管理中断的外设 NVIC ( Nested Vectored Interrupt Controller , 嵌套向量中断控制器),通过优先级控制中断的嵌套和调度。NVIC 是一个总的中断控制器,无论是来在内 核的异常还是外设的外部中断,都由NVIC 统一进行管理。
在 Cortex-M3 中,将优先级拆分为抢占优先级( Preempt Priority )和子优先级( Subpriority ),每个中断都需要指定这两级,具有高优先级的中断可以打断低优先级的中断,实现中断嵌套。

NVIC寄存器简介

/**
## 中断 NVIC 结构体定义
 */
typedef struct
{
  __IO uint32_t ISER[8];                 /*!<Offset: 0x000  中断使能寄存器          */
  uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                 /*!<Offset: 0x080  中断清除寄存器          */
  uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];            	 /*!<Offset: 0x100  中断使能悬起寄存器      */
  uint32_t RESERVED2[24];                                   
  __IO uint32_t ICPR[8];                 /*!<Offset: 0x180  中断清除悬起寄存器 	  */
  uint32_t RESERVED3[24];                                    
  __IO uint32_t IABR[8];            	 /*!<Offset:0x200   中断有效位寄存器       */
  uint32_t RESERVED4[56];                                   
  __IO uint8_t  IP[240];                 /*!< Offset: 0x300  中断优先级寄存器 (8Bit wide) */
  uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                    /*!< Offset: 0xE00  软件触发中断寄存器     */
}  NVIC_Type;   
```在配置中断的时候我们一般只用 ISER、ICER 和 IP 这三个寄存器,ISER 用来使能中断,ICER 用来清除中断,IP 用来设置中断优先级。

**## NVIC 中断配置固件库**

      固件库头文件 core_cm3.h 中,提供了 NVIC 的一些函数,这些函数遵循 CMSIS 规则,只要是 Cortex-M3 的处理器都可以使用,具体如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/c0d80b7d77244675a6d8cb3598558682.png)


优先级的介绍
    优先级的分组由内核外设SCB 的应用程序中断及复位控制寄存器 AIRCR 的 PRIGROUP[10:8] 位决定,F103 分为了 5 组,具体如下:主优先级 = 抢占优先级。
设置优先级分组可调用库函数 NVIC_PriorityGroupConfig() 实现,有关 NVIC 中断相关的库函数都在库文件 misc.c 和 misc.h 中。

## * 配置中断优先级分组:抢占优先级和子优先级

```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)
{
  	/* 检查参数*/
  	assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
    /* 设置优先级分组*/ 
    SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

在这里插入图片描述
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断服务程序执行过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以抢占低抢占式优先级的中断的执行。在抢占式优先级相同的情况下,有几个子优先级不同的中断同时到来,那么高子优先级的中断优先被响应。
在抢占式优先级相同的情况下,如果有低子优先级中断正在执行,高子优先级的中断要等待已被响应的低子优先级中断执行结束后才能得到响应,即子优先级不支持中断嵌套。Reset、NMI、Hard Fault 优先级为负数,高于普通中断优先级,且优先级不可配置。

NVIC中断编程步骤

    在配置每个中断的时候一般有 3 个编程步骤:

    1、使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。

    2、初始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。NVIC_InitTypeDef 结构体在固件库头文件 misc.h 中定义。
typedef struct
{
  uint8_t NVIC_IRQChannel;                      /*!< 中断源 */
 
  uint8_t NVIC_IRQChannelPreemptionPriority;    /*!< 抢占优先级 */
 
  uint8_t NVIC_IRQChannelSubPriority;         	/*!< 子优先级*/
 
  FunctionalState NVIC_IRQChannelCmd;           /*!< 中断使能或者失能 */   
} NVIC_InitTypeDef;

a. NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序 也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。

typedef enum IRQn
{
/******		Cortex-M3 处理器异常编号 		***********/
  NonMaskableInt_IRQn      = -14,    /*!< 2 不可屏蔽中断*/
  MemoryManagement_IRQn    = -12,    /*!< 4 Cortex-M3 内存管理中断*/
  BusFault_IRQn            = -11,    /*!< 5 Cortex-M3 总线故障中断*/
  UsageFault_IRQn          = -10,    /*!< 6 Cortex-M3 使用故障中断*/
  SVCall_IRQn              = -5,     /*!< 11 Cortex-M3 SV 呼叫中断*/
  DebugMonitor_IRQn        = -4,     /*!< 12 Cortex-M3 调试监视器中断*/
  PendSV_IRQn              = -2,     /*!< 14 Cortex-M3挂起 SV 中断*/
  SysTick_IRQn             = -1,     /*!< 15 Cortex-M3 系统滴答中断*/
 
/******  STM32 特定中断号码*******/
  WWDG_IRQn                = 0,      /*!< 窗口看门狗中断*/
  PVD_IRQn                 = 1,      /*!< PVD 通过 EXTI 线路检测中断*/
  TAMPER_IRQn              = 2,      /*!< 篡改中断*/
  RTC_IRQn                 = 3,      /*!< RTC 全局中断*/
  FLASH_IRQn               = 4,      /*!< 闪存全局中断*/
  RCC_IRQn                 = 5,      /*!< RCC 全局中断*/
  EXTI0_IRQn               = 6,      /*!< EXTI Line0 中断*/
  EXTI1_IRQn               = 7,      /*!< EXTI Line1 中断*/
  EXTI2_IRQn               = 8,      /*!< EXTI Line2 中断*/
  EXTI3_IRQn               = 9,      /*!< EXTI Line3 中断*/
  EXTI4_IRQn               = 10,     /*!< EXTI Line4 中断*/
  DMA1_Channel1_IRQn       = 11,     /*!< DMA1 Channel 1 全局中断*/
  DMA1_Channel2_IRQn       = 12,     /*!< DMA1 Channel 2 全局中断*/
  DMA1_Channel3_IRQn       = 13,     /*!< DMA1 Channel 3 全局中断*/
  DMA1_Channel4_IRQn       = 14,     /*!< DMA1 Channel 4 全局中断*/
  DMA1_Channel5_IRQn       = 15,     /*!< DMA1 Channel 5 全局中断*/
  DMA1_Channel6_IRQn       = 16,     /*!< DMA1 Channel 6 全局中断*/
  DMA1_Channel7_IRQn       = 17,     /*!< DMA1 Channel 7 全局中断*/
  ADC1_2_IRQn              = 18,     /*!< ADC1 and ADC2 全局中断*/
  USB_HP_CAN1_TX_IRQn      = 19,     /*!< USB 设备高优先级或 CAN1 TX 中断*/
  USB_LP_CAN1_RX0_IRQn     = 20,     /*!< USB 设备低优先级或 CAN1 RX0 中断*/
  CAN1_RX1_IRQn            = 21,     /*!< CAN1 RX1 中断*/
  CAN1_SCE_IRQn            = 22,     /*!< CAN1 单片机中断*/
  EXTI9_5_IRQn             = 23,     /*!< External Line[9:5] 中断*/
  TIM1_BRK_IRQn            = 24,     /*!< TIM1 打破 中断*/
  TIM1_UP_IRQn             = 25,     /*!< TIM1 更新 中断*/
  TIM1_TRG_COM_IRQn        = 26,     /*!< TIM1 触发和换向中断*/
  TIM1_CC_IRQn             = 27,     /*!< TIM1 捕获比较中断*/
  TIM2_IRQn                = 28,     /*!< TIM2 全局中断*/
  TIM3_IRQn                = 29,     /*!< TIM3 全局中断*/
  TIM4_IRQn                = 30,     /*!< TIM4 全局中断*/
  I2C1_EV_IRQn             = 31,     /*!< I2C1 事件中断*/
  I2C1_ER_IRQn             = 32,     /*!< I2C1 错误中断*/
  I2C2_EV_IRQn             = 33,     /*!< I2C2 事件中断*/
  I2C2_ER_IRQn             = 34,     /*!< I2C2 错误中断*/
  SPI1_IRQn                = 35,     /*!< SPI1 全局中断*/
  SPI2_IRQn                = 36,     /*!< SPI2 全局中断*/
  USART1_IRQn              = 37,     /*!< USART1 全局中断*/
  USART2_IRQn              = 38,     /*!< USART2 全局中断*/
  USART3_IRQn              = 39,     /*!< USART3 全局中断*/
  EXTI15_10_IRQn           = 40,     /*!< External Line[15:10] 中断*/
  RTCAlarm_IRQn            = 41,     /*!< 通过 EXTI 线路中断发出 RTC 报警*/
  USBWakeUp_IRQn           = 42,     /*!< USB 设备通过 EXTI 线路中断从挂起状态唤醒*/
  TIM8_BRK_IRQn            = 43,     /*!< TIM8 打破中断*/
  TIM8_UP_IRQn             = 44,     /*!< TIM8 更新中断*/
  TIM8_TRG_COM_IRQn        = 45,     /*!< TIM8 触发和换向中断*/
  TIM8_CC_IRQn             = 46,     /*!< TIM8 捕获比较中断*/
  ADC3_IRQn                = 47,     /*!< ADC3 全局中断*/
  FSMC_IRQn                = 48,     /*!< FSMC 全局中断*/
  SDIO_IRQn                = 49,     /*!< SDIO 全局中断*/
  TIM5_IRQn                = 50,     /*!< TIM5 全局中断*/
  SPI3_IRQn                = 51,     /*!< SPI3 全局中断*/
  UART4_IRQn               = 52,     /*!< UART4 全局中断*/
  UART5_IRQn               = 53,     /*!< UART5 全局中断*/
  TIM6_IRQn                = 54,     /*!< TIM6 全局中断*/
  TIM7_IRQn                = 55,     /*!< TIM7 全局中断*/
  DMA2_Channel1_IRQn       = 56,     /*!< DMA2 Channel 1 全局中断*/
  DMA2_Channel2_IRQn       = 57,     /*!< DMA2 Channel 2 全局中断*/
  DMA2_Channel3_IRQn       = 58,     /*!< DMA2 Channel 3 全局中断*/
  DMA2_Channel4_5_IRQn     = 59      /*!< DMA2 Channel 4 and Channel 5 全局中断*/
} IRQn_Type;
      b. NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定,具体参考下图。

    c. NVIC_IRQChannelSubPriority:子优先级,具体的值要根据优先级分组来确定,具体参考下图。
    d. NVIC_IRQChannelCmd:中断使能(ENABLE)或者失能(DISABLE)。操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。

代码举例:
在配置中断优先级的函数中需要这样写:

NVIC_InitTypeDef NVIC_InitStructure;
 
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能按键外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道NVIC_Init(&NVIC_InitStructure);//⑤初始化 NVIC

在main.c中需要调用此函数:


 
```c
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
//抢占优先级可选0-3,响应优先级可选0~3;

3、编写中断服务函数。

在启动文件 startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值