目录
- 1.NVIC和系统控制块特性
- 2.中断使能和清除使能
- 3.中断挂起和清除挂起
- 4.中断优先级
- 5.中断控制的通用汇编代码
- 使能和禁止中断
- 设置和清除中断挂起状态
- 设置中断优先级
- 6.异常屏蔽寄存器(PRIMASK)
- 7.中断输入和挂起行为
- 8.中断等待
- 9.系统异常的控制寄存器
- 10.系统控制寄存器
1.NVIC和系统控制块特性
嵌套向量中断控制器(NVIC)集成在Cortex-M0处理器里,它与处理器内核紧密相连,并且提供了中断控制功能以及对系统异常的支持。
除了NVIC,系统控制块(SCB)位于系统控制空间(SCS)地址区域。SCB具有的特性可以给操作系统提供支持,例如SysTick异常对应的内部定时器。
NVIC的特性如下:
- 灵活的中断管理:使能/禁止,优先级配置。
- 硬件嵌套中断支持。
- 向量化的异常入口。
- 中断屏蔽。
Cortex-M0处理器中的NVIC支持最多32个外部中断和一个不可屏蔽中断(NMI),中断输入请求可以是电平触发的,也可以是最小一个时钟周期的脉冲信号。每个外部中断都可以独立地使能或禁止,并且其挂起状态也可以手动地设置和清除。
NVIC的寄存器经过了存储器映射,并且用C语言访问也很容易,其寄存器的起始地址为0xE000E100。对于Cortex-M0处理器,对NVIC寄存器的访问必须是每次一个字。
同NVIC 寄存器类似,SCB寄存器也是按照字来访问的,起始地址为0xE000E010。SCB寄存器涉及的特性包括SysTick定时器操作、系统异常管理、优先级控制和休眠模式控制。
2.中断使能和清除使能
中断控制寄存器为可编程的,用于控制中断请求(异常编号16及以上)的使能和禁止。寄存器的宽度根据支持的中断数量而不同,最大为32位,最小为1位。可以通过两个独立的地址编程这个寄存器,使能中断时使用SETENA地址,而禁止中断时则使用CLRENA地址:
将设置和清除操作分为两个不同的地址具有诸多优势。
首先,它减少了使能中断所需要的步骤,因此也就减少了程序代码并且降低了执行时间。
多个应用程序进程同时访问寄存器时,可能会导致已编程的控制信息丢失,而设置和清除的分离则能防止这种情况的发生,这也是这种安排的第二个优势。
3.中断挂起和清除挂起
如果一个中断发生了,却无法立即处理(比如处理器正在处理更高优先级的中断),这个中断请求将会被挂起。挂起状态保存在一个寄存器中,如果处理器的当前优先级还没有降低到可以处理挂起的请求,并且没有手动清除挂起状态,该状态将会一直保持合法。
可以通过操作中断设置挂起(SETPEND)和中断清除挂起(CLRPEND)这两个寄存器来访问或修改中断挂起状态。
同中断使能控制寄存器类似,中断挂起状态寄存器也是在物理上为一个寄存器,而通过两个地址来实现设置和清除相关位。这就使得每一位都可以独立修改,而无须担心在两个应用程序进程竞争访问时出现的数据丢失。
4.中断优先级
每一个外部中断都有一个对应的优先级寄存器,每个优先级都是2位宽,并且使用中断优先级寄存器的最高两位,每个寄存器占1个字节(8位)。
Cortex-M0中的NVIC寄存器只支持字传输,这样每次访问都会同时涉及4个中断优先级寄存器。
未使用的位读出为0,写入这些位的操作会被忽略,而读出时则为0。
由于每次访问优先级寄存器就相当于访问4个中断的优先级,如果只想改变其中的1个,需要将整个字读出,修改1个字节,然后写回整个字。
中断优先级寄存器的编程应该在中断使能之前,其通常是在程序开始时完成的。应该避免在中断使能之后改变中断优先级,因为这种情况的结果在ARMv6-M体系结构上是不可预知的,并且不被Cortex-M0处理器支持。
5.中断控制的通用汇编代码
使能和禁止中断
中断的禁止和使能非常简单,下面的函数“nvic_set_enable”和“nvic_clr_enable”以中断编号作为输入,中断编号在函数调用前保存在R0中:
要使用这些函数,只需要将中断编号放在R0中,然后调用函数即可,例如:
在ARM汇编器中(包括 Keil MDK),FUNCTION和ENDFUNC这两个关键字分别用于指示函数的开始和结束,它们的使用是可选的,ALIGN关键字则保证了函数的开始处于正确的对齐地址上。
设置和清除中断挂起状态
-
设置和清除中断挂起状态的汇编函数同使能和禁止中断的类似,唯一的区别在于标号和NVIC寄存器的地址不同。
-
应该注意的是,清除某中断的挂起状态未必能够完全阻止中断的产生。如果中断源持续产生中断请求(电平输出),即便你试图清除NVIC的寄存器,中断挂起状态仍可能保持为高。
设置中断优先级
-
设置中断优先级的汇编函数稍微复杂一些。首先,它需要两个输入参数:中断编号和新的优先级;其次,由于总共有多达8个的优先级寄存器,还需要计算要修改的中断所在的位置;最后,由于优先级寄存器是按照字来访问的,还需要执行读-修改-写的过程,才能把正确的字节数据写到32位优先级寄存器中。
-
不过,在大多数应用程序中,可以在程序的开始部分,使用更加简单的代码一次设置多个中断的优先级。例如,可以将优先级预定义在一个常数表中,然后使用一个很短的指令序列将这个表复制到NVIC优先级寄存器中。
6.异常屏蔽寄存器(PRIMASK)
- 异常屏蔽寄存器(PRIMASK)可以屏蔽掉除了NMI和硬件错误异常的其他所有的中断和系统异常。
- PRIMASK寄存器只有1位有效,并且在复位后默认为0。该寄存器为0时,所有的中断和异常都处于允许状态;而设为1后,只有NMI和硬件错误异常处于使能。实际上,当PRIMASK 设置为1后,处理器的当前优先级就降到了0(可设置的最高优先级)。
- 对时间敏感的程序完成后,应该清除PRIMASK。要不然即使在中断处理中使用了_disable_irq()函数(或者设置PRIMASK),处理器将停止接受新的中断请求。这点与ARM7TDMI有所不同,ARM7TDMI处理器在中断返回时,由于CPSR的恢复,其I位会被重设(使能中断)。而Cortex-MO处理器的PRIMASK和xPSR是相互独立的,因此异常返回不会影响中断屏蔽状态。
7.中断输入和挂起行为
Cortex-M0处理器允许两种形式的中断请求:电平触发以及脉冲输入,这一特性涉及包括NMI在内的中断输入对应的多个寄存器。每一个中断输入都对应着一个挂起状态寄存器,且每个寄存器只有1位,用于保存中断请求,而不管这个请求有没有得到确认(例如,通过I/O引脚相连的外部硬件产生一个中断脉冲)。当处理器开始处理这个异常时,硬件将会自动清除挂起状态。
NMI的情况也基本上是一样的,只是由于NMI的优先级最高,当它产生后几乎能立即得到响应。除此之外,NMI与IRQ相当类似:NMI的挂起状态也可以由软件产生,如果处理器仍然在处理之前的NMI请求,新的NMI则会保持挂起状态。
一些简单的中断处理如下:
-
大多数ARM处理器的外设都使用电平触发中断输出,当中断事件发生时,由于外设连接到了NVIC上,中断信号会得到确认。在处理器执行中断服务并且清除外设的中断信号以前,该信号会保持高电平。在NVIC内部,当检测到有中断发生时,该中断的挂起状态会被置位,当处理器接受该中断并且开始执行中断服务程序后,挂起状态就会被清除。
-
有些中断源可能会产生脉冲形式的中断请求(至少持续1个时钟周期)。在这种情况下,在中断得到服务之前,挂起状态寄存器将会一直保持该请求。
-
如果中断请求没有立即执行,并且在确认之前被软件清除了,这样处理器会忽略掉本次请求,并且不会执行中断处理。可以通过写NVIC_CLRPEND寄存器来清除中断挂起状态,这种处理在设置外设时非常有用,因为在设置以前,该外设可能已经产生了一个中断请求。
-
如果在软件清除挂起状态时,外设仍然保持着中断请求,挂起状态还会立即生成。
-
如果外设产生的中断请求在异常处理时没有被清除,异常返回后挂起状态就会被又一次激活,这样中断服务程序会再次执行,若外设中还有待处理的数据这种情况就会发生(例如,只要接收FIFO中还有数据,数据接收机就要将中断请求保持高电平)。
-
对于脉冲中断,如果在中断服务开始执行以前,中断请求脉冲产生了多次(例如,处理器可能在处理另外一个中断请求),这种多个中断脉冲会被当做一次中断请求。
-
在中断服务程序执行过程中产生的脉冲中断请求,会被当做新的中断请求,并且在本次中断退出后,还会引起中断服务程序再次执行。
由于和当前执行中断的优先级相同,故第二个中断请求不会立即引发中断。一旦处理器退出了中断处理,当前的优先级就会降低,这样挂起的中断就会得到获取服务的机会。
即使某中断已经被禁止,该中断的挂起状态仍然可以被激活。因此,当外设需要重新编程以及更改中断设置时,在重新使能中断前,你可能需要清除NVIC里的中断挂起状态,这个操作可以通过写入地址为0xE000E280的中断清除寄存器来实现。
8.中断等待
通常情况下,Cortex-M0的中断等待时间为16个周期。这个等待时间从中断确认的处理器时钟周期开始,一直到中断处理开始执行结束。计算中断等待需要具备以下前提:
- 该中断使能并且没有被PRIMASK或是其他正在执行的异常处理所屏蔽。
- 存储器系统没有任何等待状态。
下面几种情况可能会导致不同的中断等待:
- 中断的末尾连锁,如果中断返回时产生了另外一个中断请求,处理器就会跳过出栈和压栈过程,这样就减少了中断等待时间。
- 延迟到达,如果中断发生时,另外一个低优先级的中断正在进行压栈处理,由于延迟到达机制的存在,高优先级的中断会首先执行,这样也会导致高优先级的中断的等待时间减小。
上面这两种情况会使得中断等待减至最小,不过有些嵌入式应用需要零误差的中断响应。Cortex-M0处理器的接口上有一个叫做IRQLATENCY的8位信号,并且它与NVIC相连,可以用作中断等待控制。如果将这个信号连接到0,Cortex-M0处理器就会以最快的速度处理中断请求。
9.系统异常的控制寄存器
对于Cortex-M0处理器,只有3个与OS相关的系统异常才具有可编程的优先级,它们包括SVC、PendSV和SysTick,其他像NMI和硬件错误等系统异常的优先级则是固定的。
未使用的位读出为0,对这些未使用的位的写操作将会被忽略。Cortex-M0处理器只使用系统处理优先级寄存器SHPR2和SHPR3。
如果使用符合CMSIS的设备驱动,可以使用如下图所示的寄存器名来访问SHPR2和SHPR3。
另一个对系统异常处理有用的寄存器为中断控制状态寄存器(ICSR),这个寄存器允许软件挂起NMI异常,并且可以访问PendSV和SysTick的挂起状态。它还提供了对调试器有用的信息,例如当前活跃的异常编号以及当前是否有挂起异常。
如果使用符合CMSIS的设备驱动库,可以使用寄存器名“SCB->ICSR”访问ICSR。
10.系统控制寄存器
NVIC地址区域(从0xE000E000到0xE000EFFF)中也包含了多个系统控制寄存器,因此NVIC的整个存储器区域又被称作系统控制空间(SCS)。
- CPU ID基址寄存器
CPU ID基址寄存器中包含了处理器的ID,并且是只读的,它为应用软件以及调试器提供了处理器内核类型和版本信息。
- 应用中断和复位控制寄存器
应用中断和复位控制寄存器(AIRCR)具有多个功能,它可以用于应用程序请求系统复位、识别系统的大小端以及清除所有的异常活动状态(只能由调试器完成)。如果使用符合CMSIS的设备驱动,可以使用“SCB->AIRCR”访问该寄存器。
VECTKEY域用于防护对该寄存器的意外写操作引起的系统复位或清除异常状态。
SYSRESETREQ位用于请求系统复位,当写入1并且键值合法时,它就会触发处理器上的SYSRESETREQ信号,系统的实际复位时间与这个信号的连接方式有关。根据系统复位设计的不同,从该位写入1到实际复位可能只需要很小的一段延时。在典型的微控制器设计中,SYSRESETREQ产生的系统复位可以对处理器和系统的大部分都有效,而不应影响微控制器的调试系统。这样即使软件触发了一次复位,调试操作也可以正常工作。
VECTCLRACTIVE位被调试器用来清除异常状态,比如当调试要执行程序返回而不想复位处理器时,就可以使用该位,处理器上运行的应用程序代码不应使用这一特性。
- 配置和控制寄存器
Cortex-M0处理器上的配置和控制寄存器(CCR)为只读的,它决定了栈的双字对齐设置和非对齐访问的处理。
STACKALIGN为1,表示当产生异常压栈时,栈帧总是自动对齐到双字对齐的存储器位置上。UNALIGN_TRP位为1,表示试图执行非对齐访问的操作会导致错误异常发生。如果使用符合CMSIS的设备驱动,可以通过寄存器名“SCB->CCR”来访问配置和控制寄存器。