对于中断这个概念,我喜欢用经常发生在CPU中的多线程抢占执行现象来类比,每个中断(或者说中断服务)就像是一个线程,它们根据事先定义好的运行规则运作着,所以这就引出了如何管理中断,我们应如何确定哪个中断先执行,哪个后执行。
1.NVIC中断优先级分
CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。
STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。
STM32F103系列上面,又只有60个可屏蔽中断(在107系列才有68个)
中断管理方法:
首先,对STM32中断进行分组,可以定义为组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。
分组配置是在寄存器SCB->AIRCR中配置:
看到这肯定还是蒙圈的,下面先解释什么是抢占优先级,什么是响应优先级
总结来说就是:首先比较抢占优先级,值越低优先级越高,高抢占优先级可以打断低抢占优先级,只有当抢占优先级一样时才会比较响应优先级,而且响应优先级不存在打断现象,它的作用是在两个抢占优先级一致的中断同时发生时确定谁先执行,即响应优先级高的先执行
总的来说,我们能从表格中获取的信息有:
1.AIRCR寄存器的位8到10能设置编号0到4的分组,该寄存器的位4到位7设置了抢占优先级跟响应优先级的位数,毋庸置疑,位数越多,表示的范围越大,也就是区分度越高。这个两个优先级的作用就如图片和加粗文字部分所描述的一样。
2.如何设置中断优先级分组
–相关库函数(在官方固件库包的misc.c文件中找):
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);//设置为分组2
3.设置分组后就要确定中断的通道,优先级等信息
–相关寄存器:
__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]; //中断激活标志位寄存器组
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;
RESERVED代表没有用到。
4.如何设置某个中断
+++(1.)优先级
中断优先级控制的寄存器组:IP[240] //这是一个寄存器组,就像是一个数组一样,每个位代表一个IP寄存器
全称是:Interrupt Priority Registers
240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。
每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。
相关函数:void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
+++(2.)使能
中断使能寄存器组:ISER[8] //这是一个寄存器组,就像是一个数组一样,每个位代表一个ISER寄存器
这是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);
+++(3.)失能
中断失能寄存器组: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);
+++(4.)挂起,解挂,就像是多线程一样
中断挂起控制寄存器组:ISPR[8]
作用:用来挂起中断
这也是32位寄存器,每个位操作一个中断
中断解挂控制寄存器组:ICPR[8]
作用:用来解挂中断
同样也是32位的,每个位操作一个中断
相关库函数: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)
+++(5.)中断激活标志位
中断激活标志位寄存器组:IABR [8]
作用:只读,通过它可以知道当前在执行的中断是哪一个,如果对应位为1,说明该中断正在执行。
相关函数:static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
中断参数初始化设置举例:调用void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);函数,其中NVIC_InitTypeDef结构体被定义为:
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寄存器
调用NVIC_Init函数后就已经设置了中断的参数。但是并没有涉及挂起,解挂,读取中断激活标志位,因为这些操作对应于别的函数。
关于中断优先级管理的总结与步骤: