提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、简介
中断一般是由硬件(如外设和外部输入引脚)产生的事件,它会引起程序流偏离正常的流程(如给外设提供服务)。所有的Cortex-M处理器都会提供一个用于中断处理的嵌套向量中断控制器(NVIC)。除了中断请求,还有其他的需要服务的时间,成为“异常”。
一般流程如下:
(1) 外设确认到处理器的中断请求;
(2) 处理器暂停当前执行的任务;
(3)处理器执行外设的中断服务程序(ISR),若有必要可选择软件清除中断请求;
(4)处理器继续执行之前暂停的任务。
Cortex-M3/4的NVIC支持最多240个IRQ(中断请求,多数由外设产生)、1个不可屏蔽中断(NMI,通常有看门狗或掉电检测等产生)、1个SysTick(系统节拍)定时中断以及多个系统异常(来自内核)。
二、异常类型
Cortex-M处理器的异常架构具有多种特性,编号1~15的为系统异常(部分优先级固定),16及以上的则为中断输入。包含中断在内的多数异常,都具有可编程的优先级。
异常列表:
中断列表:
对于使用CMSIS-Core的普通编程,中断标识由中断枚举实现,从数值0开始(代表中断#0),系统异常的编号为负数,同事定义了系统异常的名称。
三、中断管理简介
Cortex-M 处理器具有多个用于中断和异常管理的可编程寄存器,多数唯一NVIC和系统控制块(SCB)中。
NVIS和SCB位于系统控制空间(SCS),地址从0xE000E000开始,大小为4KB。SCS中海油SysTick,MPU以及用于调试的寄存器。该区域中基本上所有的寄存器都只能由运行在特权访问等级的代码访问。
对于一般的应用编程,最好使用CMSIS-Core访问函数,如下表所示,
复位后,所有的中断都处于禁止状态,且默认的优先级都为0,在使用任何一个中断之前,需要:
- 设置所需中断的优先级(可选)。
- 使能外设中可以触发中断的中断产生控制
- 使能NVIC中的中断
四、优先级定义
异常优先级和处理器当前的优先级决定异常是否被处理器接收以及何时被处理器接收并执行异常处理。高优先级(编号更小)可以抢占低优先级的异常,这就是异常/中断嵌套的情况。优先级实际数量由芯片设计厂商决定,大量优先级会增加NVIC的复杂度,同时增加功耗降低速度。中断优先级(最多具有128个抢占等级)由优先级寄存器,宽度为3~8bit,如下所示,只用了3bit。
利用系统控制块(SCB)中的优先级分组寄存器,每个可编程优先级的优先级配置寄存器可分为两部分(分组/抢占优先级和子优先级)。在处理器已经运行一个中断处理时能否在产生运行另一个中断处理,是由该中断的抢占优先级大小决定。子优先级只在两个相同抢占优先级的中断同时产生,先处理具有更高子优先级(数值更小)的异常。优先级分寄存器各数值下抢占优先级和子优先级bit位情况如下:
在确定实际的分组优先级和子优先级,必须考虑以下因素:
- 实际的优先级配置寄存器
- 优先级分组配置
注:两个中断具有相同的分组/抢占优先级和子优先级,则异常编号更小的优先级更高(IRQ#0优先级高于IRQ#1)。
五、向量表和向量表重定位
当Cortex-M处理器接收了某异常请求后,处理器需要确定该异常楚(中断则为ISR)的起始地址。该信息存储在向量表中,一般定义在供应商的启动代码中。默认从地址0开始,向量地址则为异常编号乘 4。
启动代码中使用的向量表还包含主栈地址(MSP)的初始值,因为NMI等异常可能会紧挨着复位产生,而且此时并未进行任何初始化操作。
一般来说,起始地址(0x00000000)处应该为启动存储器,一般为Flash或者ROM,而且运行不能对他们进行修改。不过有些应用需要在运行时修改或定义向量表,为了这种处理,Cortex-M3/4 实现了一种向量表重定位的特性。提供了一个名为向量表偏移寄存器(VTOR),该寄存器将正在使用的存储器的起始地址定义为向量表,如下图所示:
VTOR的复位值为0,若使用符合CMSIS的设备驱动库进行应用编程,可通过SCB->VTOR访问该寄存器。将向量表重定位到SRAM区域的开头出,代码如下:
//字访问的宏定义
#define HW32_REG(ADDRESS) (* ((volatile unsigned long *)(ADDRESS)))
#define VTOR_NEW_ADDRESS 0x20000000
int i;
//在设置VTOR之前先将向量表复制到SRAM
for(i = 0; i < 48; i++) //假定异常数量为48
{
HW32_REG((VTOR_NEW_ADDRESS + (i << 2))) = HW32_REG((i << 2));
}
_DMB();//数据存储器屏障,确保到存储器的写操作结束。
SCB->VTOR = VTOR_NEW_ADDRESS;
_DSB();//数据同步屏障,确保接下来的指令都是用新配置。
使用VTOR时,需要将向量表大小扩展为下一个2的整数次方,且新的向量表地址必须要对齐这个数值。
例如:微控制器有32个中断源,则向量表大小为(32+16(用于异常处理空间))×4(每个向量的字节数)=192(0xC0),扩展到下一个2的整数次方就是256字节,因此向量表的地址可被设置为0x00000000、0x00000100、0x00000200等。
向量表重定位特性可用于多种情形:
- 具有BootLoader的设置
- 应用程序加载到RAM
- 动态修改向量表
六、中断输入和挂起行为
每个中断都有以下属性:
- 都可呗禁止(默认)或使能
- 都可被挂起(等待服务的请求)或解除挂起
- 都可处于活跃(正在处理)或非活跃状态
NVIC在设计上既支持脉冲中断请求的外设,也支持产生高电平中断请求的外设,无需配置任何一个NVIC寄存器以选择其中一种中断类型。对于脉冲请求,脉冲宽度至少为一个时钟周期;对于电平触发,在ISR的操作清除请求之前,请求服务的外设要一直保持信号电平(如写入寄存器以清除中断请求)。中断挂起和激活行为如下图所示:
中断的挂起状态位于中断挂起状态寄存器中,可手动清除。若中断产生时在处理器正在处理另一个优先级更高的中断,而在处理器对该中断请求做出响应之前,挂起状态呗清除掉了,该请求就会被取消且不会在得到处理,如下所示:
若外设持续保持请求状态,及时软件清除挂起状态,挂起状态还是被置位:
若在得到处理后,中断源仍在继续保持请求状态,那么中断就会再次进入挂起状态且再次得到处理器的服务:
对于脉冲中断,若在处理器开始处理前,中断请求信号产生了多次,他们会被当做一次中断请求:
中断的挂起状态可以在其正在处理时再次置位,如下所示,在之前的中断请求正被处理时产生了新的请求,这样被引发新的挂起状态,因此处理器在前一个ISR结束后,需要再次处理这个中断
及时中断被禁止,它的挂起状态仍可置位。这种情况下,若中断在稍后被使能了,它仍keil被触发并得到服务。有时候这种情况不是我们希望的,因此在使能NVIC中的中断前手动清除挂起状态。
六、异常流程
接受异常请求
若满足以下条件,处理器会接受请求:
- 处理器正在运行(未被暂停或处于复位状态)
- 异常处于使能状态(NMI和HardFault 为特殊状态)
- 异常的有限寄高于当前等级
- 异常未被异常屏蔽寄存器(如PRIMASK)屏蔽
异常进入流程
- 多个寄存器和返回地址被压入当前使用的栈。可将异常处理用普通C函数实现。若处理器处于线程模式且正在使用进程栈指针(PSP),则PSP指向的栈区域就会用于该压栈过程,柔则就会使用主栈指针(MSP)指向的栈区域。
- 取出异常向量(异常处理/ISR的起始地址)。为了减少等待时间,这一步可能会和压栈操作并行执行。
- 取出待执行异常处理的指令。在确定了异常处理的起始地址后,指令就会被取出。
- 更新NVIC寄存器和内核寄存器,包含挂起状态和异常的活跃状态,处理器内核中的寄存器包含程序状态寄存器(PSR),链接寄存器(LR),程序计数器(PC)以及栈指针(SP)。
执行异常处理
在执行异常处理时,处理器就会处于处理模式,此时:
- 栈操作使用主栈指针(MSP)
- 处理器运行在特权访问等级
在异常处理的的结尾,程序代码执行的返回会引起EXC_RETURN数值被加载到程序计算器中(PC),并触发异常返回机制。
异常返回
对于ARM Cortex-M处理器,异常返回机制由一个特殊的地址EXC_RETURN触发,该数值在异常入口处产生且存储在链接寄存器(LR)中。当该数值由某个允许的异常返回指令写入PC,他就会触发异常返回流程。可用于触发异常返回的指令如下: