STM32_基础入门(八)_NVIC中断优先级管理_nvic中断优先级分组_阿杰在线送代码的博客-程序员秘密

技术标签: stm32  STM32  arm  单片机  

持续关注阿杰在线更新保姆式笔记~~坚持日更

参考资料:《STM32中文参考手册V10》-第9章 中断和事件

目录

一、NVIC中断优先级分组

中断优先级分组寄存器

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

 中断优先级分组库函数

二、NVIC中断优先级设置

中断优先级设置寄存器

中断优先级设置库函数 

三、NVIC总结


 使用中断就得设置优先级

一、NVIC中断优先级分组

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

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

  • 中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。 
  • 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。
《STM32中文参考手册》9.1.2
下面就直接介绍一下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跟内核紧密耦合,它控制着整个芯片中断的相关功能
 

中断优先级分组寄存器

 几十个中断,怎么管理?

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

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

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

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

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

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

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

 中断优先级分组库函数

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

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


void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  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 */

二、NVIC中断优先级设置

中断优先级设置寄存器

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

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

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;   

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

先介绍几个寄存器组长度为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文件中):

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
  uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
  
  /* Check the parameters */
  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)
  {
    /* Compute the Corresponding IRQ Priority --------------------------------*/    
    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;
    
    /* Enable the Selected IRQ Channels --------------------------------------*/
    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);
  }
}

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


typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) */
 
  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */
 
  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */
 
  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

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

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

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

#define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */ 

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

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

NVIC_InitTypeDef   NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断--》stm32f10x.h167 IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);	//根据上面指定的参数初始化NVIC寄存器

三、NVIC总结

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

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

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//misc.h

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

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

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//misc.h

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_50546241/article/details/122756296

智能推荐

hdu2028 最小公倍数_洛克地的博客-程序员秘密

#include_int64 ans(_int64 a, _int64 b){ _int64 n = a*b; _int64 temp,r; if(a {  temp = a;  a = b;  b = temp; } while(b!=0) {  r = a%b;  a = b;  b = r; } return n/a;}

Oracle 19C 安装指引_chesterchai的博客-程序员秘密

Oracle 19C 安装指引18C新功能1.简化的基于镜像的Oracle数据库安装从18C开始,Oracle可以作为镜像文件来下载和安装,必须解压缩镜像文件到ORACLE_HOME目录,然后执行runInstaller安装。有关更多信息,请参阅关于基于映像的Oracle数据库安装2.基于RPM的Oracle数据库安装rpm -ivh 命令安装,可以自动完成安装前检查,将提取的软件包分配给预分配的用户和组,完成其他所有操作。有关更多信息,请参阅使用RPM软件包安装Oracle数据库3.只读O

MATLAB摄像头实时图像处理_matlab 摄像头_今天不飞了的博客-程序员秘密

之前由于双目测距的工作,用MATLAB获取摄像头数据并实时处理。现将一些基本操作分享给大家。欢迎交流

Mac小技巧之解除百度云盘限速_mac百度网盘限速解除 2023_十万个逗的博客-程序员秘密

背景:Mac上的百度云盘下载速度堪忧,本人用的Chrome浏览器。以下也是以Chrome为例。解决办法:1、安装arua2gui,下载Aria2GUI-vx.x.x.zip这个压缩包,并解压将Aria2GUI放到你想要的位置比如说应用程序里,此时只是安装了一个下载软件,如果需要网页网盘直接导出下载,需要安装以下两个软件2、YAAW-for-Chrome,如下图,点击Download ZIP下载YA...

java 读取mysql日志_如何在MySQL中查看日志文件?_解说柯基~~~的博客-程序员秘密

我已经读过Mysql服务器创建了一个日志文件,它记录了所有活动 - 比如何时执行查询和执行什么查询 .谁能告诉我我的系统中存在哪些内容?我怎么读呢?基本上,我需要使用不同的输入备份数据库[两个日期之间的备份]所以我想我需要在这里使用日志文件,这就是我想要这样做的原因......我认为必须以某种方式保护此日志,因为可能会记录用户名和密码等敏感信息[如果有任何查询需要];它可能是安全的,不容易被看到?...

