本文主要围绕基于ARM M3内核提供的终中断管理器NVIC的介绍、管理以及实现展开。
一、NVIC简要介绍
ARM—CM3 内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。单片机设计商并不会使用内核提供的所有中断接口,而是选择其中的一部分。
中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。
NVIC共支持1至240个外部中断输入,可以看做是所有外部中断的一个大管家。每隔外部中断都需要在NVIC下列寄存器中“挂号”。
以下寄存器是NVIC中与每一个外部中断都要打交道的(每隔外部中断都有预留位置)
- 使能与除能寄存器:用于告知NVIC当前那些外部中断需要打开
- 悬起与解悬寄存器:用于读取和设置外部中断是否为悬起状态(被同级或高优先级中断打断,或被屏蔽)
- 优先级寄存器:用于设置具体每一个中断的优先级
- 活动状态寄存器:用于方便查看某一外部中断当前的运行状态
此外还有一些对外部中断整体起着关键作用 - 异常掩屏寄存器
- 向量表偏移寄存器
- 软件触发中断寄存器
- 优先级分组位段寄存器
二、NVIC管理方式
面对如此大量的外部中断,要管好他们着实的不容易。NVIC管理外部中断主要通过两个寄存器一个是优先级分组位段寄存器另一个是优先级寄存器
首先要了解什么为什么要优先级分组
在ARM—CM3中优先级可以分为抢占优先级和响应优先级(亚优先级)两种,他们同时受一个对应的8位优先级寄存器控制,那问题来了,在这八位优先级寄存器中那些位由于控制抢占优先级哪些位用于控制响应优先级,这时就需要优先级分组寄存器出马。
如上图所示不同的优先级分组会指定抢占优先级和亚优先级(响应优先级)的尾段,进而确定给优先级寄存器中参数的具体意义。
接下来引出第二个问题抢占优先级与响应优先级的区别
- 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
- 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
- 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
- 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断的中断号小,中断号小的优先执行。
简单来说判断一个外部中断能否打断打断正在运行的中断,看新发生的中断的抢占优先级是否比被打断的中断的优先级高,若两个中断同时发生则先看抢占优先级,相同则看响应优先级,若还相同就按照中断号进行判定,终端号小的优先执行。
值得注意的是
在ARM—CM3中具体确定某一个外部中断的是优先级寄存器,但优先级寄存器并不一定是8位的,由于芯片设计商在设计具体型号的芯片时并不会真的设计有240个外部中断,因此用于优先级表达的优先级寄存器也不一定是8位全部为有效位,ARM-M3支持最高8位最低3位的优先级有效位。在配合中断优先级分组,确定具体哪些位控制抢占优先级哪些位控制响应优先级。
三、如何找到对应的中断处理函数地址
我们知道ARM再启动时会初始化中断行向量表,那里预设了所有中断处理函数的入口地址,在终端来临时ARM会进入异常状态,有以下两种情况
- 向量中断模式用于系统内部中断(RESET、NMI不可屏蔽中断、异常处理)。当向量中断产生时,控制器直接将PC赋值,如IRQ异常 跳到0x0000000d处(相当于重新启动单片机)
- 外部中断模式,跳转到统一的函数地址,此函数通过判别寄存器标识位和优先级关系进行中断-处理。
首先明确每一个中断都会有对应的序号(位置值),该数字决定了任意一个外部中断在中断向量表中的偏移量,当中断发生会通过过该中断对应的序号计算出在中断向量表的偏移量,根据中断向量表基地址+偏移地址就可以计算出保存该中断对应的中断处理函数入口地址的地址。
四、中断相应过程
响应中断时,要做三件事情:
入栈:
- 把xPSP,PC,LR,R12以及R3~R0寄存器的值压入栈
- 如果程序过大,需要用到R4~R11,由编译器负责生成代码来push他们
- 当前代码正在使用PSP,则压入PSP,正在使用MSP则压入MSP。进入服务程序后,将一直使用MSP
取中断向量:同时从中断向量表中找出对应的中断服务程序入口地址
更新寄存器:
- 更新SP(PSP或MSP)
- PSR的IPSR位段(新响应的中断编号)
- PC(指向中断服务程序的入口地址)
- LR(EXC_RETURN)
- NVIC相关寄存器
值得注意的是LR寄存器在入栈时,也会将当前所处的工作模式保存到LR中的EXC_RETURN值,当中断返回时将决定返回的模式和使用堆栈指针。