stm32学习-nvic的理解与使用

1、nvic的资料

Nested Vectored Interrupt Controller,全称嵌套向量中断控制器,见名知意,这是arm公司提供的中断控制的部件。

在线pdf文档

 

从pdf中可以看到nvic被归类为cortex-m4内核的外设,这个外设并非st公司参与设计,是arm公司设计的,所以在stm32参考手册里面nvic的资料写的非常少。

既然是外设就应该关注的是它的寄存器。

图1 中断寄存器在内存寻址的位置

nvic寄存器在寄存器统一编制中的位置是在最后一个块内存中。

下图为nvic在内核中的位置,由图可以看出nvic是一个处理器中的硬件,ARM公司将所有中断都认为是异常,所以中断和异常都交给nvic处理。注意到nvic将io端口与外设的中断分开了。

图2 nvic在内核中的位置

2、nvic的官方介绍

ARM的官方文档介绍了哪些nvic的内容:

4.2.1 通过使用CMSIS访问cortex-m4内核的nvic寄存器

CMSIS是ARM公司与多家不同的芯片和软件供应商一起紧密合作定义的,提供了内核与外设、实时操作系统和中间设备之间的通用接口。全称为Cortex Microcontroller Software Interface Standard,conrtex-m 软件接口标准。

这里告诉你可以调用哪些函数去控制寄存器。

4.2.2 中断使能寄存器

interrupt set-enable registers,该寄存器可以开启某个中断。

4.2.3 中断清除寄存器

interrupt clear-enable register,该寄存器可以清除某个中断。

4.2.4 中断挂起寄存器

interrupt set-pending registers ,将某个正在执行的中断挂起。

4.2.5 中断清除挂起寄存器

interrupt clear-pending registers ,将某个已经挂起的中断恢复。

4.2.6 中断活跃寄存器

interrupt active bit registers,该寄存器可以保存了每个中断当前是否活跃或活跃挂起的状态,可读。

4.2.7 中断优先级寄存器

interrupt priority registers,该寄存器配置每一个中断的优先级。

4.2.8 软件触发中断寄存器

software trigger interrupt register,往该寄存器写值可以用来触发某个中断,该寄存器仅有一个,在低9位写中断号就可以触发某个中断。

图3 软件中断触发寄存器

4.2.9 电平敏感和脉冲中断

Level-sensitive and pulse interrupts 暂时不知道干嘛的

4.2.10 nvic使用指南和技巧

3、寄存器的理解

在nvic介绍的开头

图4 nvic简介

 

其中提到范围可以是1-240个中断,这里的意思是这款芯片支持最多240个外部中断,soc厂商可以自行加外设进行设计,还有16个中断被这个内核所使用,所以0-15是内部中断,16-255是外部中断。这16个内部中断的声明如下所示。DCD指令用于申请一个空间按4字节对齐,在这个cortex-m4的芯片中初始化中断向量表在内存寻址的最低位置0x0000 0000,可见16个内部中断号也并没有用完,有些也作了保留。

在这里一定要清晰中断向量表和中断寄存器地址的区别,中断向量表保存在了4GB可寻址地址的最低为,中断寄存器保存在了4GB可寻址地址的尾部(也不是最高位可见图1和图6)。

图5 startup_stm32f40_41xxx.s中声明

 所以这款cortex-m4一共可以支持256个中断号,那么就需要8个32位寄存器来对这256个中断进行相应的操作,不过在stm32f407中外部中断只使用了82个,所以只需要操作前3个寄存器即可。32*3=96。在其他stm32f4的其他系列中可能用到更多或更少的中断号。但是要知道无论SOC厂商用多少个,ARM公司的cortex-m4的芯片最多能支持240个外部中断就可以了。

图6 寄存器功能表

 

有两个有点特殊的寄存器,中断优先级寄存器和软件触发中断寄存器,下面单独来说明。

4、两个特殊的寄存器

中断优先级寄存器和软件触发中断寄存器。

中断优先级寄存器

