26.3.16 ZD-EP56 [中断/NVIC] 什么是中断?

我也想知道什么是中断。


1 什么是中断

简单来说,打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断。

中断具有以下作用:

  • 实时控制:在特定时间内对相应事件做出响应,如温度监控。
  • 故障处理:检测到故障第一时间处理。
  • 数据传输:不确定数据接收时间,在接收到数据时中断等待执行后续语句。

中断的意义是高效处理紧急程序,不会一直占用CPU资源。

STM32 GPIO外部中断的示意图如下:

STM32 GPIO外部中断简图

首先外部中断通过GPIO传递到SYSCFG(System Configuration)进而将信息写入EXTI进行初步处理,最后通过NVIC使能/除能相应中断并控制其优先级再交由CPU处理中断。

这里我们先对NVIC进行介绍:


2 NVIC介绍及其工作原理

NVIC,Nested Vectored Interrupt Controller嵌套向量中断控制器,属于内核部分。

NVIC支持256个中断(16个内核+240个外部)和256个优先级且允许裁切,但实际情况用不到这么多的中断和优先级,于是ST公司将NVIC的中断与优先级进行了裁切,针对STM32F407,内核中断裁切成10个,外部中断裁切成82个,优先级裁切成16个。那么这个“个”到底是怎么算的?

  • 中断的 “个”:是可独立触发和处理的中断事件通道数,每个通道对应一个 IRQn(Interrupt Request number),比如 EXTI0_IRQnTIM3_IRQn。这里的IRQn对应着每个系统/硬件的中断事件,具体事件举例可以参考待会讲到的中断向量表。
  • 优先级的 “个”:是可分配的优先级等级总数,用来控制中断的嵌套和响应顺序。优先级操作通过中断优先级寄存器(IPR)进行,它本身容量是8位的,但STM32只使用了高四位,因此可以产生24=16个数,对应16个优先级,具体寄存器及优先级逻辑介绍见后。

2.1 中断向量表

中断向量表是定义在启动文件的一个对应表格,它定义了一块固定的内存,以4字节对齐(即每4字节/32位对应一个中断程序,且严格按照4字节的大小进行分配,防止出现乱序读取的情况),用于存放各个中断服务函数程序的首地址,当中断发生时,CPU会自动执行对应的中断服务函数。具体逻辑让我们看到下面这张表:

中断向量表作用示意图

正常情况下CPU执行main函数,当CPU接受到中断信号时,在向量表中查找对应中断的中断服务函数,他们都可以抢断CPU的使用权,进而让CPU执行对应的中断服务函数。

我们来看一段系统中断向量表的示意:

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

系统中断向量表定义

这里的DCD全称为Define Constant Data,这里的Constant Date就可以理解成是4字节,这里的操作就是以四字节对齐,可以看到这里总共定义了10个中断服务函数,对应了内核中断裁切成了10个,我们也可以参考下表:

中断向量表-系统中断部分

可以看到这里也是对应了10个中断服务。

2.2 NVIC寄存器

NVIC寄存器的相关定义可以在core_cm4.h文件中找到,这里我们直接看到其定义内容:

typedef struct
{
  __IOM uint32_t ISER[8U]; /* 中断使能寄存器 */
    uint32_t RESERVED0[24U];
  __IOM uint32_t ICER[8U]; /* 中断清除使能寄存器 */
    uint32_t RSERVED1[24U];
  __IOM uint32_t ISPR[8U]; /* 中断使能挂起寄存器 */
    uint32_t RESERVED2[24U];
  __IOM uint32_t ICPR[8U]; /* 中断解挂寄存器 */
    uint32_t RESERVED3[24U];
  __IOM uint32_t IABR[8U]; /* 中断有效位寄存器 */
    uint32_t RESERVED4[56U];
  __IOM uint8_t IP[240U]; /* 中断优先级寄存器(8Bit 位宽) */
    uint32_t RESERVED5[644U];
  __OM uint32_t STIR; /* 中断触发中断寄存器 */
} NVIC_Type;

NVIC寄存器定义

