学习或者了解过ARM的朋友应该都会知道NVIC这么个东西,这个东西也是ARM中非常重要的东西,它是ARM不可分离的部分,搭配着内核共同完成着对中断的响应。
1、那到底NVIC是个啥东西呢?
NVIC:简称嵌套向量中断控制器。它管理着包括内核、外部异常等的所有中断的响应和处理。
为了可以方便说明NVIC的原理和使用方法,本文以STM32F103作为参照说明NVIC的基本组成和使用原理。
STM32F103的内核使用的是Cortex-M3,是32bit的微处理器。它有专门的异常响应系统(NVIC)用于支持系统异常和外部异常中断。
Cortex-M3的异常响应系统支持16个系统异常、240个外部异常中断,总共256个异常响应,可以支持的异常数量可谓是很多了。
内核中对每种异常都有一个编号用于表示对应的中断,其中编号0-15表示系统异常,这部分属于内核的,不可更改,另外的16之后的是外部中断,除了少数不支持编程外,其他的都支持编程控制。
在 Cortex-M3 中有3个系统异常是固定优先级的,它们高于其他所有的异常,以确保它们能及时得到响应。
系统异常编号如下所示:
注意:上图的系统异常的编号,0是不存在的,不是异常。
外部异常的编号如下所示:
2、ARM Cortex-M 的优先级的定义
NVIC可以支持240个外部中断,也就是说最多有240个中断优先级。
虽然外部可以支持的中断异常可以高达240个,但是往往在设计芯片的时候都会做精简,不会完全用上,这个主要跟芯片设计时所考虑的应用场景有关吧。比如说有些芯片所支持的优先级数有可能是8级、16级、32级、48级等等的。
ARM公司对于中断优先级的管理提供了8个位给芯片厂商设置中断的优先级,芯片厂商可以根据需要自己裁剪,达到最终到底使用级优先级的目的。
但是,芯片厂商不一定都完全使用这8个位,可能只使用几个位。
STM32F103是ST公司开发的,中断优先级就只使用了这8个位中的高4位 ,所以最多可设置16个中断优先级。
这个是怎么理解呢?
假如我现在有一款芯片,它是用4个位表达优先级的,那么优先级配置寄存器的划分如下图:
[0:3]没有被使用到,你去读它们也是返回零,写它们就直接忽略写入的值。所以3个位的情况下,能够使用的 16 个优先级为:0x00(最高) ~ 0xE0。
注意:中断优先级使用数值来表示。数值越小,优先级越高。
3、抢占优先级 & 响应优先级
Cortex-M3 支持中断嵌套,可以让优先级高的中断抢占优先级低的中断,从而确保高优先级的中断异常能及时得到响应,而被打断了的低优先级中断异常先进入等待,等待高优先级的中断执行完毕之后再执行。
在NVIC中有一个寄存器:应用程序中断及复位控制寄存器(AIRCR),它里面有一个位段名为“优先级组”,如下:
这个优先级组的作用是很大的,它将每个优先级可配置的异常中断都能进行分组配置,意思就是可以将一个中断进行设置,分为抢占优先级和响应优先级。
首先,我们要知道,每一个外部中断它都有一个相对应的优先级寄存器,这个寄存器也是8Bit的长度,同样是使用高四位表示中断的优先级的。然后在这四位中再继续进行划分,分出了用多少个Bit表示抢占优先级和多少个Bit表示响应优先级。
根据这4个Bit的划分,抢占优先级和响应优先级(子优先级、亚优先级)的组合可以有如下的结果:
抢占优先级和响应优先级怎么理解呢?
简单来说就是,当一个系统中有很多的外部中断时,假如某些中断同时发生,那到底应该先响应哪个呢?如果设置了抢占优先级和响应优先级,那就会先比较抢占优先级,谁高就先执行谁;如果抢占优先级一样,就比较响应优先级,谁高就先执行谁;如果没有设置抢占优先级,而响应优先级又是一样,那就按照系统默认的中断优先级,谁高先响应谁。
4、中断优先级分组的寄存器设置
前面介绍NVIC、中断优先级的一些基本特性,对NVIC应该有一定的认识了。那怎么去配置中断的优先级呢?
首先,通过应用程序中断及复位控制寄存器AIRCR 的第8位至第10位设置优先级的分组,寄存器如下图:
特别注意:想要直接使用位操作更改10:8位是无效的!!!
比如:
AIRCR |= 5<<8; //这么写是无效
直接位操作对这个寄存器的内容更改是不生效的,如果想要修改这个寄存器需要同时操作31:16位才能生效。在寄存器中是有相关的说明的,如下:
打个比方:如果我要设置中断为分组2,抢占优先级(0--3)、响应优先级(0--3),那么有效的操作方式可以按照如下:
SCB->AIRCR = 0X05FA<<16 |5<<8; //设置为分组2. 抢占优先级(0--3)、响应优先级(0--3)
或
SCB->AIRCR = 0x05FA0500 ; //设置为分组2. 抢占优先级(0--3)、响应优先级(0--3)
5、抢占优先级和响应优先级的寄存器设置
设置好了优先级的分组之后,如果你想要使用某一个异常中断并且希望设置它的抢占优先级和响应优先级,那你需要了解相关可能用到的寄存器,相关的寄存器可以到内核文件中查阅,如下:
上面的寄存器分别如下:
5.1、中断设置启用寄存器
5.2、中断清除启用寄存器
5.3、中断设置挂起寄存器 ISPR
5.4、中断清除挂起寄存器 ICPR
5.5、中断活动位寄存器 IABR
5.6、中断优先级寄存器 IP
IPR0-IPR16寄存器为每个中断提供4位优先级字段。这些寄存器是字节可访问。每个寄存器包含四个优先级字段,它们映射到CMSIS中断优先级数组IP[0]到IP[67]。
每个优先级字段保存一个优先级值0-255。值越低,相应中断的优先级越高。处理器只实现每个字段的位[7:4],位[3:0]读取为零并忽略写入。
5.7、软件触发中断寄存器
当SCR中的USERSETMPEND位设置为1时,无权限软件可以访问STIR,只有特权软件才能启用对STIR的非特权访问。
6、中断设置的操作示例
中断的设置的步骤:
1、设置中断分组。抢占优先级是多少,子优先级是多少
SCB->AIRCR = 0X05FA << 16 | 0X05 << 8;
2、开总中断
NVIC->ISER[1] |= 1<<(37 -32);
7、举例:设置串口中断
如果要使用串口的中断,需要完成两部分操作:
(1)设置中断优先级分组
配置串口中断的优先级,如果不手动配置而有启用串口中断,会默认为自然优先级。
外部中断的优先级是通过 IP[n] (n=0~239) 进行设置的,这个n值可以通过查阅中断向量号即可以得到,如下:
比如:我要设置串口中断的抢占优先级为1,响应优先级为2。那么示意图如下:
操作示例如下:
NVIC->IP[37] = 0X60;
(2)开启串口1中断
串口1的中断向量表如下:
开启串口1的总中断的示例如下:
NVIC->ISER[1] |= 1<<(37-32);