爬虫str转json报错:json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)处理_将str中有非法字符转json中报错_风启新尘的博客-程序员秘密

首先他报错,百度了好久,试了各种办法,都不行,最后花了2小时的时间,被我百度出来了。我也不容易啊,如果有帮助就关注一下pip install demjsonresponse = requests.get(url=url, headers=headers).textwhh=demjson.encode(response, encoding='utf-8')hh=json.loads(whh)print(hh)有帮助的话,记得三联。...

随便推点

C++之编译预处理_PzLu的博客-程序员秘密

C++之编译预处理文章目录C++之编译预处理引言预处理指令1. #include 指令2. #define 指令和 #undef 指令3. 条件编译 指令预处理操作符defined 操作符例子总结引言想必学习C++的朋友们对这个是十分熟悉的了。一个 C++ 程序需要经过编译器编译成功后生成目标代码,再结合链接库,形成链接程序,最后经运行调试,才能得到正确的程序。这里讲得就是关于编译预处理...

2021年西式面点师(中级)考试及西式面点师(中级)考试题库_zd0618的博客-程序员秘密

题库来源:安全生产模拟考试一点通公众号小程序安全生产模拟考试一点通:西式面点师(中级)考试是安全生产模拟考试一点通总题库中生成的一套西式面点师(中级)考试题库,安全生产模拟考试一点通上西式面点师(中级)作业手机同步练习。2021年西式面点师(中级)考试及西式面点师(中级)考试题库1、【单选题】使用分步搅拌法调制蛋糕面糊,进炉烧烤时体积膨胀值大,在,而且组织松软细腻,但()。(C)A、损耗比较大B、成品风味不足C、搅拌较费事D、感官性质不良2、【单选题】冻禽在冷藏时被假...

人活着系列之芳姐和芳姐的猪(Floyd)_WY5421的博客-程序员秘密

Problem Description百年来,人活着是为了什么这个问题一直萦绕在人的脑海里,也一直困扰着人的思想。人活着就是活着了,为活着本身而活着,而不是为活着之外的任何事物而活着的。正因为活着,所以活着。对,是有点莫明其妙,但也是一句最受用的话。芳姐特别喜欢猪,所以,她特意养了n头猪,建了m个猪圈,顺便在m个猪圈间修了k条无向边,每条边有都有起点u,终点v,距离w。每头猪呆在一个特定的...

LIO-SAM ouster_libmetis.so_飞同学的博客-程序员秘密

1 ROS tested with Melodicsudo apt-get install -y ros-melodic-navigationsudo apt-get install -y ros-melodic-robot-localizationsudo apt-get install -y ros-melodic-robot-state-publisher2 gtsam (Georgia Tech Smoothing and Mapping library)wget -O ~/Downlo

asp.net文本编辑器的用法_hkf97的博客-程序员秘密

到官网下载最新版的KindEditor 4.11,解压文件后可得asp:与asp结合的示例代码asp.net:与asp.net结合的示例代码attached:上传文件的根目录,可在相关的代码中修改examples:功能演示的示例代码jsp:与jsp结合的示例代码lang:语言包php:与php结合的示例代码plugins:控件的功能代码的实现kindedit

eFuse技术_xinxulsq的博客-程序员秘密

1. 概念eFuse的概念最早来源于2004年IBM工程师的发现。该发现表明:与更旧的激光熔断技术相比,电子迁移(EM)特性可以用来生成小得多的熔丝结构。EM熔丝可以在芯片上编程,不论是在晶圆探测阶段还是在封装中。采用I/O电路的片上电压(通常为2.5V),一个持续200微秒的10毫安直流脉冲就足以编程单根熔丝。通过运用eFuse技术,允许计算机芯片的动态实时重新编程。抽象地说,计算机逻辑通常是“...

推荐文章

热门文章

相关标签