这里我们主要看到ISER,ICER,AIRCR,IPR,下面我们逐个来介绍:

  • ISER[8]:Interrupt Set Enable Register,即中断使能寄存器,作用就是使能某个中断进而进行对应操作,它是一个32位寄存器,且共有8个,他的每一个位都能控制一个中断,所以理论上他能控制32*8=256个中断,但STM32将它裁切到了82个,所以理论上我们能控制的ISER只有[0~2]共三个,其中0和1各控制32个,3控制18个。
  • ICER[8]:Interrupt Clear Enable Register,即中断除能寄存器,作用就是清除某个中断的使能。其对应位功能与ISER一样,这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的(类似端口置位/复位寄存器)。
  • ISPR[8]:Interrupt Set Pending Registers,即中断使能挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
  • ICPR[8]:Interrupt Clear Pending Registers,即中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断解挂。写 0 无效。
  • IABR[8]:Interrupt Active Bit Registers,即中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
  • IP(R)[240]Interrupt Priority Registers,即中断优先级控制的寄存器组。这个寄存器组与STM32F407 的中断分组密切相关。IP 寄存器组由 240 个 8bit的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32F407只用到了其中的 82 个。IP[81]~IP[0]分别对应中断 81~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。关于中断优先级控制的寄存器组我们下面详细讲解。
  • 此外还有一个AIRCR(Application Interrupt and Reset Control Register应用程序中断及复位控制寄存器),它是一个32位寄存器,其[10:8]位用来控制优先级分组,因此理论上能分23=8组,但因STM32在IPR中只使用了高4位,只能分出0:4,1:3,2:2,3:1,4:0共五组,详细分法见后。

2.3 中断优先级

顾名思义,中断优先级就是CPU处理中断的先后顺序,也可以用来表示中断抢夺CPU控制权的能力。STM32中的中断优先级可以分为抢占式优先级和响应优先级响应优先级也称子优先级,每个中断源都需要被指定这两种优先级

两者区别如下:

  • 抢占优先级:抢占优先级高的中断可以打断正在执行的抢占优先级低的中断。
  • 响应优先级:抢占优先级相同,响应优先级高的中断不能打断响应优先级低的中断。

另外还有一种优先级为自然优先级,当抢占优先级和响应优先级都相同时比较自然优先级,自然优先级即中断向量表中的排序,三个优先级均遵循数值越小越优先的原则(对应到中断向量表就是越靠前越优先)。

在 NVIC 中由寄存器 NVIC_IPR0-NVIC_IPR59 共 60 个寄存器控制中断优先级,每个寄存器的每 8 位又分为一组,可以分 4 组,所以就有了 240 组宽度为 8bit 的中断优先级控制寄存器,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是实际上 M3 /M4/M7 芯片为了精简设计,只使用了高四位[7:4],低四位取零,这样以至于最多只有 16 级中断嵌套,即 2^4=16。

NVIC的优先级分组参考AIRCR寄存器的设置对IPR的高四位进行拆分能得到如下分组:

AIRCR中断分组设置表

以防还是不太理解,再详细解释一下就是AIRCR中有三位([10:8])来设置分组,理论上能设置23=8组,但分组的实际分法要参考IPR中抢占优先级和响应优先级的拆分,而STM32只使用了IPR中的高4位部分([7:4]),因此对高4位只纯在 0位对应抢占优先级,4位对应响应优先级;1位对应抢占优先级,3位对应响应优先级...以此类推总共只有5种情况,于是按这五种情况分配到AIRCR中的八组情况中。总而言之这两个寄存器就是相互制约的,AIRCR决定了IPR的分组,而IPR又影响着AIRCR的分组。

我们下面参考这个实例来讲解:

STM32中断优先级举例(分组情况为2,即抢占和响应各2位)

在分组为2的情况下,抢占优先级和响应优先级各有两位,各能储存22=4个优先级信息,根据乘法原理 ,总共能储存4*4=16个优先级信息。

优先级的排序遵循数值越小优先级越大,抢占优先级大于响应优先级,抢占优先级和响应优先级相同就看自然优先级(中断向量表)

因此我们看到这个实例,先看抢占优先级,最小的为2,因此RTC和EXTI1的中断会更先执行,而这两者的响应优先级EXTI1小于RTC,于是最先处理的中断就是EXTI1,其次就是EXTI1;再看抢占优先级为3的EXTI0和Systick,不难发现他们的响应优先级也相同,这个时候我们看自然优先级,可以发现Systick的自然优先级为6小于EXTI0的13,因此Systick第三个执行,EXTI0最后执行。

在这个过程中,因抢占优先级不同,RTC和EXTI1可以打断EXTI0的Systick的中断,而抢占优先级相同的EXTI1不能打断RTC的中断。

2.4 NVIC工作原理

如图:

NVIC工作原理示意图

其中SHPR全称为System Handler Priority Registers,系统处理优先级寄存器,它是一组专门用于配置 内核异常 / 中断 优先级的寄存器,和管理外部中断的 IPR(Interrupt Priority Registers) 是同级别的优先级控制模块。可以理解成SHPR是处理芯片内部中断的IPR。

