-
2021-09-13 20:51:27
STM32-中断优先级管理NVIC
1.NVIC中断优先级分组
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。STM32F中文参考手册中搜索向量表可以找到相应的中断说明。
CM4/CM7 内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。
STM32F4/F7并没有使用CM4内核的全部东西,而是只用了它的一部分。
STM32F40xx/STM32F41xx总共有92个中断。10个内核中断,82个可屏蔽中断。
STM32F42xx/STM32F43xx则总共有97个中断。10个内核中断,87个可屏蔽中断。
STM32F76x总共118个中断,10个内核中断,108个可屏蔽中断。STM32具有16级可编程的中断优先级,而我们常用的就是这些可屏蔽中断。
几十个中断,怎么管理?
首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。
分组配置是在寄存器SCB->AIRCR中配置: IP bit[7:4]有4位,2^4=16,所以说它们的优先级可以有16个值,这时候如果是2位抢占优先级,那么它的值可能为0、1、2、3。也就是抢占优先级可以取0到3 。首先进行分组来决定几位抢占优先级、几位响应优先级。数越小它的优先级越高。
组 AIRCR[10:8] IP bit[7:4]分配情况 分配结果 0 111 0:4 0位抢占优先级,4位响应优先级 1 110 1:3 1位抢占优先级,3位响应优先级 2 101 2:2 2位抢占优先级,2位响应优先级 3 100 3:1 3位抢占优先级,1位响应优先级 4 011 4:0 4位抢占优先级,0位响应优先级 抢占优先级 & 响应优先级区别:
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。抢占决定了是否能打断别人。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;举例:
假定设置中断优先级组为2,然后设置
中断3(RTC中断)的抢占优先级为2,响应优先级为1。
中断6(外部中断0)的抢占优先级为3,响应优先级为0
中断7(外部中断1)的抢占优先级为2,响应优先级为0。
那么这3个中断的优先级顺序为:中断7>中断3>中断6。
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
中断优先级分组函数位于HALLIB中的stm32f7xx_hal_cortex.c:void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { /* Check the parameters */ assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup)); /* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */ NVIC_SetPriorityGrouping(PriorityGroup); }
可以找到IS_NVIC_PRIORITY_GROUP的定义,进而确定PriorityGroup参数怎么写。
#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PRIORITYGROUP_0) || \ ((GROUP) == NVIC_PRIORITYGROUP_1) || \ ((GROUP) == NVIC_PRIORITYGROUP_2) || \ ((GROUP) == NVIC_PRIORITYGROUP_3) || \ ((GROUP) == NVIC_PRIORITYGROUP_4))
在stm32f7xx_hal.c中可以找到HAL_Init函数:其中HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);可以看到中断分组为2 。
HAL_StatusTypeDef HAL_Init(void) { /* Configure Flash prefetch and Instruction cache through ART accelerator */ #if (ART_ACCLERATOR_ENABLE != 0) __HAL_FLASH_ART_ENABLE(); #endif /* ART_ACCLERATOR_ENABLE */ /* Set Interrupt Group Priority */ HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */ HAL_InitTick(TICK_INT_PRIORITY); /* Init the low level hardware */ HAL_MspInit(); /* Return function status */ return HAL_OK; }
2.NVIC中断优先级设置
分组设置好之后,怎么设置单个中断的抢占优先级和响应优先级?
中断设置相关寄存器:
_IO uint8_t IP[240]; //中断优先级控制的寄存器组
_IO uint32_t ISER[8]; //中断使能寄存器组
_IO uint32_t ICER[8]; //中断失能寄存器组
_IO uint32_t ISPR[8]; //中断挂起寄存器组
_IO uint32_t ICPR[8]; //中断解挂寄存器组
_IO uint32_t IABR[8]; //中断激活标志位寄存器组位于core_cm7.h中:NVIC_Type结构体中成员变量就是那些寄存器。
/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). */ typedef struct { __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ uint32_t RESERVED0[24U]; __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ uint32_t RSERVED1[24U]; __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ uint32_t RESERVED2[24U]; __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ uint32_t RESERVED3[24U]; __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ uint32_t RESERVED4[56U]; __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ uint32_t RESERVED5[644U]; __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ } NVIC_Type;
对于每个中断怎么设置优先级?
中断优先级控制的寄存器组:IP[240],全称是:Interrupt Priority Registers
240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F40x系列一共82个可屏蔽中断,使用IP[81]~IP[0]。
每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。
在stm32f7xx_hal_cortex.c可找到:
/** * @brief Sets the priority of an interrupt. * @param IRQn: External interrupt number. * This parameter can be an enumerator of IRQn_Type enumeration * (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f7xxxx.h)) * @param PreemptPriority: The preemption priority for the IRQn channel. * This parameter can be a value between 0 and 15 * A lower priority value indicates a higher priority * @param SubPriority: the subpriority level for the IRQ channel. * This parameter can be a value between 0 and 15 * A lower priority value indicates a higher priority. * @retval None */ void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority) { uint32_t prioritygroup = 0x00; /* Check the parameters */ assert_param(IS_NVIC_SUB_PRIORITY(SubPriority)); assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority)); prioritygroup = NVIC_GetPriorityGrouping(); NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority)); }
中断优先级设置步骤:
- 系统运行后在HAL_Init函数中设置中断优先级分组。调用函数:
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
//中断优先级分组2 整个系统执行过程中,只设置一次中断分组。 - 针对每个中断,设置对应的抢占优先级和响应优先级:
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t
PreemptPriority, uint32_t SubPriority); - 使能中断通道:
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn) { /* Check the parameters */ assert_param(IS_NVIC_DEVICE_IRQ(IRQn)); /* Enable interrupt */ NVIC_EnableIRQ(IRQn); }
更多相关内容 - 系统运行后在HAL_Init函数中设置中断优先级分组。调用函数:
-
STM32复习笔记(三)端口复用、映射和中断优先级
2018-09-10 13:37:17声明:本篇文章只是个人知识盲区、知识弱点、重点部分的归纳总结,望各位大佬不喜勿喷。梳理顺序是按照正点原子的视频和文档...目前内容:端口复用、映射和中断优先级 一、端口复用: STM32有很多的内置外设...声明:本篇文章只是个人知识盲区、知识弱点、重点部分的归纳总结,望各位大佬不喜勿喷。梳理顺序是按照正点原子的视频和文档的实际顺序梳理,转载请注明出处。
作者:sumjess
适用:这个视频我已经看过3遍了,总会有忘记的,所以来写这本书的随手笔记,记录重点、易忘点。该博客可以当做字典,也可以当做笔记。
目前内容:端口复用、映射和中断优先级
一、端口复用:
STM32有很多的内置外设,这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。
例如串口1 的发送接收引脚是PA9,PA10,当我们把PA9,PA10不用作GPIO,而用做复用功能串口1的发送接收引脚的时候,叫端口复用。
-----------------------------------------端口复用配置过程------------------------------------------
端口复用配置过程:
以PA9,PA10配置为串口1为例
GPIO端口时钟使能。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
复用外设时钟使能。
比如你要将端口PA9,PA10复用为串口,所以要使能串口时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
端口模式配置。 GPIO_Init()函数。
PA9,PA10复用为串口1配置过程
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//①IO时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//②外设时钟使能
//③初始化IO为对应的模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 PA.10 浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
二、端口重映射:
1、什么是端口重映射?
每个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在STM32中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。
为了使不同器件封装的外设IO功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上。STM32中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。
2、部分重映射 & 完全重映射
部分重映射:功能外设的部分引脚重新映射,还有一部
分引脚是原来的默认引脚。
完全重映射:功能外设的所有引脚都重新映射。
3、引脚重映射配置过程(串口1为例):
① 使能GPIO时钟(重映射后的IO);
② 使能功能外设时钟(例如串口1);
③ 使能AFIO时钟。重映射必须使能AFIO时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
① 开启重映射。
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
p 根据第一个参数,来确定是部分重映射还是全部重映射
4、哪些情况需要开启AFIO辅助功能时钟?
对寄存器AFIO_MAPR,AFIO_EXTICRX和AFIO_EVCR进行读写操作前,应当首先打开AFIO时钟。
AFIO_MAPR:配置复用功能重映射
AFIO_EXTICRX:配置外部中断线映射
AFIO_EVCR: 配置EVENTOUT事件输出
三、NVIC中断优先级管理:
1、NVIC中断优先级分组:
首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。
分组配置是在寄存器SCB->AIRCR中配置:
组
AIRCR[10:8]
IP bit[7:4]分配情况
分配结果
0
111
0:4
0位抢占优先级,4位响应优先级
1
110
1:3
1位抢占优先级,3位响应优先级
2
101
2:2
2位抢占优先级,2位响应优先级
3
100
3:1
3位抢占优先级,1位响应优先级
4
011
4:0
4位抢占优先级,0位响应优先级
2、抢占优先级 & 响应优先级区别:
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
举例:
假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。 中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这3个中断的优先级顺序为:中断7>中断3>中断6。
特别说明:
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
3、中断优先级分组函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
4、中断优先级设置:
(1)中断设置相关寄存器
__IO uint8_t IP[240]; //中断优先级控制的寄存器组
__IO uint32_t ISER[8]; //中断使能寄存器组
__IO uint32_t ICER[8]; //中断失能寄存器组
__IO uint32_t ISPR[8]; //中断挂起寄存器组
__IO uint32_t ICPR[8]; //中断解挂寄存器组
__IO uint32_t IABR[8]; //中断激活标志位寄存器组
(2)MDK中NVIC寄存器结构体
typedef struct { __IO uint32_t ISER[8]; uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; uint32_t RESERVED4[56]; __IO uint8_t IP[240]; uint32_t RESERVED5[644]; __O uint32_t STIR; } NVIC_Type;
(3)对于每个中断怎么设置优先级?
中断优先级控制的寄存器组:IP[240]
全称是:Interrupt Priority Registers
240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。
每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
(4)中断使能寄存器组:ISER[8]
作用:用来使能中断
32位寄存器,每个位控制一个中断的使能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ISER[0]和ISER[1]。
ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59;
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
(5)中断失能寄存器组:ICER[8]
作用:用来失能中断
32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。
ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~59;
配置方法跟ISER一样。
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
(6)中断挂起控制寄存器组:ISPR[8]
作用:用来挂起中断
(7)中断解挂控制寄存器组:ICPR[8]
作用:用来解挂中断
static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);
static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn);
static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
(8)中断激活标志位寄存器组:IABR [8]
作用:只读,通过它可以知道当前在执行的中断是哪一个
如果对应位为1,说明该中断正在执行。
static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
(9)中断参数初始化函数
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
typedef struct { uint8_t NVIC_IRQChannel; //设置中断通道 uint8_t NVIC_IRQChannelPreemptionPriority;//设置响应优先级 uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级 FunctionalState NVIC_IRQChannelCmd; //使能/使能 } NVIC_InitTypeDef; NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
5、中断优先级设置步骤:
①系统运行后先设置中断优先级分组。调用函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
整个系统执行过程中,只设置一次中断分组。
②针对每个中断,设置对应的抢占优先级和响应优先级:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
③ 如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
-
STM32-NVIC中断优先级管理
2021-10-01 09:16:391 NVIC中断优先级分组 1.1NVIC的中断向量表 Cortex-M3和CM4内核都支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32F1并没有使用CM3内核的全部东西,...1 NVIC中断优先级分组
1.1 NVIC的中断向量表
Cortex-M3和CM4内核都支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32F1并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32F1有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个),本博文只介绍60个外部可屏蔽中断。STM32F4也同样没有使用CM4内核的全部东西,而是只用了它的一部分,STM32F40xx/STM32F41xx总共有92个中断,STM32F42xx/STM32F43xx则总共有96个中断。STM32F40xx/STM32F41xx的92个中断里面,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这82个可屏蔽中断。
注意一下:CM3和CM4的外部中断和STM32的外部中断不是一个概念。CM3和CM4的外部中断:除了内核异常之外的都是外部中断;STM32的外部中断:外部中断EXTI只有6个。其实,内核中断也叫内核异常,注意中断和异常的区别。- 中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。
- 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。
下面就简单介绍一下STM32F103系列的16个内核中断(异常)、60个中断:
在中断向量表中从优先级7-66(中断号从0-59)代表着STM32F103的60个中断。优先级号越小,优先级越高。当表中的某处异常或中断被触发,程序计数器指针(PC)将跳转到该异常或中断的地址处执行,该地址处存放这一条跳转指令,跳转到该异常或中断的服务函数处执行相应的功能。因此,异常和中断向量表只能用汇编语言编写。
在MDK中,有标准的异常和中断向量表文件可以使用(startup_stm32f10x_hd.s),在其中标明了中断处理函数的名称,不能随意定义。而中断通道NVIC_IRQChannel(即IRQn_Type类型)是在stm32f10x.h文件中进行了宏定义。
什么是NVIC?即嵌套向量中断控制器(Nested Vectored Interrupt Controller)。CM3的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,中断向量表中60个中断都由它来处理。NVIC是Cortex-M3核心的一部分,关于它的资料不在《STM32的技术参考手册》中,应查阅ARM公司的《Cortex-M3技术参考手册》。Cortex-M3的向量中断统一由NVIC管理。
NVIC的核心功能是中断优先级分组、中断优先级的配置、读中断请求标志、清除中断请求标志、使能中断、清除中断等,它控制着STM32中断向量表中中断号为0-59的60个中断!!外部中断信号从核外发出,信号最终要传递到NVIC(嵌套向量中断控制器)。NVIC跟内核紧密耦合,它控制着整个芯片中断的相关功能。1.2 中断优先级分组寄存器
这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的,在文档《Cortex-M3权威指南(中文)》中能查找到。
分组配置是在寄存器SCB->AIRCR中配置,具体的分配关系如下所示:
组
AIRCR[10:8]
IP bit[7:4]分配情况
分配结果
0
111
0:4
0位抢占优先级,4位响应优先级
1
110
1:3
1位抢占优先级,3位响应优先级
2
101
2:2
2位抢占优先级,2位响应优先级
3
100
3:1
3位抢占优先级,1位响应优先级
4
011
4:0
4位抢占优先级,0位响应优先级
STM32有16个级别(4-bit)优先级可使用:
其中AIRCR寄存器来确定是用哪种分组,IP寄存器是相对应于那种分组抢占优先级和响应优先级的分配比例。例如组设置成3,那么此时所有的60个中断优先寄存器高4位中的最高3位是抢占优先级,低1位为响应优先级。CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit。
抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高。
介绍一下抢占优先级、响应优先级的区别:
1)高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
2)抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
3)抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
4)如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
除此之外有两点需要注意:
1)打断的情况只会与抢占优先级有关, 和响应优先级无关!
2)一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
优先级举例说明:假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这3个中断的优先级顺序为:中断7>中断3>中断6。1.3 中断优先级分组库函数
CM3核的优先级分组方式,使用的设置函数NVIC_SetPriorityGrouping()。接下来介绍STM32的中断优先级分组函数NVIC_PriorityGroupConfig(),用来进行中断分组设置的,此函数是在固件库下misc.c文件中(文件目录是:STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src\misc.c):
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { /* 检查参数 */ assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); /* 根据“NVIC_PriorityGroup”的值设置PRIGROUP[10:8]位 */ SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }
函数的参数的取值,是在同文件中进行宏定义的:
#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 */
2 中断优先级设置
2.1 中断优先级设置寄存器
分组设置好了之后,怎么设置单个中断的抢占优先级和响应优先级?
MDK为与NVIC相关的寄存器定义了如下的结构体,控制着中断向量表中60个中断(由于与中断内核有关,定义在core_cm3.h文件中):/* cortex-m3内核分组方式(8组)结构体表达方式:*/ typedef struct { __IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */ uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */ uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */ uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */ uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */ uint32_t RESERVED4[56]; __IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */ uint32_t RESERVED5[644]; __O uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */ } NVIC_Type; /* STM32分组(5组)方式结构体表达方式*/ /******************************* typedef struct { vu32 ISER[2]; u32 RESERVED0[30]; vu32 ICER[2]; u32 RSERVED1[30]; vu32 ISPR[2]; u32 RESERVED2[30]; vu32 ICPR[2]; u32 RESERVED3[30]; vu32 IABR[2]; u32 RESERVED4[62]; vu32 IPR[15]; } NVIC_TypeDef; *******************************/
我们依次介绍一下这些寄存器:
先介绍几个寄存器组长度为8,这些寄存器是32位寄存器。由于STM32只有60个可屏蔽中断,8个32位寄存器中只需要2个就有64位了,每1位控制一个中断。
1)ISER[8](Interrupt Set-Enable Registers):中断使能寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。其中只使用到了ISER[0]和ISER[1],ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59。要使能某个中断,就必须设置相应的ISER位为1,使该中断被使能(这仅仅是使能,还要配合中断分组、屏蔽、I/O口映射等设置才算完整)。具体每一位对应哪个中断参考stm32f103x.h里面第140行。
2)ICER[8](Interrupt Clear-Enable Registers):中断移除寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。该寄存器的作用于ISER相反。这里专门设置一个ICER来清除中断位,而不是向ISER位写0,是因为NVIC的寄存器写1有效,写0无效。
3)ISPR[8](Interrupt Set-Pending Registers):中断挂起控制寄存器--static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);。通过置1可以将正在进行的中断挂起,执行同级或者更高级别的中断。写0无效。
4)ICPR[8](Interrupt Clear-Pending Registers):中断解挂控制寄存器--static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn);。通过置1可以将正在挂起的中断解挂。写0无效。
5)IABR[8](Interrupt Active-Bit Registers):中断激活标志位寄存器--static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn);。这是一个只读寄存器,可以知道当前在执行的中断是哪一个(为1),在中断执行完后硬件自动清零。
最后,介绍一个寄存器组长度为240,这个寄存器为8位寄存器。240个8位寄存器,每个中断使用一个寄存器来确定优先级。由于CM3由240个外部中断,所以这个寄存器组的数目就是240(注意与上面寄存器的区别,一个是一个寄存器控制一个,一个是一位控制一个)。
1)IP[240](Interrupt Priority Registers):中断优先级控制的寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。这是用来控制每个中断的优先级。由于STM32F10x系列一共60个可屏蔽中断,故使用IP[59]~IP[0]。其中每个IP寄存器的高4位[7:4]用来设置抢占和响应优先级(根据分组),低4位没有用到。而两个优先级各占几个位又要由上面讲到的中断优先级分组决定。如何理解中断挂起与解挂的含义?
中断的挂起与解挂针对的是中断标志位,也可以叫做允许中断发生的位。
针对于中断标志位的操作有什么用?
1)一般来说单片机的中断发生有两个条件,一是中断标志位置位,二是中断允许,如果这两个条件都满足则进入中断,因为正常情况下中断一直是允许的,那么只能通过标志位来区分是否有中断挂起;
2)如果进入中断不清除标志位,那么这一中断服务程序结束后由于标志位还是置位的并且中断是允许的,那么还会再次进入该中断,就会发生一直在执行中断程序的情况。
举例说明:在A中断中设置B中断的中断标志位为1,那么当B中断条件满足时,B中断可以发生,但是如果清除A中B中断标志位,那么无论是否满足触发B中断的条件,A中断中永远不可能执行B中断,因为中断发生的条件是:“中断标志位有效+中断条件满足”。2.2 中断优先级设置库函数
接下来介绍如何使用库函数实现中断优先级管理,这里使用NVIC_Init()函数来进行对每个中断优先级的设置(misc.c文件中):
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) { uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F; /* 检查参数 */ assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd)); assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority)); assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority)); if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE) { /* 计算相应的IRQ优先级 --------------------------------*/ tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08; tmppre = (0x4 - tmppriority); tmpsub = tmpsub >> tmppriority; tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre; tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub; tmppriority = tmppriority << 0x04; NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority; /* 启用已选IRQ通道 --------------------------------------*/ NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); } else { /* 禁用已选IRQ通道 -------------------------------------*/ NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); } }
其中,NVIC_InitTypeDef为一个结构体,它的成员变量为:
typedef struct { uint8_t NVIC_IRQChannel; /*!< 指定要启用或禁用的IRQ通道。该参数可以为@ref IRQn_Type (有关完整的STM32设备IRQ通道列表,请参阅stm32f10x.h文件) */ uint8_t NVIC_IRQChannelPreemptionPriority; /*!< 指定NVIC_IRQChannel中指定的IRQ通道的抢占优先级。取值范围为0 ~ 15,如@ref NVIC_Priority_Table所示 */ uint8_t NVIC_IRQChannelSubPriority; /*!< 指定NVIC_IRQChannel中指定的IRQ通道的子优先级。取值范围为0 ~ 15,如@ref NVIC_Priority_Table所示 */ FunctionalState NVIC_IRQChannelCmd; /*!< 指定是否启用或禁用NVIC_IRQChannel中定义的IRQ通道。可以设置为“启用”或“禁用” */ } NVIC_InitTypeDef;
NVIC_InitTypeDef结构体有4个成员变量:
1) NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn;
2) NVIC_IRQChannelPreemptionPriority:定义此中断的抢占优先级别,具体的值要根据优先级分组来确定,具体参考表格 上图优先级分组真值表 ;
3) NVIC_IRQChannelSubPriority:定义此中断的响应优先级别,具体的值要根据优先级分组来确定,具体参考表格 上图优先级分组真值表 ;
4) NVIC_IRQChannelCmd:该中断是否使能,操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。NVIC_IRQChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。IRQn_Type 结构体定义:
typedef enum IRQn { /****** Cortex-M3 Processor Exceptions Numbers ***************************************************/ NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable 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 */ #ifdef STM32F10X_HD 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 */ RTCAlarm_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 */ #endif /* STM32F10X_HD */ }IRQn_Type;
其实我们看NVIC_Init()函数内部使能中断,也是通过ISER寄存器配置的。这与我么之前的内容并不矛盾。函数内部使用NVIC->ISER,而NVIC是core_cm3.h的一个宏定义:
#define NVIC_BASE (SCS_BASE + 0x0100) /*!< NVIC Base Address */ #define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */
也就是直接操作结构体来实现操作ISER寄存器。具体原理可以看STM32-寄存器地址名称映射分析。
比如,使能串口1中断,抢占优先级为1,响应优先级为2,初始化的方法为:NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ; // 抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; // 子优先级位2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
3 总结与分析
最后总结一下中断优先级设置的步骤:
1)系统运行后先设置中断优先级分组。调用函数:void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
整个系统执行过程中,只设置一次中断分组;
2)针对每个中断,设置对应的抢占优先级和响应优先级:void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
3)如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
注意:在启动文件startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
-
【STM32】NVIC 中断优先级管理,抢占优先级,响应优先级,中断寄存器
2021-09-15 12:08:22main.c /* *工程模板(寄存器版本) */ #include "stm32f10x.h" /** * 主函数 */ int main(void) ... // 开启GPIOB 端口时钟 ... // 配置PB0为通用推挽输出,速度为10M GPIOB_CRL |= (1<<4*0); // P.抢占优先级和响应优先级
STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占
抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由
NVIC_IRQChannelPreemptionPriority
的参数配置。响应
响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断, 响应属性由
NVIC_IRQChannelSubPriority
参数配置。例如,现在有三个中断向量,如下表
若内核正在执行C 的中断服务函数,则它能被抢占优先级更高的中断A 打断,由于B 和C 的抢占优先级相同,所以C 不能被B 打断。但如果B 和C 中断是同时到达的,内核就会首先响应响应优先级别更高的B 中断(高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的,而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断)。
NVIC 的优先级组
在配置优先级的时候,还要注意一个很重要的问题,即中断种类的数量。
NVIC 只可以配置16 种中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个4 位的数字来决定,把这个4 位数字的位数分配成抢占优先级部分和响应优先级部分。
有5 组分配方式:
- 第 0 组: 所有4 位用来配置响应优先级。即16 种中断向量具有都不相同的响应优先级。
- 第 1 组:最高1 位用来配置抢占优先级,低3 位用来配置响应优先级。表示有2 种级别的抢占优先级(0 级,1 级),有8 种响应优先级,即在16 种中断向量之中,有 8 种中断,其抢占优先级都为0 级,而它们的响应优先级分别为0~7,其余8 种中断向量的抢占优先级则都为1 级,响应优先级别分别为0~7。
- 第 2 组:2 位用来配置抢占优先级,2 位用来配置响应优先级。即4 种抢占优先级,4 种响应优先级。
- 第 3 组:高3 位用来配置抢占优先级,最低1 位用来配置响应优先级。即有8 种抢占优先级,2 种响应2 优先级。
- 第 4 组:所有4 位用来配置抢占优先级,即NVIC 配置的16 种中断向量都是只有抢占属性,没有响应属性。
要配置这些优先级组,可以采用库函数
NVIC_PriorityGroupConfig()
,可输入的参数为NVIC_PriorityGroup_0 ~ NVIC_PriorityGroup_4
,分别为以上介绍的5 种分配组。于是,有读者觉得疑惑了,如此强大的STM32,所有GPIO都能够配置成外部中断,USART、ADC 等外设也有中断,而NVIC 只能配置16 种中断向量,那么在某个工程中使用超过16 个中断怎么办呢?注意NVIC 能配置的是16 种中断向量,而不是16 个,当工程中有超过16 个中断向量时,必然有两个以上的中断向量是使用相同的中断种类,而具有相同中断种类的中断向量不能互相嵌套。
中断寄存器
ISER[8]
全称 Interrupt Set-Enable Registers,是一个中断使能寄存器组。
CM3 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。
但是 STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]), 总共可以表示 64 个中断。
而 STM32F103 只用了其中的前 60 位。ISER[0]的 bit0~bit31 分别对应中断 0~31。ISER[1]的 bit0~27 对应中断 32~59;这样总共 60 个中断就分别对应上了。
你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中 断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。具体每一位对应哪个中断,请参考 stm32f10x.h 里面的第 140 行处(针对编译器 MDK5 来说)。
ICER[8]
全称 Interrupt Clear-Enable Registers,是一个中断除能寄存器组。
该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和 ICRT 一样。
这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄 存器都是写 1 有效的,写 0 是无效的。具体为什么这么设计,请看《CM3 权威指南》第 125 页, NVIC 概览一章。
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[240]
全称 Interrupt Priority Registers,是一个中断优先级控制的寄存器组。
这个寄存器组相当重要!STM32 的中断分组与这个寄存器组密切相关。
IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。
而 STM32 只用到了其中的前 60 个。IP[59]~IP[0]分别对应中断 59~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。
STM32 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体分配关系如下表:
组 AIRCR[10:8] bit[7:4]分配情况 分配结果 0 111 0: 4 0 位抢占优先级,4 位响应优先级 1 110 1: 3 1 位抢占优先级,3 位响应优先级 2 101 2: 2 2 位抢占优先级,2 位响应优先级 3 100 3: 1 3 位抢占优先级,1 位响应优先级 4 011 4: 0 4 位抢占优先级,0 位响应优先级 例如,组设置为 3,那么此时所有的 60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是响应优先级。这样每个中断,你就可以设置抢占优先级位 0~7(因为抢占优先级有 3 位,最高可表示为 000=0~111=7),响应优先级(只有 1 位因此)为 1 或 0。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。
这里需要注意两点:
第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的,而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。结合实例说明一下:
假定设置中断优先级组为 2,然后设置中断 3(RTC 中断)的抢占优先级为 2,响应优先级为 1。中断 6(外部中断 0)的抢占优先级为 3,响应优先级为 0。中断 7(外部中断 1)的抢占优先级为 2,响应优先级为 0。那么这 3 个中断的优先级顺序为:中断 7>中断 3>中断 6。
上面例子中的中断 3 和中断 7 都可以打断中断 6 的中断。而中断 7 和中断 3 却不可以相互打断!
接下来我们介绍如何使用库函数实现以上中断分组设置以及中断优先级管理,使得我们以后的中断设置简单化。NVIC 中断管理函数主要在 misc.c 文件里面。
中断优先级分组函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //中断优先级分组函数
这个函数唯一目的就是通过设置 SCB->AIRCR 寄存器来设置中断优先级分组
比如我们设置整个系统的中断优先级分组值为 2,那么方法是:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
中断初始化函数
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); //中断初始化函数
其中
NVIC_InitTypeDef
是一个结构体,中间有三个成员变量,这三个成员变量的作用是:NVIC_IRQChannel
:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如 USART1_IRQn。NVIC_IRQChannelPreemptionPriority
:定义这个中断的抢占优先级别。NVIC_IRQChannelSubPriority
:定义这个中断的子优先级别。NVIC_IRQChannelCmd
:该中断是否使能。
比如我们要使能串口 1 的中断,同时设置抢占优先级为 1,子优先级位 2,初始化的方法是:
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // 串口 1 中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 ; // 抢占优先级为 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; // 子优先级位 2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ 通道使能 NVIC_Init(&NVIC_InitStructure); // 根据上面指定的参数初始化 NVIC 寄存器
中断优先级设置步骤总结
- 系统运行开始的时候设置中断分组。确定组号,也就是确定抢占优先级和子优先级的 分配位数。调用函数为
NVIC_PriorityGroupConfig();
- 设置所用到的中断的中断优先级别。对每个中断调用函数为
NVIC_Init();
Ref.
-
NVIC中断优先级管理示例解析
2020-08-07 10:25:16NVIC中断优先级管理 STM32中断优先级简介 如何管理中断? 什么是“抢占优先级”和“响应优先级”呢? 优先级是如何定义的? 特别说明 函数介绍 中断优先级分组选择函数 对于每个中断怎么设置优先级? 如何... -
STM32中断优先级的管理(NVIC)
2020-12-04 20:43:53STM32 NVIC 中断优先级管理 CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32 有 84 ... -
无基础也可以了解,NVIC中断优先级分组
2021-06-02 00:31:27STM32可以对中断进行分组,可以分为5组。 -
STM32中断优先级NVIC配置说明
2021-03-26 16:48:19STM32中断优先级NVIC配置说明 NVIC中断优先级分组列表 优先级分组抢占优先级响应优先级高4bit分配情况 NVIC_PriorityGroup_0 取值:0取值:0~150bit抢占优先级、4bit响应优先级 NVIC_... -
中断优先级分组和抢占式优先级和响应优先级
2019-05-15 21:42:54STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作’亚优先级’或’副优先级’,每个中断源都需要被指定这两种优先级。 具有高抢占式优先级的中断可以在具有低抢占式优先级... -
STM32H743xx——中断优先级说明
2019-09-26 09:25:15STM32H7的中断优先级说明 STM32H743 将中断分为 5 个组,组 0~4。 该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系下表所示: 组 AIRCR[10:8] bit[7:4]分配情况 分配... -
STM32的中断优先级说明(抢占式与响应式优先级)
2018-03-28 22:18:24注:查看寄存器请看《ARM Cortex-M4权威指南...STM32F4 的中断分组:STM32F4 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。如下图:通过这个表,我们就可以清楚的看... -
NVIC中断优先级管理
2017-05-03 20:27:461、参考资料 ...2、NVCI中断优先级分组 ·CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。 ·STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。 -
2021-04-16 NVIC中断优先级管理
2021-04-16 10:38:58NVIC中断优先级设置 NVIC总结 CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。 STM32 有 84... -
2022-3-5 stm32 NVIC中断优先级管理-学习笔记
2022-03-05 14:07:57STM32 有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。 而我们常用的就是这 68 个可屏蔽中断,但是 STM32 的 68 个可屏蔽中断,在 STM32F103 系列 上面,又只有 60 个(在 ... -
STM32中断优先级NVIC
2021-05-21 21:06:30CM3(Cortex M3)内核支持256个中断,其中包括16个内核中断和240个外部中断,并且具有256级的可编程中断设置 STM32并没有使用CM3内核的全部东西,而是只用了它的一部分 STM32有84个中断,包括16个内核中断和68个可... -
stm32中断优先级
2017-09-29 11:18:26CM3 内核支持 256 个中断,STM32 并没有使用 CM3 内核的全部只用了它的一部分。 STM32 有 84 个中断,包括 16 个内核中断和...与 NVIC 相关的寄存器中IPR[15]:是一个中断优先级控制的寄存器组由15个32bit的寄存器组成. -
Stm32学习(五)NVIC中断优先级
2020-07-11 16:45:511.NVIC中断优先级分组 CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。 STM32并没有使用CM3内核的全部东西,而是只用了它的一部分 STM32有84个中断,包括16个内核... -
STM32学习系列之中断优先级
2016-11-15 15:45:00同时,对每个中断设置一个抢占优先级和一个响应优先级值。 这里写图片描述 抢占优先级&响应优先级 1.高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。 2.抢占优先级相同的中断,高响应优先级不... -
ARM中断优先级之理解
2019-10-09 18:01:51单片机的中断分为抢占优先级和响应优先级(子优先级),中断时首先由抢占优先级决定产生哪个中断。 抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。 高的抢占式优先级可以打断低的抢占式... -
STM32的中断优先级NVIC
2021-12-03 10:23:023、STM32有84个中断,包括了16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。 4、STM32F103系列上面,又只有60个可屏蔽中断(在107系列才有68个) 5、中断管理方法:首先,对STM32中断进行分组,组0~4... -
第九章:STM32 对于NVIC中断优先级的设置
2017-12-26 16:21:28NVIC中断优先级管理: 首先是中断分组,我们知道所用的CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。然而STM32并没有使用CM3内核的全部东西,而是只用了它的一... -
【STM32】中断优先级管理
2020-08-23 16:45:03CM4 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有256 级的可编程中断设置。但 STM32F4 并没有使用 CM4 内核的全部东西,而是只用了它的一部分。 STM32F40xx/STM32F41xx 总共有 92 个... -
NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例
2015-11-11 19:19:58没写博客的感悟:昨天没有写博客,...今天主要总结和复习三个知识点,NVIC中断优先级管理、串口通信、及串口通信的简单配置实例: 第一部分: NVIC中断优先级管理: 首先是中断分组,我们知道所用的CM3内核支持 -
STM32----NVIC中断优先级详解
2020-10-12 20:01:33STM32----NVIC中断优先级 之前在学习和应用STM32时总是有疑惑,总是感觉优先级分组有点混乱,而且在排序中断优先级时没有一定的头绪,在疫情期间对这一方面的知识进行了深入的了解,下面将我学到的一些知识分享给... -
关于嵌入式学习随笔->6《NVIC中断优先级管理》
2019-02-25 23:16:00当中断请求源发出中断请求时,如果中断请求得到允许,单片机暂时中止当前正在执行的主程序,转到中断服务处理程序处理中断请求,处理完中断服务请求后,再回到原来被中止的程序处(断点),继续执行被中断的... -
STM32 中断优先级
2020-02-13 20:33:371.ARM cortex_m3 内核支持 256 个中断(16 个内核+240 外部)和可编程 256 级中断优先级 的设置,与其相关的中断控制和中断优先级控制寄存器(NVIC、SYSTICK 等)也都属于 cortex_m3 内核的部分。STM32 采用了 ... -
21.NVIC中断优先级管理
2022-02-23 16:25:05NVIC中断优先级管理 -
STM32的抢占优先级与子优先级
2020-07-08 15:06:31STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。 抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被...