你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

NVIC中断优先级管理(中断向量表)

[复制链接]
STMCU 发布时间:2020-9-14 12:57

Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个)。


注意一下:CM3的外部中断和STM32的外部中断不是一个概念。CM3:除了内核异常之外的都是外部中断;STM32:外部中断EXTI只有6个

其实,内核中断也叫内核异常。


  • 中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。
  • 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。


因此,下面就直接介绍一下STM32F103系列的16个内核中断(异常)、60个中断:


1.png



在中断向量表中从优先级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跟内核紧密耦合,它控制着整个芯片中断的相关功能。


STM32中断优先级分组中断优先级分组寄存器

这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的,在文档《Cortex-M3权威指南(中文)》中能查找到。


具体的分配关系如下所示:


AIRCR中断分组设置表


3.png


其中AIRCR寄存器来确定是用哪种分组,IP寄存器是相对应于那种分组抢占优先级和响应优先级的分配比例。例如组设置成3,那么此时所有的60个中断优先寄存器高4位中的最高3位是抢占优先级,低1位为响应优先级。CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit。


抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高。


介绍一下抢占优先级、响应优先级的区别:


  • 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
  • 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
  • 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
  • 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;



除此之外有两点需要注意:


  • 打断的情况只会与抢占优先级有关, 和响应优先级无关!
  • 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。



中断优先级分组库函数

CM3核的优先级分组方式,使用的设置函数NVIC_SetPriorityGrouping()。


接下来介绍STM32的中断优先级分组函数NVIC_PriorityGroupConfig(),用来进行中断分组设置的,此函数是在固件库下misc.c文件中(文件目录是:


STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src\misc.c):



  1. void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
  2. {
  3.   /* Check the parameters */
  4.   assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  5.   
  6.   /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  7.   SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
  8. }
复制代码

函数的参数的取值,是在同文件中进行宏定义的:


  1. #define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
  2.                                                             4 bits for subpriority */
  3. #define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
  4.                                                             3 bits for subpriority */
  5. #define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
  6.                                                             2 bits for subpriority */
  7. #define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
  8.                                                             1 bits for subpriority */
  9. #define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
  10.                                                             0 bits for subpriority */
复制代码


STM32中断优先级管理


中断优先级设置寄存器

分组设置好了之后,怎么设置单个中断的抢占优先级和响应优先级?


MDK为与NVIC相关的寄存器定义了如下的结构体,控制着中断向量表中60个中断(由于与中断内核有关,定义在core_cm3.h文件中):



  1. typedef struct
  2. {
  3.   __IO uint32_t ISER[8];                      /*!< Offset: 0x000  Interrupt Set Enable Register           */
  4.        uint32_t RESERVED0[24];                                   
  5.   __IO uint32_t ICER[8];                      /*!< Offset: 0x080  Interrupt Clear Enable Register         */
  6.        uint32_t RSERVED1[24];                                    
  7.   __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  Interrupt Set Pending Register          */
  8.        uint32_t RESERVED2[24];                                   
  9.   __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  Interrupt Clear Pending Register        */
  10.        uint32_t RESERVED3[24];                                   
  11.   __IO uint32_t IABR[8];                      /*!< Offset: 0x200  Interrupt Active bit Register           */
  12.        uint32_t RESERVED4[56];                                   
  13.   __IO uint8_t  IP[240];                      /*!< Offset: 0x300  Interrupt Priority Register (8Bit wide) */
  14.        uint32_t RESERVED5[644];                                 
  15.   __O  uint32_t STIR;                         /*!< Offset: 0xE00  Software Trigger Interrupt Register     */
  16. }  NVIC_Type;
复制代码


我们依次介绍一下这些寄存器:


先介绍几个寄存器组长度为8,这些寄存器是32位寄存器。由于STM32只有60个可屏蔽中断,8个32位寄存器中只需要2个就有64位了,每1位控制一个中断。


  • ISER[8](Interrupt Set-Enable Registers):中断使能寄存器。其中只使用到了ISER[0]和ISER[1],ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59。要使能某个中断,就必须设置相应的ISER位为1,使该中断被使能(这仅仅是使能,还要配合中断分组、屏蔽、I/O口映射等设置才算完整)。具体每一位对应哪个中断参考stm32f103x.h里面第140行。
  • ICER[8](Interrupt Clear-Enable Registers):中断移除寄存器。该寄存器的作用于ISER相反。这里专门设置一个ICER来清除中断位,而不是向ISER位写0,是因为NVIC的寄存器写1有效,写0无效。
  • ISPR[8](Interrupt Set-Pending Registers):中断挂起控制寄存器。通过置1可以将正在进行的中断挂起,执行同级或者更高级别的中断。写0无效。
  • ICPR[8](Interrupt Clear-Pending Registers):中断解挂控制寄存器。通过置1可以将正在挂起的中断解挂。写0无效。
  • IABR[8](Interrupt Active-Bit Registers):中断激活标志位寄存器。这是一个只读寄存器,可以知道当前在执行的中断是哪一个(为1),在中断执行完后硬件自动清零。


