LPC2292芯片中断的一点见解
0赞中断向量表必须写在程序的最前面,并由分散加载文件将此处代码加载到0x0000 0000处,这样才能保证LPC2292芯片在复位中断后从正确的地址进入相应的处理程序,分散加载文件在后面分析。
Reset
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0xb9205f80
LDR PC, [PC, #-0xff0]
LDR PC, FIQ_Addr
这是中断向量表里的内容,由于LPC2292芯片的中断向量表表中每个中断只占4个字节,所以不能在中断向量表中完成中断处理程序,在这里使用LDR指令,LDR指令正好占4个字节,所以此中断向量表与LPC2292芯片对应,LDR将中断处理函数的首地址赋给PC,即跳向相应的中断处理程序。在中断向量表的第六行是LPC2292中断向量表的保留字段,这里的DCD指令只用来占用4个字节,使后面的中断处理程序的首地址能够和中断向量表对齐,如IRQ中断向量在地址0x0000 0018处,当发生IRQ中断后,LPC2292芯片就会转到地址0x0000 0018处去执行指令。如果前面的保留字段没有被占用,那么编译器便会将LDR PC, [PC, #-0xff0]指令放入到保留字段的地址0x0000 0014处,而不是放入到它本应该被存放的地址0x0000 0018处,LPC2292芯片转到地址0x0000 0018处执行指令时就不能进入IRQ中断处理程序中,而有可能进入后面的FIQ中断处理程序中,这样就发生了程序紊乱。数据0xb9205f80没有特殊含义,可以任意更改。
LPC2292芯片有向量中断模式,向量中断模式主要是减少中断的延迟时间,提高其响应速度。LDR PC, [PC, #-0xff0],执行这条语句时,PC值为0x18,由于3级流水线,再加8,就是0x20,然后再减掉0xff0,就等于0xFFFFF030,而这个地址里面保存的内容刚好就是IRQ服务程序的地址(这个地址是在IRQ发生时,由硬件自动装进去的)。
ResetAddr DCD ResetInit
UndefinedAddr DCD Undefined
SWI_Addr DCD SoftwareInterrupt
PrefetchAddr DCD PrefetchAbort
DataAbortAddr DCD DataAbort
FIQ_Addr DCD FIQ_Handler
这里的DCD指令是为了申请申请一个字的存储单元,将相应的中断函数首地址赋给变量,而其中的Undefined,PrefetchAbort,DataAbort并不需要相应的中断函数,所以每个中断函数都只是写了一个死循环。如果开发中需要其他的功能,可以直接添加。
软中断SoftwareInterrupt是由软件提供的中断,这里使用软中断向外提供4个函数IRQDisable(),IRQEnable(),FIQDisable(),FIQEnable(),分别是IRQ向量中断的开与关,FIQ快速中断的开与关。这4个函数在汇编语言里实现,但要让外部以软中断的方式使用,必须与target.h中的中断联合起来使用。
这里以IRQDisable()函数介绍一下软中断的调用流程,首先target.h中的__swi(0x00) void SwiHandle1(int Handle);定义了软中断函数SwiHandle1(int Handle)函数,它使用0号软中断。__swi(0x00)告诉Keil编译器它使用的是软中断,因此编译器在编译的时候会将SwiHandle1(int Handle)函数与汇编语言编写的软中断函数SoftwareInterrupt联系在一起。并通过寄存器R0传递参数。然后使用#define IRQDisable() SwiHandle1(0)定义了IRQDisable()函数,当调用此函数时,实际就是调用了SwiHandle1(0)函数,向汇编语言编写的软中断函数SoftwareInterrupt传参数0。汇编语言编写的软中断函数SoftwareInterrupt通过判断寄存器R0中的值得到0,然后进入汇编语言编写的IRQDisable中,实现关IRQ中断的功能。
最重要的是ResetInit中断函数是LPC2292上电复位初始化程序入口,在上面的中断向量表中已经定义。它实现了LPC2292各个模式中堆栈的初始化InitStack,并且根据配置项的不同,初始化目标板TargetReset Init,最后进入主函数。
InitStack
MOV R0, LR
MSR CPSR_c, #0xd3
LDR SP, StackSvc
MSR CPSR_c, #0xd2
LDR SP, StackIrq
MSR CPSR_c, #0xd1
LDR SP, StackFiq
MSR CPSR_c, #0xd7
LDR SP, StackAbt
MSR CPSR_c, #0xdb
LDR SP, StackUnd
MSR CPSR_c, #0xdf
LDR SP, =StackUsr
MOV PC, R0
这段代码实现的LPC2292各个模式下堆栈的初始化,要初始化各个工作模式下的堆栈,就必须使处理器处于相应的工作模式下,因此程序中需要进行工作模式的切换;另外,要注意只有当处理器处于特权模式(非用户模式)下的时候,才能进行工作模式的切换。当系统复位或软件中断响应时,处理器进入的是管理模式,此时,可以进行各种模式的切换。首先进入InitStack子函数,将当前PC值存入R0寄存器中,然后修改CPSR值,进入各个模式后,初始化当前模式下的堆栈。最后将R0寄存器中的值存入PC中,从子函数中返回。
