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

发布者:京玩儿最新更新时间:2019-03-13 来源: eefocus关键字:STM32  NVIC  中断优先级管理  中断向量表 手机看文章 扫描二维码
随时随地手机看文章

STM32F1xx官方资料:

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


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个中断:



在中断向量表中从优先级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中断分组设置表

组 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位响应优先级


其中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):


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 */

 


STM32中断优先级管理

中断优先级设置寄存器

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


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寄存器。具体原理见【STM32】MDK中寄存器地址名称映射分析。


比如,使能串口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寄存器

 


总结与分析

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


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


void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

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


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


void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

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


关键字:STM32  NVIC  中断优先级管理  中断向量表 引用地址:【STM32】NVIC中断优先级管理(中断向量表)

上一篇:【STM32】SysTick滴答定时器(delay延时函数讲解)
下一篇:【STM32】STM32端口复用和重映射(AFIO辅助功能时钟)

推荐阅读最新更新时间:2024-03-16 16:25

STM32文字取模&图片取模
1.文字取模 2.图片取模 总结 这只是文字显示,图片显示的一种方式,可以说还是一种比较笨的方法!可以不用但是要知道。 我也是很久不用了,还卡了一些时间。 现在大家都是再用字符下载到W25Qxx中,或者直接用文件读写SD卡的方式打开图片。
[单片机]
<font color='red'>STM32</font>文字取模&图片取模
STM32学习013_SPI串行外设接口通信
SPI(Serial Periphreal Iterface-串行外设接口)总线系统是一种同步串行外设接口,使MCU与各种外围接口以串行方式进行通讯交换信息,SPI有三个寄存器,控制寄存器SPCR,状态寄存器SPSR,数据寄存器SPDR,外围设备包括FlashRam,网络控制器,LCD显示驱动器,AD转换器和MCU etc.接口一共用4条线,串行时钟线(SCLK),主机输入/从机输出数据线MISO,主机输出/从机输入数据线MOSI,和低电平有效的从机选择线NSS. SPI接口主要应用在EEPROM,flash 实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。 数据传输的过程:在主器件的移位脉冲下,数据按位传输,低位在
[单片机]
<font color='red'>STM32</font>学习013_SPI串行外设接口通信
STM32之USART(串口通信)
如果你看过《STM32的中文手册》,你会发现STM32的串口是非常强大的,不仅支持最基本的通用串口同步,异步通讯,还具有LAN总线的功能(局域互联网),IRDA功能(红外通讯),SmartCard功能 异步串口通讯协议: 这里介绍的是串口最基本,最常用的方式,全双工,异步通讯方式. 通过串口的通讯协议,我们知道要配置串口通讯,至少要配置几个参数: 字长(一次传送的数据长度); 波特率(每秒传输的数据位数); 奇偶校验位; 还有停止位; 当然,在我们的ST库里面,肯定有一个串口初始化结构体啦,这个结构体肯定有几个成员是来存储这些控制参数的!!!!! *串口线:* 要实现基本的全双工异步通讯,只要3条线,分别为Rx、Tx、和GND.
[单片机]
STM32的RS485调试过程记录
RS485是半双工,RS422是全双工。 A接A,B接B,不要交叉。 RS485标准是4根线,定义如下: RO: Receiver Output: If A B by 200mV, RO will be high;If A B by 200mV, RO will be low.2 /RE: 接收器输出使能。当RE为低电平时,RO有效;当RE为高电平时,RO为高阻状态。 RI: Driver Input. A low on DI forces output Y low and output Z high. Similarly, a high on DI forces output Y high and output Z low
[单片机]
STM32基于官方库函数的时钟配置
stm32可选的时钟源 在STM32中,可以用内部时钟,也可以用外部时钟,在要求进度高的应用场合最好用外部晶体震荡器,内部时钟存在一定的精度误差。 准确的来说有4个时钟源可以选分别是HSI、LSI、HSE、LSE(即内部高速,内部低速,外部高速,外部低速),高速时钟主要用于系统内核和总线上的外设时钟。低速时钟主要用于独立看门狗IWDG、实时时钟RTC。 ①、HSI是高速内部时钟,RC振荡器,频率为8MHz,上电后默认的系统时时钟 SYSCLK = 8MHz,Flash编程时钟。 ①、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。 ③、LSI是低速内部时钟,RC振荡器,频率为40kHz
[单片机]
<font color='red'>STM32</font>基于官方库函数的时钟配置
STM32 Nor Flash DFU
次要讲讲怎么实现Nor Flash的升级。 Nor Flash的DFU工程还是基于之前的flash DFU的工程上修改而来。工程的目录如下: STM32 Nor Flash DFU - ziye334 - ziye334的博客 我使用的Nor Flash芯片是M29W128F, 该芯片共有128Mb的空间,通过FSMC挂接在BANK0。 正好在UBS的官方程序里也有使用芯片的例子,所以也就是说管方的Nor Flash的驱动代码是使用 M29W128F这款芯片的。所以我们需要从拷贝fsmc_nor.c和fsmc_nor,h这两个文件添加到我们的USB_User这个组中。还要讲我们之前的flash_if文件修改为nor_if名。这
[单片机]
[STM32]STM32F407系列教程之二,gpio输入输出实验
一、实验准备 1.模板demo 原因呢,我在第一讲中已经说过,费尽千辛万苦搭建了一个模板,流过多少泪、费劲多少事,只有亲自搭建过的才会体会到(第一讲我只是讲了组成,后续有机会,我将带大家亲手搭建一个),搭建完成后,备份压缩(玩过Linux的大概有点感触,配置了一个新环境,就得做个镜像,哪天系统崩了,就可以重装系统,这里也是一样的)。 2.板级支持包 就是在我的工程文件中,所有以”Bsp_”开头的文件,位于BSP文件夹下: BSP文件夹下有两个文件夹:BINC、BSRC。一个存放h文件,一个存放c文件,是可以放到一个文件中,我只是为分类能够明显点。 PS:关于这种分类,我在其他文件夹中也有类似的设计,有兴趣可自行查找
[单片机]
[<font color='red'>STM32</font>]STM32F407系列教程之二,gpio输入输出实验
STM32如何收发float类型数据?
在之前文章里提到了共用体用来传输浮点数的用法,但那篇笔记中没有详细介绍,这篇笔记我们一起来看一看具体实例。 实际应用中,我们可能需要两个设备通过串口传输浮点数据: 本篇笔记为了方便演示,使用串口助手模拟其中一个设备,本篇笔记内容如下: 我们创建一个用于管理float类型数据的共用体: unionfloat_data { floatf_data; uint8_tbyte ; }; 数据的流向如: 本次使用串口助手模拟发送设备,省略了第一步,主要看第②、③步。 创建两个共用体变量,用于发送与接收: unionfloat_datarx_float_data,tx_float_data; 收发相关代码: 左
[单片机]
<font color='red'>STM32</font>如何收发float类型数据?
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved