arm中断保护和恢复_ARM中断(二)

本文详细介绍了S3C2440处理器如何处理IRQ和FIQ中断,包括两次查表过程、中断处理流程,以及如何通过中断向量表和中断处理函数之间的映射关系实现中断的跳转。通过对中断处理宏定义和C语言中断函数安装的分析,揭示了中断保护和恢复的具体步骤。
摘要由CSDN通过智能技术生成

本文感谢 郑星 朋友

2440支持IRQ(普通中断)和FIQ(快速中断)。2440有60个中断源,不支持中断嵌套。

CPU每执行一条指令都会检查CPSR寄存器,当发现I或F位被置1时,就进行中断处理。需要两次查表过程(为什么要查两次表??没有办法,ARM把所有的中断都归纳成一个IRQ中断异常和一个FIQ中断异常;第一次查表主要是查出是什么异常,可我们总要知道是这个中断异常中的什么中断呀!没办法还需要查第二次)。第一步跳入异常向量表:

地址

异常名称

指令

0x00

复位异常

B  RestHandler

0x04

未定义指令异常

B  HandlerUndef

0x08

软件中断异常

B  HandlerSWI

0x0C

指令预取异常

B  HandlerPabort

0x10

数据预取异常

B  HandlerDabort

0x14

保留

0x18

IRQ中断异常

B  HandlerIRQ

0x1C

FIQ中断异常

B  HandlerFIQ

如果S3C2440刚上电或者是复位,那么pc指针被硬件强制转换到0x00地址处,那么按照2440init.s中的指令“B  ResetHandler”,pc会跳转到ResetHandler处继续执行程序。

我们以外部中断为例,当发生外部中断后,PC指针指向0x18,执行B  HandlerIRQ 指令,接着跳转到HandlerIRQ标号处执行,S3C2440有很多的中断源,所以不可能把中断函数的地址直接赋给HandlerIRQ。这中间应该还有一个转换。就是根据不同的、具体的中断源,HandlerIRQ对应于不同的中断处理函数的地址。那么接下来看看HandlerIRQ标号后面的内容吧:

HandlerIRQ HANDLER HandleIRQ

很明显,这里还有一个宏定义在里面,要知道HANDLER的内容,我们可以在2440init.s中查到:

;下面这个宏是用于第一次查表过程的实现中断向量的重定向,如果你比较细心的话就是发现

;在_ISR_STARTADDRESS=0x33FF_FF00里定义的第一级中断向量表是采用型如Handle***的方式的.

;而在程序的ENTRY处(程序开始处)采用的是b Handler***的方式.

;在这里Handler***就是通过HANDLER这个宏和Handle***建立联系的.

;这种方式的优点就是正真定义的向量数据在内存空间里,而不是在ENTRY处的ROM(FLASH)空间里,

;这样,我们就可以在程序里灵活的改动向量的数据了.

;=======================================================

;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。

;本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。每个字

;空间都有一个标号,以Handle***命名。

;在向量中断模式下使用“加载程序”来执行中断服务程序。

;这里就必须讲一下向量中断模式和非向量中断模式的概念

;向量中断模式是当cpu读取位于0x18处的IRQ中断指令的时候,系统自动读取对应于该中断源确定地址上的;

;指令取代0x18处的指令,通过跳转指令系统就直接跳转到对应地址

;函数中 节省了中断处理时间提高了中断处理速度标 例如 ADC中断的向量地址为0xC0,则在0xC0处放如下

;代码:ldr PC,=HandlerADC 当ADC中断产生的时候系统会

;自动跳转到HandlerADC函数中

;非向量中断模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将interrupt

;pending寄存器中对应标志位置位 然后跳转到位于0x18处的统一中断

;函数中 该函数通过读取interrupt pending寄存器中对应标志位 来判断中断源 并根据优先级关系再跳到

;对应中断源的处理代码中

MACRO ;宏定义的开始

$HandlerLabel HANDLER $HandleLabel

$HandlerLabel ;标号

sub sp,sp,#4 ;(1)减少sp(用于存放转跳地址)

stmfd sp!,{r0} ;(2)把工作寄存器压入栈(lr does not push because it returnto original address)

ldr r0,=$HandleLabel ;将HandleXXX的址址放入r0

ldr r0,[r0] ;把HandleXXX所指向的内容(也就是中断程序的入口)放入r0

