文章目录
- 七、嵌套向量中断控制器和中断控制
- 7.1 嵌套向量中断控制器概述
- 7.2 基本的中断配置
- 7.2.1 中断使能和清除使能
- 7.2.2 中断设置挂起和清除挂起
- 7.2.3 优先级
- 7.2.4 活跃状态
- 7.2.5 PRIMASK和FAULTMASK特殊寄存器
- 7.2.6 BASEPRI特殊寄存器
- 7.2.7 其他异常的配置寄存器
- 7.3 设置中断的步骤实例
- 7.4 软件中断
- 7.5 SYSTICK 定时器
七、嵌套向量中断控制器和中断控制
7.1 嵌套向量中断控制器概述
我们已经了解了,嵌套向量中断控制器(NVIC)为集成在Cortex-M3处理器中的一部分,它同Cortex-M3的CPU内核逻辑紧密相连,其控制寄存器的访问方法和进行了存储器映射的设备一样。除了用于中断处理的控制寄存器和控制逻辑,NV1C单元还包括 SYSTICK定时器的控制寄存器以及调试控制。本章中,我们会看到用于中断控制的控制逻辑,存储器保护单元和调试控制逻辑将会在后面的章节中讨论。
NVIC支持1-240个外部中断输入(通常还被称作中断请求IRQ),具体数量由芯片生产商在开发Cortex-M3芯片的时候确定。另外,NVIC还有一个不可屏版中断(NMI)输人, NMI的实际功能也由芯片生产商确定。多数情况下,MI是不能被外部控制的。
NVIC可以通过系统控制空间(SCS)地址区域访问,该区域的存储器地址为 0xE000E000。大多数中断控制/状态寄存器只能在特权模式访问,而软件触发中断寄存器(STIR)是个例外,经过设置,在用户模式也能访问。中断控制/状态寄存器可通过多种传输方式访问,包括字、半字和字节传输。
另外,有些其他的中断屏蔽寄存器也同中断有关,它们为第3章中介绍的“特殊寄存器”,而且只能通过特殊寄存器访问指令进行操作:将特殊寄存器送至通用目的寄存器(MRS)和将通用目的寄存器送至特殊寄存器(MRS)指令。
7.2 基本的中断配置
每个外部中断都有与之相关的一些寄存器:
- 使能和清除使能寄存器;
- 设置挂起和清除挂起寄存器;
- 优先等级;
- 活动状态。
另外,其他的多个寄存器也可以影响到中断处理:
- 异常屏蔽寄存器(PRIMASK、FAULTMASK、BASEPRI);
- 向量表偏移寄存器;
- STIR;
- 优先级分组。
7.2.1 中断使能和清除使能
中断使能寄存器可以通过两个地址编程,要设置使能位,需要写SETENA寄存器地址;而要清除使能位,则需要写CLRENA寄存器地址。这样,使能和禁止中断就不会影响到其他的中断使能状态。SETENA和CLRENA寄存器都是32位宽的,每个位都代表一个中断输入。
由于Cortex-M3的外部中断可能超过32个,SETENA和CLRENA寄存器的个数可能会超过一个,如SETENA0、SETENA1等(见表8.1)。只有存在的中断的使能位才是可用的。所以,若只有32个中断输入,则只需要SETENA0和CLRENA0。SETENA和 CLRENA寄存器可以通过字、半字或字节的方式访问。由于前16个异常类型为系统异常,外部中断#0的起始异常编号为16(见表8.2)。
7.2.2 中断设置挂起和清除挂起
如果中断发生了而没有立即执行(例如,若另一个更高优先级的中断处理正在执行),它就会被挂起。中断挂起状态可以通过中断设置挂起(SETPEND)和中断清除挂起(CLRPEND)寄存器访问。和使能寄存器类以,若存在多于32个外部中断,挂起状态控制需要的寄存器数量可能会超过1个。
软件可以修改挂起状态寄存器的数值,因此通过CLRPEND寄存器可以取消当前挂起的异常,或者通过SETPEND寄存器产生一个软件中断。
7.2.3 优先级
每个外部中断都有相应的优先级寄存器,其最大为8位,最少为3位。前面的章节已经介绍了,根据优先级分组设置,每个寄存器又可以进一步分为抢占优先级和子优先级。优先级寄存器可以通过字节、半字或字的方式进行访问,优先级寄存器的数量由芯片的外部中断数量决定(见表8.3)。附录D的表D.19中有优先级配置寄存器的详细内容。
7.2.4 活跃状态
每个外部中断都有一个活跃状态位,当处理器开始执行中断处理时,该位被置为1并且当执行中断返回时清除。不过,在中断服务程序执行期间,更高优先级的中断可能会发生并且引起抢占。在这个期间,尽管处理器已经在运行另外一个中断处理,之前的中断仍会被视作处于活跃状态。活跃状态寄存器为32位宽不过只能通过半字或字节传输来访问,若外部中断超过32个,活跃状态寄存器也会多于1个。外部中断的活跃状态寄存器为只读的(见表8.4)。
7.2.5 PRIMASK和FAULTMASK特殊寄存器
PRIMASK寄存器用于禁止除NMI和硬件错误以外的所有异常,它实际上是将当前的优先级改为了0(最高的可编程优先级)。用C语言编程时,要设置或清除PRIMASK寄存器,可以使用符合Cortex微控制器软件接口标准(CMSIS)的设备驱动库或编译器提供的内在函数:
对于汇编语言用户,可以使用改变进程状态(CPS)指令修改PRIMASK的当前状态:
也可以使用MRS和MSR指令修改这个寄存器,例如,
PRIMASK可用于在关键任务中暂时禁止所有中断,当PRIMASK置位时,如果发生了错误,硬件错误就会执行。
FAULTMASK和PRIMASK类似,只是它会将当前的实际优先级修改为-1,这样甚至连硬件错误都会被屏蔽掉。在FAULTMASK置位时,只有NMI会执行。错误处理可以用该寄存器将优先级提高至-1,这样硬件错误异常的某些特性就可以被利用起来了(第12章有这方面的更多信息)。在使用符合CMSIS的驱动库编程时,你可以使用设备驱动库提供的内在函数来设置或清除FAULTMASK:
使用汇编语言的开发人员则可以使用CPS指令修改FAULTMASK的当前状态:
MRS和MSR指令也可以用于访问FAULTMASK寄存器。
在退出除了NMI之外的异常处理后,FAULTMASK会自动清除。FAULTMASK和 PRIMASK寄存器都不能用于用户态。
7.2.6 BASEPRI特殊寄存器
有些情况下,需要禁止低于某个等级的中断,此时,BASEPRI寄存器就派上用场了,而且只需将所需屏被的优先级写入BASEPRI即可。要调低屏蔽的优先级或禁止屏蔽,应该使用BASEPRI寄存器名。BASEPRI和 BASEPRI MAX无法在用户态设置。和其他的优先级寄存器一样,BASEPRI寄存器的格式受实际使用的优先级寄存器的宽度影响。例如,若优先级寄存器只实现了3位,BASEPRI可被编程为0x00、0x20、0x40、·、 0xC0和0xE0。
7.2.7 其他异常的配置寄存器
使用错误、存储器管理错误以及总线错误异常通过系统处理控制和状态寄存器(0xE000ED24)使能,该寄存器还可以控制大多数系统异常的错误挂起状态和活跃状态。
在写这个寄存器时应该小心,防止系统异常的活跃状态被意外修改。要不然,如果已经激活的系统异常的活跃状态被意外清除的话,在异常处理退出时错误异常就会产生。
可以通过中断控制和状态寄存器设置NMI、SYSTICK定时器和PendSV的挂起,该寄存器中的多个位用于调试。多数情况下,应用程序开发只会用到挂起位(见表8.6)。
7.3 设置中断的步骤实例
对于大多数简单的应用程序,程序存储在ROM中并且无须修改异常处理,整个向量表都可以位于代码区域ROM的开始处(0x00000000)。这样,向量表偏移总是为0,而且异常向量已经在ROM中了。设置中断所需的步骤也仅仅包括:
- 设置优先级分组,这步是可选的。默认的优先级分组设置为0,也就是只有第0位用作子优先级。
- 设置中断的优先级,这步是可选的。所有中断的优先级默认为0(最高)。
- 使能中断。
另外,若允许多级中断嵌套,应该确保栈存储足够用。由于异常处理总是使用主栈指针,主栈存储应该有足够多的空间以应对最大级别的嵌套中断。
若应用程序的不同阶段都需要修改中断处理,向量表可能需要被重定位在静态随机访问存储器(SRAM),以便修改异常向量。在这种情况下,还需要采取以下步骤:
- 在系统启动时,优先级分组寄存器可能需要设置,优先级分组默认为0(优先级的bit[7:1]为抢占等级,bit[0]为子等级);
- 复制硬件错误、NMI处理、以及其他所需向量到SRAM中新向量表的位置;
- 设置向量表中的偏移寄存器指向新的向量表;
- 在新的向量表中设置中断的向量;
- 设置中断的优先级;
- 使能中断。
例如,假定新向量表的起始地址定义为“NEW VECT TABLE”,我们可以通过符合 CMSIS的设备驱动库,使用C编程,如下实现以上步骤:
若软件需要在多个硬件设备上运行,那么还需要确定:
- 设计中支持的中断数量;
- 优先级寄存器的数量。
Cortex-M3的中断控制器类型寄存器中包含所支持的中断输入数量,单位为32(见表8.7)。通过执行对SETEN或优先级寄存器等中断配置寄存器的读/写测试,你可以得到外部中断的具体数量。
要确定中断优先级寄存器的实际位数,可以将0xFF写入某个优先级寄存器中,然后读出后查看有多少位为1。最小数量为3,对应的读回值应该为0xE0。
7.4 软件中断
软件中断可以由多种方式产生,如表8.8所示,第一种为使用SETPEND寄存器,使用 STIR则为第二种。
系统异常(NMI、错误、PendSV等)不能通过这个寄存器挂起。用户默认不能写NVIC,不过,如果用户程序有必要写这个寄存器,可以将NVIC配置控制寄存器(0xE000ED14)的位1(USERSETMPEND)置位,这样可以给用户访问NVIC中STIR的权限。
7.5 SYSTICK 定时器
SYSTICK定时器集成在NVIC中,并且可以产生SYSTICK异常(异常类型为#15)。许多操作系统都需要使用硬件定时器来产生中断,以便OS可以执行任务管理,如允许多个任务运行在不同时间片上以及确保单个任务不会锁定整个系统。要实现这个目的,定时器需要能够产生中断,并且如果可能的话,它应该避免用户任务的访问,这样用户程序就不能改变定时器的动作了。
Cortex-M3处理器具有一个简单的定时器,由于Cortex-M3芯片都有相同的定时器,那么在Cortex-M3产品间的软件移植将会非常简单。该定时器具有24位计数值,并可以使用Cortex-M3处理器内部时钟信号或者外部参考时钟(Cortex-M3TRM中也称为STCLK信号)。不过,STCLK的时钟源由芯片设计者决定,因此各产品间的时钟频率可能会有所不同。在选择时钟源时,应该仔细查看芯片的数据手册。
SYSTICK定时器可用于产生中断,它具有特定的异常类型和异常向量。由于不同 Cortex-M3产品的处理都是一样的,软件移植也随之简化了。如表8.9~表8.12所示, SYSTICK定时器由四个寄存器控制。
在多种Cortex-M3产品上运行时,应用程序可以通过校准值寄存器产生同样的 SYSTICK中断间隔。将TENMS中的数值写入重装载值寄存器,就能得到大约10ms的中断间隔。对于其他的中断间隔,软件代码需要依据校准值计算出新的合适数值。不过,并不是所有Cortex-M3产品的TENMS域都是可用的(Cortex-M3的参考输入信号可能被固定连到低电平),因此在使用这个特性时需要查看生产商提供的文献。
除了可作为操作系统的系统节拍外,SYSTICK定时器还可用于多种用途:作为闹钟定时、时间测量等。应该注意的是,在处理器调试期间暂停时,SYSTICK定时器也会停止。根据微控制器的设计,SYSTICK定时器也可以在处理器进入某种休眠模式时停止工作。
要设置SYSTICK定时器,推荐的编程流程如下:
- 将SYSTICK控制和状态寄存器写0以禁止SYSTICK;
- 向SYSTICK重装载值寄存器写入新的重装载值;
- 写SYSTICK当前值寄存器将当前值清为0;
- 写SYSTICK控制和状态寄存器启动SYSTICK定时器。