其余相关寄存器均已解释,这里不再过多解释,明白外部外部中断在传导到NVIC时要先进行使能/除能处理再按分组进行优先级排序最后传导给CPU即可。


3 NVIC的使用及其相关函数

如图:

NVIC的使用流程

可以看到NVIC的使用过程和它的工作原理是反过来的,先设置分组,再设置优先级,最后进行使能操作。

3.1 HAL_NVIC_SetPriorityGrouping()

中断分组的设置在HAL_init()中进行,通过HAL_NVIC_SetPriorityGrouping()函数实现,其声明如下:

void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)

HAL_NVIC_SetPriorityGrouping()函数声明

还函数只有一个形参,即PriorityGroup优先级组,输入内容如下:

/**
  * @brief  Sets the priority grouping field (preemption priority and subpriority)
  *         using the required unlock sequence.
  * @param  PriorityGroup The priority grouping bits length. 
  *         This parameter can be one of the following values:
  *         @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority
  *                                    4 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority
  *                                    3 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority
  *                                    2 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority
  *                                    1 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority
  *                                    0 bits for subpriority
  * @note   When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible. 
  *         The pending IRQ priority will be managed only by the subpriority. 
  * @retval None
  */

PriorityGroup@refer内容

跳转后可以得到:

#define NVIC_PRIORITYGROUP_0         0x00000007U /*!< 0 bits for pre-emption priority
                                                      4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1         0x00000006U /*!< 1 bits for pre-emption priority
                                                      3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2         0x00000005U /*!< 2 bits for pre-emption priority
                                                      2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3         0x00000004U /*!< 3 bits for pre-emption priority
                                                      1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4         0x00000003U /*!< 4 bits for pre-emption priority
                                                      0 bits for subpriority */

PriorityGroup输入内容宏定义

可以看到对应输入的内容就是0-7除去0,1,2,即除去AIRCR[10:8]中的三种情况。

3.2 HAL_NVIC_SetPriority()

分组后即进行优先级设置,通过HAL_NVIC_SetPriority()函数实现,该函数声明如下:

void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority,uint32_t SubPriority);

HAL_NVIC_SetPriority()函数声明

其中IRQn即前面提到的中断请求编号,它对应的是IRQn_Type结构体,以F407为例,定义在 stm32f407xx.h 中,下面给出部分内容:

  DMA2_Stream7_IRQn           = 70,     /*!< DMA2 Stream 7 global interrupt                                    */
  USART6_IRQn                 = 71,     /*!< USART6 global interrupt                                           */
  I2C3_EV_IRQn                = 72,     /*!< I2C3 event interrupt                                              */
  I2C3_ER_IRQn                = 73,     /*!< I2C3 error interrupt                                              */
  OTG_HS_EP1_OUT_IRQn         = 74,     /*!< USB OTG HS End Point 1 Out global interrupt                       */
  OTG_HS_EP1_IN_IRQn          = 75,     /*!< USB OTG HS End Point 1 In global interrupt                        */
  OTG_HS_WKUP_IRQn            = 76,     /*!< USB OTG HS Wakeup through EXTI interrupt                          */
  OTG_HS_IRQn                 = 77,     /*!< USB OTG HS global interrupt                                       */
  DCMI_IRQn                   = 78,     /*!< DCMI global interrupt                                             */
  RNG_IRQn                    = 80,     /*!< RNG global Interrupt                                              */
  FPU_IRQn                    = 81      /*!< FPU global interrupt                                               */

stm32f407xx.h中有关IRQn_Typy的结构体定义的部分内容

其共有0-81个成员,对应F407的外部中断个数。

而后面两个形参就分别为抢占优先级和响应优先级,可输入范围在0-15(取决于分组情况)。

3.3 HAL_NVIC_EnableIRQ()

设置完优先级即可开始使能,通过HAL_NVIC_EnableIRQ()函数实现,其声明如下:

void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);

HAL_NVIC_EnableIRQ()函数声明

通过形参IRQn直接对应到要使能的外部中断。

3.4 其他相关函数

此外另有HAL_NVIC_DisableIRQ()中断除能函数,形参与使能函数一致和HAL_NVIC_SystemReset()系统复位函数,无形参.

除能中断的作用有:临时屏蔽中断,防止中断干扰正常流程;禁用中断,减少无用外设的资源损耗;调试与故障处理。


4 结语

其实这个NVIC我没听懂,我听懂了吗?如听。蠕动。。

欢迎订阅 Subscribe to 沿江路右转Turn Right at Yanjiang Rd.

期待您的精彩评论 Looking forward to your wonderful comments.
[email protected]
订阅 Subscribe
赣ICP备2026002696号