str r0,[sp,#4] ;(3)把中断服务程序(ISR)压入栈

ldmfd sp!,{r0,pc} ;(4)用出栈的方式恢复r0的原值和为pc设定新值(也就完成了到ISR的转跳)

MEND ;宏定义的结束

这个宏的作用其实就是在不改变任何寄存器的前提下,把pc指针指向$HandleLabel。这里是将PC指针直接由HandlerIRQ指向HandleIRQ。(在这里Handler***就是通过HANDLER这个宏和Handle***建立联系的.

;这种方式的优点就是正真定义的向量数据在内存空间里,而不是在ENTRY处的ROM(FLASH)空间里,

;这样,我们就可以在程序里灵活的改动向量的数据了.)

下面我们就得关注一下HandleIRQ这个标号了。它在2440init.s中设这样定义的:

^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset #4HandleUndef #4HandleSWI #4HandlePabort #4HandleDabort #4HandleReserved #4HandleIRQ #4HandleFIQ #4………

(其中 “^”表示MAP指令,“#”表示FIELD指令)

很简单,这里定义了一个内存地址块,首地址:_ISR_STARTADDRESS代表了地址为0x33FF_FF00的内存区域,每隔4个字节,定义一个标号。很很容易就找到了 HandleIRQ这个我们需要找的标号。那么它所代表的内存区域自然就是0x33FF_FF00+0x04*6的内存地址了(跳到此地址执行,这个过程由硬件自动完成)。那么接下来的工作就是要把真正的、具体的中断处理函数的地址赋给HandleIRQ了。这里大家先看一下下面的两端代码:

1):

; 进入C语言前的最后一步了,就是把我们用说查二级向量表

; 的中断例程安装到一级向量表(异常向量表)里.

ldr r0,=HandleIRQ;This routine isneeded

ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c

str r1,[r0]

(2):

这一段程序就是用来进行第二次查表的过程了.

;如果说第一次查表是由硬件来完成的,那这一次查表就是由软件来实现的了.

IsrIRQ

sub sp,sp,#4 ;给PC寄存器保留 reserved forPC

stmfd sp!,{r8-r9} ;把r8-r9压入栈

ldr r9,=INTOFFSET ;把INTOFFSET的地址装入r9 INTOFFSET是一个内部的寄存器,存着中断的偏移

ldr r9,[r9] ;I_ISR

ldr r8,=HandleEINT0 ;这就是我们第二个中断向量表的入口的,先装入r8

;===================================================================================

;哈哈,这查表方法够好了吧,r8(入口)+index*4(别望了一条指令是4 bytes的喔),

;这不就是我们要找的那一项了吗.找到了表项,下一步做什么?肯定先装入了!

;==================================================================================add r8,r8,r9,lsl #2 ;地址对齐,因为每个中断向量占4个字节,即isr = IvectTable + Offeset * 4ldr r8,[r8] ;装入中断服务程序的入口

str r8,[sp,#8] ;把入口也入栈,准备用旧招

ldmfd sp!,{r8-r9,pc};施招,弹出栈,哈哈,顺便把r8弹出到PC了,跳转成功!

首先我们来看一下第(1)段程序,前面已经提到,此时的的pc已经指向了HandeIRQ所表示的内存了,但是现在该内存还是空的,pc跳转到这里也不能接着往下运行了。所以才有了第1段代码,它的作用就是给HandleIRQ安装句柄了,把IsrIRQ的入口地址填充到了HandeIRQ里面了。所以程序接着就会跳转到IsrIRQ处执行。也就是上面的第(2)段程序了。这段程序具体讲解我就不说了(2次查表),跟最上面的宏定义很类似,其实就是让PC跳转到另外一个地方(pc=HandleEINT0+INTOFFSET*4)。而那个地方正是真正的中断函数。那我们再来看看这个地址是怎么算出来的。首先HandleEINT0就是上面那个MAP定义里面的一个内存区域,有没有发现它是第一个中断源,紧接着它,就是其它各种类型的中断源了。而INTOFFSET则是S3C2440的一个特殊功能寄存器了,某个类型的中断发生了,它的值就会发生变化。而后面为什么要乘以4呢,因为文字池中定义的标号都是4字节的,其实是因为S3C2440中指针变量就是占据4个字节的,这个可以用sizeof(*p)来验证。所以此时pc指针就指向了另外一个地方,那就是刚才说的中断表了。而在c语言中,我们通常会有这样的中断函数句柄安装语句:pISR_EINTn = (unsigned int )key_interrupt;

这里的pISR_EINTn其实是有定义的,我们以pISR_EINT0为例,宏定义如下:

#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20))

看这个地址,其实就是上面那个中断表里面的:

^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00

……0-7HandleEINT0 #4HandleEINT1 #4HandleEINT2 #4HandleEINT3 #4……

大家发现了吧,HandleEINT0的地址是不是就是 _ISR_STARTADDRESS+0X20,所以说c语言中我们写的中断函数安装句柄,就是这个作用。

好的,现在让我们来总结一下吧。(以外部中断0为例)

首先是在c语言的函数中,我们已经执行了这样一条语句pISR_EINTn = (unsigned int )key_interrupt;它的作用是把中断处理函数key_interrupt的地址赋给了中断表中的HandleEINT0。

然后当某个时刻,发生了外部中断0,那么pc指针被强制指向了0x18处,执行指令:

b HandlerIRQ

跳转到HandlerIRQ之后,执行如下代码:(已经把宏定义屏蔽)

HandlerIRQ

sub sp,sp,#4stmfd sp!,{r0}

ldr r0,=HandleIRQ

ldr r0,[r0]

str r0,[sp,#4]

ldmfd sp!,{r0,pc}

然后pc之争有指向了HandleIRQ这个内存区域。而又由于HandlerIRQ已经被安装了IsrIRQ的句柄,所以紧接着pc又跳转到IsrIRQ处执行如下程序:

IsrIRQ

sub sp,sp,#4 ;reserved forPC

stmfd sp!,{r8-r9}

ldr r9,=INTOFFSET

ldr r9,[r9]

ldr r8,=HandleEINT0

add r8,r8,r9,lsl #2ldr r8,[r8]

str r8,[sp,#8]

ldmfd sp!,{r8-r9,pc}

它的作用是让pc指针根据INTOFFSET的值跳转到中断向量表中的HandleEINT0处。而在此处已经被安装了c语言中的中断处理函数的句柄,所以pc又跳到了中断处理函数中区执行中断函数了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值