STM32中断及其优先级
Posted __2017__
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32中断及其优先级相关的知识,希望对你有一定的参考价值。
STM32中断配置示例:
/* NVIC config */
void NVIC_config(void)
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
这里涉及异常(中断)相关的: 1、异常与中断 2、优先级分组,NVIC_PriorityGroupConfig 3、抢占优先级,NVIC_IRQChannelPreemptionPriority 4、子(亚)优先级,NVIC_IRQChannelSubPriority 5、中断号定义,NVIC_IRQChannel 6、优先级配置的寄存器实现,NVIC_Init()的实现 此篇对上述内容进行整理,以下内容参考资料《Cortex-M3权威指南》
一、Cortex-M3异常简介
CM3 在内核水平上搭载了一个异常响应系统,所有中断机制都由NVIC实现。CM3有256个预定义的异常(所有能打断正常执行流的事件都称为异常)类型 , 其中编号为 1 - 15 的对应系统异常,大于等于 16 的则全是外部中断。如下图示:虽然CM3 是支持240 个外中断的,但具体使用了多少个是由芯片生产商决定。比如:STM32 有84 个中断,包括16 个内核中断和68 个可屏蔽中断。不同系列支持的中断数也是不同的,需参考具体芯片datasheet。
二、异常优先级
除了个别异常(复位、NMI、硬fault)的优先级被定死外,其它异常的优先级都是可编程的。 在CM3 中,优先级对于异常来说很关键的,它会影响一个异常是否能被响应,以及何时可以响应。优先级的数值越小,则优先级越高。CM3 支持中断嵌套,使得高优先级异常会抢占(preempt)低优先级异常。CM3 允许的最少使用位数为3 个位(位序MSB),亦即至少要支持8 级优先级。举例来说,如果只使用了3 个位来表达优先级,则优先级配置寄存器的结构会如图所示:
在图中, [4:0] 没有被实现,所以读它们总是返回零,写它们则忽略写入的值。因此,对于3 个位的情况,我们能够使用的8 个优先级为:0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0 以及0xE0。 注:STM32使用3位,即[7:4]表示优先级,所以STM32具有16级优先级。
三、优先级分组:抢占优先级和子(亚)优先级
为了使抢占机能变得更可控,CM3 还把256 级优先级按位分成高低两段,分别是抢占优先级和子优先级。NVIC 中有一个寄存器是“应用程序中断及复位控制寄存器(AIRCR)”,它里面有一个位段名为“优先级组”:
该位段的值对每一个优先级可配置的异常都有影响,把其优先级分为位段: MSB 所在的位段(左边的)对应抢占优先级,而 LSB 所在的位段(右边的)对应子优先级,位数与分组位置的关系:如下图示:
比如,如果优先级分组为1,则抢占优先级有64级[7:2],子优先级有4级[1:0]。
在计算抢占优先级和子优先级的有效位数时,必须先求出下列值:
1、芯片实际使用了多少位来表达优先级
2、优先级组是如何划分的。
举个例子,如果只使用 3 个位来表达优先级( [7:5] ),并且优先级组的值是 5 (从比特 5 处分组),则你得到 4 级抢占优先级,且在每个抢占优先级的内部有 2 个子优先级,如图 所示:
四、中断寄存器组
-->NVIC = ((NVIC_Type *) 0xE000E100)
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;
ISER : Interrupt Set-Enable Registers ,中断使能寄存器组。 这里用了 2 个 32 位的寄存器,总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。 ISER[0] 的 bit0~bit31 分别对应中断 0~31, ISER[1] 的 bit0~27 对应中断 32~59 ;依次类推,这样总共 60 个中断就分别对应上了。要使能某个中断,必须设置相应的 ISER 位为 1。
ICER : Interrupt Clear-Enable Registers ,是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的 。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。通过这种方式,使能/除能中断时只需把“当事位”写成 1,其它的位可以全部为零。再也不用像以前那样,害怕有些位被写入 0 而破坏其对应的中 断设置(写 0 没有效果),从而实现每个中断都可以自顾地设置,而互不侵犯——只需单一 的写指令,不再需要读-改-写。 ISPR : Interrupt Set-Pending Registers ,是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1 ,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
ICPR : Interrupt Clear-Pending Registers ,是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1 ,可以将挂起的中断解挂。写 0 无效。
IABR : Active Bit Registers ,是一个中断激活标志位寄存器组。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。对应位所代表的中断和 ISER 一样,如果为 1 ,则表示该位所对应的中断正在被执行。
IPR : Interrupt Priority Registers ,是一个中断优先级控制的寄存器组。这个寄存器组相当重要! STM32 的中断分组与这个寄存器组密切相关。因为 STM32 的中断达 60 多个,所以 STM32 采用中断分组的办法来确定中断的优先级。 IPR 寄存器组由 240 个 8 bit 的寄存器组成,每个可屏蔽中断占用 8bit ,这样总共可以表示 240 个可屏蔽中断。
STIR:Software Trigger Interrupt Register,向软件触发中断寄存器STIR 的INTID字段([8:0])写入中断ID即可触发相应的中断。 Besides using NVIC-ISPR[n] registers, you can also use a Software Trigger Interrupt Register (NVIC->STIR) to trigger an interrupt using software:
For example, you can generate interrupt #3 by writing the following code in C:
NVIC->STIR = 3;
五、STM32中断实现分析
STM32 的中断分组: STM32 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如下图 表所示:通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系,例如组设置为 3 ,那么此时所有的 60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是
响应优先级。
1、中断分组设置
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
直接把分组数值写入AIRCR寄存器的[10:8]位即可,写该寄存器需要一个Key值,即AIRCR_VECTKEY_MASK。
2、指定中断线设置 有了上述大篇幅的知识加上注释,STM32中断寄存器配置也就一目了然:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
/* Compute the Corresponding IRQ Priority --------------------------------*/
tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700)) >> 0x08; /* 中断分组值 */
tmppre = (0x4 - tmppriority); /* 抢占优先级转化为[7:4]左移的位数 *
tmpsub = tmpsub >> tmppriority; /* 子优先级掩码 */
tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre; /* 抢占优先级,在[7:4]中的值 */
tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub; /* 子优先级 */
tmppriority = tmppriority << 0x04; /* 最终优先级,在[7:0]中的值 */
/* 中断号和其占用优先级寄存器一一对应 */
NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
/* [NVIC_IRQChannel >> 0x05]定位ISER数组的下标,一个寄存器对应32个中断
* [NVIC_IRQChannel & (uint8_t)0x1F)]定位ISER[x]的指定位
*/
NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
else
/* Disable the Selected IRQ Channels -------------------------------------*/
NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
以上是关于STM32中断及其优先级的主要内容,如果未能解决你的问题,请参考以下文章