最后,介绍一个寄存器组长度为240,这个寄存器为8位寄存器。240个8位寄存器,每个中断使用一个寄存器来确定优先级。由于CM3由240个外部中断,所以这个寄存器组的数目就是240(注意与上面寄存器的区别,一个是一个寄存器控制一个,一个是一位控制一个)。


  • IP[240](Interrupt Priority Registers):中断优先级控制的寄存器。这是用来控制每个中断的优先级。由于STM32F10x系列一共60个可屏蔽中断,故使用IP[59]~IP[0]。其中每个IP寄存器的高4位[7:4]用来设置抢占和响应优先级(根据分组),低4位没有用到。而两个优先级各占几个位又要由上面讲到的中断优先级分组决定。



中断优先级设置库函数

接下来介绍如何使用库函数实现中断优先级管理,这里使用NVIC_Init()函数来进行对每个中断优先级的设置(misc.c文件中)



  1. void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
  2. {
  3.   uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
  4.   
  5.   /* Check the parameters */
  6.   assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
  7.   assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));  
  8.   assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
  9.    
  10.   if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
  11.   {
  12.     /* Compute the Corresponding IRQ Priority --------------------------------*/   
  13.     tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
  14.     tmppre = (0x4 - tmppriority);
  15.     tmpsub = tmpsub >> tmppriority;

  16.     tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
  17.     tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
  18.     tmppriority = tmppriority << 0x04;
  19.         
  20.     NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
  21.    
  22.     /* Enable the Selected IRQ Channels --------------------------------------*/
  23.     NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
  24.       (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  25.   }
  26.   else
  27.   {
  28.     /* Disable the Selected IRQ Channels -------------------------------------*/
  29.     NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
  30.       (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  31.   }
  32. }
复制代码

其中,NVIC_InitTypeDef为一个结构体,它的成员变量为:



  1. typedef struct
  2. {
  3.   uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
  4.                                                    This parameter can be a value of @ref IRQn_Type
  5.                                                    (For the complete STM32 Devices IRQ Channels list, please
  6.                                                     refer to stm32f10x.h file) */

  7.   uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
  8.                                                    specified in NVIC_IRQChannel. This parameter can be a value
  9.                                                    between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  10.   uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
  11.                                                    in NVIC_IRQChannel. This parameter can be a value
  12.                                                    between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  13.   FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
  14.                                                    will be enabled or disabled.
  15.                                                    This parameter can be set either to ENABLE or DISABLE */   
  16. } NVIC_InitTypeDef;
复制代码

NVIC_InitTypeDef结构体有4个成员变量:

  • NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn;
  • NVIC_IRQChannelPreemptionPriority:定义此中断的抢占优先级别;
  • NVIC_IRQChannelSubPriority:定义此中断的响应优先级别;
  • NVIC_IRQChannelCmd:该中断是否使能。



其实我们看NVIC_Init()函数内部使能中断,也是通过ISER寄存器配置的。这与我么之前的内容并不矛盾。函数内部使用NVIC->ISER,而NVIC是一个宏定义,

  1. #define NVIC     ((NVIC_Type *) NVIC_BASE)     /*!< NVIC configuration struct         */
复制代码

也就是直接操作结构体来实现操作ISER寄存器。

比如,使能串口1中断,抢占优先级为1,响应优先级为2,初始化的方法为:



  1. NVIC_InitTypeDef   NVIC_InitStructure;
  2. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
  3. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
  4. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
  5. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
  6. NVIC_Init(&NVIC_InitStructure);        //根据上面指定的参数初始化NVIC寄存器
复制代码


总结与分析

最后总结一下中断优先级设置的步骤:


1、系统运行后先设置中断优先级分组。调用函数:

  1. void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
复制代码

整个系统执行过程中,只设置一次中断分组;


2、针对每个中断,设置对应的抢占优先级和响应优先级:

  1. void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
复制代码

3、如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。



2.png
1 收藏 1 评论0 发布时间:2020-9-14 12:57

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版