本文将讲述三星S5PV210 SOC芯片(ARM-Cortex A8核心)裸板的中断发生和处理过程。
关于中断的说明:
中断:指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。ARM的中断比较复杂,与其它的处理器一样,ARM的中断可分为外部中断和内部中断。
中断的三个关键部分包括:中断源、中断处理程序和中断服务程序。以下将依据这几个关键部分对S5PV210的中断进行说明。
外部中断源的配置:
1. 如图的最右,首先应该配置GPIO引脚的相应寄存器为外部中断源模式。
2. 然后需要配置外部中断控制器EXT_C,共有2个寄存器,分别为控制寄存器和中断掩码。
3. 配置中断向量控制器VIC,此处共需要配置3个寄存器,S5PV210共有VIC[0-3]四个中断向量控制器。
内部中断源的配置:
1. 内部中断不经过外部中断控制器,但仍需要经过中断向量控制器VIC,S5PV210共有VIC[0-3]四个中断向量控制器。
到此为止,外部中断信号终于可以送到A8处理器中了。
程序状态寄存器的配置:
不管是外部中断还是内部中断,都需要配置程序状态寄存器CPSR,把IRQ模式打开,才能正常触发中断。关于CPSR如下图所示:
中断处理程序的配置:
需要把中断处理程序存放到iRAM中去,中断发生后SOC芯片会根据厂家的定义自己跳转到这里。中断处理程序包括两部分,进入中断部分和退出中断部分。其功能应该包括:
中断服务程序的配置:
这个由程序员自己定义。
需要把中断服务程序的地址存放到对应的中断向量地址寄存器当中,这个在芯片的Datasheet中可以查到。例如:VIC0的第16个中断的处理程序,就放置在VIC0ADDRESS16这个寄存器当中。当发生中断时,这个寄存器里面的值会自动装载到VIC0ADDRESS这个寄存器中,在中断处理程序中取出这个寄存器的值然后跳转到中断服务程序中去。
------------------------------------------------------------------------------------------------------
ARM的处理器模式介绍
了解ARM处理器的模式,有助于我们更深入地了解中断。ARM共有以下7种工作模式,本次讲解使用的是IRQ模式。
ARM处理器有7种不同的处理器模式,在每一种处理器模式中有一组相应的寄存器组。任意时刻(也就是任意的处理器模式下),可见的寄存器包括15个通用寄存器(R0~R14)、一个或两个状态寄存器及程序计数器(PC)。
在所有的寄存器中,有些是各模式共用的同一个物理寄存器,有些是各模式自己拥有的独立的物理寄存器,如图所示。
我们从图中可以看出普通用户模式(Systen and User)、超级用户模式(Supervisor)、和普通中断模式(FIQ)的R0-R12寄存器是共用的,这样也就不难理解为何要在中断处理程序中把这13个寄存器和LR(R14)寄存器保存到内存栈中了。
对于FIQ模式的R8~R14寄存器是独有的,FIQ处理程序可以不必执行保存和恢复中断现场的指令,从而使中断处理过程非常迅速。
几个特殊的通用寄存器:
R13
每一种异常模式拥有自己的物理的R13。应用程序初始化该R13,使其指向该异常模式专用的栈地址。当进入异常模式时,可以将需要使用的寄存器保存在R13所指的栈中;当退出异常处理程序时,将保存在R13所指的栈中的寄存器值弹出。这样就使异常处理程序不会破坏被其中断程序的运行现场。
R14
R14又被称为连接寄存器(Link Register,LR),在ARM体系中具有下面两种殊的作用:
第1种:
每一种处理器模式自己的物理R14中存放当前子程序的返回地址。当通过BL或BLX指令调用子程序时,R14被设置成该子程序的返回地址。在子程序中,当把R14的值复制到程序计数器PC中时,子程序即返回。
第2种:
当异常中断发生时,该异常模式特定的物理R14被设置成该异常模式将要返回的地址,对于有些异常模式,R14的值可能与将返回的地址有一个常数的偏移量。具体的返回方式与子程序返回方式基本相同。
R15
R15用于程序计数器(PC),保存了当前的程序跑到哪里。
中断触发时到底发生了什么?
首先处理器会回到ARM公司定义好的相应的异常地址去。
然后根据该地址中的内容进行下一步跳转,这个跳转地址是ARM公司定义好的,半导体厂商可以把这个地址设计为一个iROM或iRAM的地址,继续进行下一步跳转;也可以直接把这个地址设计为内存DRAM的地址。
S5PV210这块芯片三星公司把IRQ跳转的地址设置为iROM的地址,处理器会根据三星公司固化好的iROM程序,跳转到相应的iRAM地址,三星公司在这个位置又定义了一个异常向量表,程序员编写好的中断处理代码就存放在这里,然后根据中断处理代码的逻辑进行下一步跳转,最后到达中断服务程序中。
最后附上一段中断处理程序的代码:
.text
.extern uart_init
.extern printf
.extern Start_Arm
.global _start
_start:
mov r10,lr @把程序的入口放到R10寄存器中
bl uart_init @跳到串口初始化程序中,初始化串口主要是方便调试
ldr r0,=fmt1 @初始化好之后打印一句话,这些都无关重要
bl printf
bl cpsr_init
bl vector_table_init
bl Start_Arm
cpsr_init: @设置cpsr寄存器
mrs r0,cpsr
bic r0,r0,#0x80
msr cpsr_c,r0
bx lr
vector_table_init: @初始化iRAM中的异常向量表,把中断处理程序放到这个表中
ldr r0,=handle_irq
ldr r1,=0xd0037418
str r0,[r1]
bx lr
handle_irq: @中断处理程序
sub lr,lr,#4
stmfd sp!,{r0-r12,lr}
ldr lr,=return_irq
ldr r1,=0xf2000000 @VIC0IRQSTATUS
ldr r2,[r1]
cmp r2,#0
beq return_irq
ldr r1,=0xf2000f00 @VIC0ADDRESS
ldr pc,[r1] @跳转到中断服务程序当中
return_irq:
ldmfd sp!,{r0-r12,pc}^
fmt1:
.asciz ">>> welcome to MyCode! <<<\n"
.end