中断优先级寄存器有60个,先来看看中断优先级寄存器是如何为每个中断(256)配置优先级的。在这个芯片中每个中断的优先级用1个字节来配置,60个32位寄存器有240个字节,所以只能配置240个中断的优先级,这240个中断是之前提到的外部中断,那么还有16个内部中断的优先级如何配置,答案就在如下图。下图中有四个配置内部中断优先级的寄存器,

图7 配置内部中断优先级的寄存器
图8 SHPR1
图9 SHPR2
图10 SHPR3

 

 配置内部中断也不是16个都能配置优先级,内部中断也没有16个,有一些保留的编号,有一些中断是不能配置优先级的,下面是内部中断的表,这里有16个,中间的保留代表了4个。

图11 内部中断

还有一个剩余的问题,1个字节的优先级具体是如何分的呢?cortex-m4并没有简单将一个字节的数据标识优先级大小,而是把一个字节又继续划分成主优先级和次优先级,初始化一个中断的优先级时,要指出主优先级占多少位,如果主优先级占3位,那么次优先级占5位,在执行中断的时候,优先级的判断顺序是:主优先级->次优先级->中断编号。那么又引出一个问题,这个相当于子网掩码的主优先级前缀存在哪里?实际上并不需要存储这个前缀,在真正比较的时候,直接比较1个优先级字节的大小就可以了,这个主优先级和次优先级的概念只是逻辑上的,体现在代码上,在设置优先级时,首先将分组前缀PRIGROUP的代号写到Application Interrupt and Reset Control Register这个寄存器的[10:8]中,然后在真正设置中断优先级寄存器的值的时候,会去读取这个寄存器的值,作一些代码上的移位就编程了最后的优先级的值,再初始化到中断优先级寄存器中。

图12 优先级在cortex-m4中如何分组
图13 Application Interrupt and Reset Control 寄存器

 

 软件触发中断寄存器

这也是一个有一点特殊的寄存器,软件触发中断寄存器是作为这样的用途:将中断号写入软件触发中断寄存器中就可以触发某个中断。中断号只有256个占8位,所以写入低8位即可,剩下的保留。

图12 软件触发中断寄存器介绍

5、库函数

与nvic操作相关的库函数在misc.h与misc.c中进行声明与定义。

nvic的初始化结构体

相关数据与结构体:

typedef struct
{
  uint8_t NVIC_IRQChannel;                            //指定中断编号
  uint8_t NVIC_IRQChannelPreemptionPriority;          //指定中断主优先级的值
  uint8_t NVIC_IRQChannelSubPriority;                 //指定中断次优先级的值
  FunctionalState NVIC_IRQChannelCmd;                 //是否使能中断ENABLE或DISABLE
} NVIC_InitTypeDef;

 

图13 中断编号示意(stm32f4xx.h)

 调用示例:

NVIC_InitTypeDef NVIC_InitStructure;                        //开辟结构体空间
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;            //中断编号
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   //主优先级,配合前缀别越界
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          //次优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能

 

指定中断主优先级前缀长度

需要说明的是,stm32f407的库函数对cortex-m4内核提供的中断主优先级前缀并没有全部采用,只采用了0b011,0b100,0b101,0b110,0b111这5种中断主优先级前缀。

 

函数原型:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

相关数据与结构体:

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) //前缀长度0位
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) //前缀长度1位
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) //前缀长度2位
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) //前缀长度3位
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) //前缀长度4位

调用示例:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

nvic最后的初始化

在nvic结构体和前缀长度都设置好了以后,调用NVIC_Init函数进行写入寄存器对应的中断寄存器种。NVIC_Init由库函数提供,所在文件misc.c。

函数原型:

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)

相关数据与结构体:

 NVIC_InitTypeDef结构体

调用示例:

NVIC_Init(&NVIC_InitStructure);

6、最后

完成了初始化后在stm32f4xx_it.h和stm32f4xx_it.c中完成对应中断处理函数的声明和编写。中断处理函数的名字要参照startup_stm32f40xx.s文件给出的中断名。如图5所示。

后续再总结一下exti,现在还不清楚exti在中断中扮演着什么样的角色。

 

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值