分析2440中的中断处理部分

发布者:郑大大最新更新时间:2016-11-27 来源: eefocus关键字:2440中  中断处理 手机看文章 扫描二维码
随时随地手机看文章

这个 2440test里面的中断写的向量有些隐蔽,兜了很多个圈,也难怪这么难理解,下面

就对这个东西抽丝剥茧,看清楚这究竟是一个怎么样的过程。

中断向量

    b    HandlerIRQ    ;handler for IRQ interrupt

很自然,因为所有的单片机都是那样,中断向量一般放在开头,用过单片机的人都会很熟悉

那就不多说了。

异常服务程序

这里不用中断(interrupt)而用异常(exception),毕竟中断只是异常的一种情况,呵呵

下面主要分析的是“中断异常”说白了,就是我们平时单片机里面用的中断!!!所有有器件

引起的中断,例如TIMER中断,UART中断,外部中断等等,都有一个统一的入口,那就是中断

异常 IRQ ! 然后从IRQ的服务函数里面分辨出,当前究竟是什么中断,再跳转到相应的中断

服务程序。这样看来,ARM比单片机要复杂一些了,不过原理是不变的。

上面说的就是思路,跟着这个思路来接着分析。

HandlerIRQ 很明显是一个标号,我们找到了

HandlerIRQ HANDLER HandleIRQ

这里是一个宏定义,我们再找到这个宏,看他是怎么定义的:

MACRO

$HandlerLabel HANDLER $HandleLabel

$HandlerLabel

    sub    sp,sp,#4     ;decrement sp(to store jump address)

    stmfd    sp!,{r0}     ;PUSH the work register to stack(lr does not push because it return to original

address)

    ldr r0,=$HandleLabel ;load the address of HandleXXX to r0

    ldr r0,[r0]     ;load the contents(service routine start address) of HandleXXX

    str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack

    ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)

MEND

用 HandlerIRQ 将这个宏展开之后得到的结果实际是这样的

HandlerIRQ

    sub    sp,sp,#4     ;decrement sp(to store jump address)

    stmfd    sp!,{r0}     ;PUSH the work register to stack(lr does not push because it return to original

address)

    ldr r0,=HandleIRQ ;load the address of HandleXXX to r0

    ldr r0,[r0]     ;load the contents(service routine start address) of HandleXXX

    str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack

    ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)

至于具体的跳转原理下面再说    

好了,这样的话就容易看的多了,很明显,    HandlerIRQ 还是一个标号,IRQ异常向量就是跳

转到这里执行的,这里粗略看一下,应该是保存现场,然后跳转到真正的处理函数,那么很容易

发现了这么一句 ldr r0,=HandleIRQ ,没错,我们又找到了一个标号 HandleIRQ ,看来

真正的处理函数应该是这个 HandleIRQ ,继续寻找

    AREA RamData, DATA, READWRITE

    ^ _ISR_STARTADDRESS        ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset     # 4

HandleUndef     # 4

HandleSWI        # 4

HandlePabort # 4

HandleDabort # 4

HandleReserved # 4

HandleIRQ        # 4

最后我们发现在这里找到了 HandleIRQ ,^ 其实就是 MAP ,这段程序的意思是,从 _ISR_STARTADDRESS

开始,预留一个变量,每个变量一个标号,预留的空间为 4个字节,也就是 32BIT,其实这里放的是真正

的C写的处理函数的地址,说白了,就是函数指针 - -

这样做的话就很灵活了

    

接着,我们需要安装IRQ处理句柄,说白了,就是设置处理函数的地址,让PC指针可以正确的跳转。

于是我们在接着的找到安装句柄的语句

    

      ; Setup IRQ handler

    ldr    r0,=HandleIRQ ;This routine is needed

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

    str    r1,[r0]

说白了就是将 IsrIRQ 的地址填到 HandleIRQ对应的地址里面,前面说了 HandleIRQ 放的是中断处理的

函数的入口地址,我们继续找 IsrIRQ

IsrIRQ

    sub    sp,sp,#4 ;reserved for PC

    stmfd    sp!,{r8-r9}

    ldr    r9,=INTOFFSET

    ldr    r9,[r9]                     ;读入中断偏移码

    ldr    r8,=HandleEINT0     ;二级跳转表的首地址

    add    r8,r8,r9,lsl #2          ;R8=R8+R9X4得到相应的中断入口地址

    ldr    r8,[r8]

    str    r8,[sp,#8]                ;中断入口地址送进SP(第一个代码留出的4字节空间)

    ldmfd    sp!,{r8-r9,pc}

    

要理解这个代码,得先学学2440的中断系统了,INTOFFSET存放的是当前中断的偏移号,根据偏移就知道

当前是哪个中断源发生的中断。

注意了,我们说的是中断,而不是异常,看看原来的表是啥样子的

    ^ _ISR_STARTADDRESS        ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset     # 4

HandleUndef     # 4

HandleSWI        # 4

HandlePabort # 4

HandleDabort # 4

HandleReserved # 4

HandleIRQ        # 4

HandleFIQ        # 4

HandleEINT0        # 4

HandleEINT1        # 4

HandleEINT2        # 4

HandleEINT3        # 4

.......

可以看到,前面几个是异常,从     HandleEINT0 就是 IRQ异常的向量存放的地方了,这样就可以理解为

什么上面 IsrIRQ 里面里面要执行那条指令

    ldr    r8,=HandleEINT0

    add    r8,r8,r9,lsl #2

道理很简单, HandleEINT0 就是所有IRQ中断向量表的入口,在这个地址上面,加上一个适当的偏移量,

INTOFFSET ,那么我们知道现在,到底是哪个IRQ在申请中断了。

至于具体怎么跳转的?

首先,我们说了,HandleEINT0 开始的一段内存里面,存放的就是中断服务函数的函数指针,ARM的体系

的话,每个指针变量就是占4个字节,这里就解释了,为什么这里为每个标号分配了4个字节的空间,里面

放的就是函数指针!!!下面再看看怎么跳转,继续看 IsrIRQ 里面就实现了跳转了

    str        r8,[sp,#8]

    ldmfd    sp!,{r8-r9,pc}

其实最核心就是这两句了,先查找到当前中断服务程序的地址,将他放到 R8 里面,然后出栈,弹出给PC

那么PC很自然就跳到中断服务程序了。至于这里的堆栈问题又是一个非常棘手的,需要好好的参透ARM的

中断架构,需要了解的可以自己仔细的阅读 《ARM体系结构与编程》里面说的很详细。我们这里的重点

是研究怎么跳转。

最后,我们看看在C代码中是怎么安装终端向量的,例如看 按键的外部中断,是怎么具体设置的,参看

/src/keyscan.c 里面的代码

很简单,里面只有3个函数

KeyScan_Test 是按键测试的主函数

Key_ISR 是按键中断服务函数

在 KeyScan_Test里面,我们发现了有这么一句

    pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;

可以理解否? Key_ISR就是上面提到的按键中断服务函数,函数的名字,代表的就是函数的地址!!!!

将中断服务函数的地址,注意了,是地址,这是一个 U32型的变量。送到几个变量,我们以pISR_EINT0

作为例子,查看头文件定义,在 2440addr.h 里面找到

// Interrupt vector

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

_ISR_STARTADDRESS有没有似曾相识的感觉?没错,刚才分析的汇编代码里面就提到了

    ^ _ISR_STARTADDRESS        ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset     # 4

HandleUndef     # 4

......

对,地址就是这里,然后 _ISR_STARTADDRESS+0x20 就是跳过前面的异常向量,进入IRQ中断向量的入口

所以说到尾

    pISR_EINT0 = (U32)Key_ISR;

完成的操作就是,将 Key_ISR 的地址存放到

HandleEINT0        # 4

这个IRQ向量表里面!!!!

当按键中断发生的时候,发生IRQ异常中断

当前PC值-4 保存到LR_IRQ里面,然后执行

b    HandlerIRQ

然后是执行

HandlerIRQ

    sub    sp,sp,#4     ; 预留一个用来存放PC地址

    stmfd    sp!,{r0}     ; 保存R0,因为下面使用了

    ldr r0,=HandleIRQ ; 将HandleIRQ(服务程序)的地址装载到R0

    ldr r0,[r0]

    str r0,[sp,#4] ; 保存到刚才预留的地方

    ldmfd sp!,{r0,pc} ; 弹出堆栈,恢复R0,并且将刚才计算好的 HandleIRQ 地址弹出到 PC

堆栈是向下生长的,所以 SUB SP,SP,#4 就相当于 PUSH XX,但是这个XX这个时候并没有用,因为这里

用的是强制移动 SP 指针实现的。然后得到服务程序的地址,再将这个值放回刚才预留的栈的空位上面,最后

就是POP出R0恢复,并且将刚才得到的服务程序的地址送到 PC,那么实现的效果就是跳转到 HandleIRQ 里面了。

接着看刚才是怎么安装的HandleIRQ 的

      ; Setup IRQ handler

    ldr    r0,=HandleIRQ ;This routine is needed

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

    str    r1,[r0]

可以看出,这里将 IsrIRQ 的地址的值保存到 HandleIRQ 中,也就是说,上面的 IRQ 服务程序,这个时候实际

上就是指 IsrIRQ !

所以接着的事情就是转移到 IsrIRQ 中执行:

IsrIRQ

    sub    sp,sp,#4 ; 预留一个值来保存PC

    stmfd    sp!,{r8-r9}

    ldr    r9,=INTOFFSET ; 计算偏移量,下面解释

    ldr    r9,[r9]

    ldr    r8,=HandleEINT0

    add    r8,r8,r9,lsl #2

    ldr    r8,[r8]

    str    r8,[sp,#8] ; 因为保存了2个寄存器R8 R9 ,所以SP下移了8位

    ldmfd    sp!,{r8-r9,pc} ; 恢复寄存器,弹出到PC,同上面的一样

怎么保存,操作SP,跟最后弹出到PC的部分和上面的例子一样,下面说说中间的计算部分

计算偏移量,其实原理很简单,首先 INTOFFSET 保存着当前是哪个IRQ中断,例如 0代表着 HandleEINT0,1代表

HandleEINT1 ..... 等等,这不是乱来,有一个表的,这个是由 S3C2440 的datasheet说的,自己可以去查看。

然后得到 中断处理函数的向量表,这个表的首地址就是 HandleEINT0,那么很自然的想到,怎么查表?那还不简

单?HandleEINT0 + INTOFFSET 不就完了?基地址加偏移量就得到表中某项了,当然,因为这里是中断处理向量

每一项占用4个字节,所以用lsl #2处理一下,左移2位相当于乘以4,偏移量乘以4,这应该很好理解的。

我们这个例子找到的就是 HandleEINT0 ,将里面的值读出来,里面放的是 HandleEINT0 服务函数的地址,这个

地址怎么来的?是在C程序里面设置的。我们看 keyscan.c 程序,找到一个 void KeyScan_Test(void) 函数,

里面有这么一句:

pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;

这里是安装了3个按键中断服务程序,我们只关注 0号中断,也就是

pISR_EINT0 = (U32)Key_ISR;

这句话什么意思?先看看pISR_EINT0的定义,在 2440addr.h 中定义

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

看到没有?_ISR_STARTADDRESS 不就是刚才说的那个异常向量的入口地址?加上一个 0x20

之后实际上指向的,就是 HandleEINT0 !!!这么说来,上面的意思就是,将 Key_ISR 处理函数的入口地址,送

到 HandleEINT0 中。

再来看 Key_ISR ,这是一个典型的服务程序,加了_irq 作为编译关键字,告诉编译器,这个函数是中断服务程序

得保存需要的寄存器,免得被破坏。具体可以参考 《ARM体系结构与编程》P283 页的描述。

static void __irq Key_ISR(void)

{

    .......

}

加上 _irq 关键字之后,编译器就会处理好所有的保存动作了,并不需要多关心。但是这个是 ARM-CC 编译器的关

键字,GCC中并没有这个东西,所以GCC处理中断的时候最好还是自己保存一下。

到这里为止,整个中断的过程就解释完毕。分析的过程中确实学习了很多。


关键字:2440中  中断处理 引用地址:分析2440中的中断处理部分

上一篇:2440开发板启动代码学习
下一篇:arm2410和44b0启动文件分析

推荐阅读最新更新时间:2024-03-16 15:22

ATmega48 复位与中断处理
AVR有不同的中断源。每个中断和复位在程序空间都有独立的中断向量。所有的中断事件都有自己的使能位。当使能位置位,且状态寄存器的全局中断使能位I 也置位时,中断可以发生。根据程序计数器PC 的不同,在引导锁定位BLB02 或BLB12 被编程的情况下,中断可能被自动禁止。这个特性提高了软件的安全性。详见 P254“ 存储器编程” 的描述。 程序存储区的最低地址缺省为复位向量和中断向量。完整的向量列表请参见 P47“中断”。列表也决定了不同中断的优先级。向量所在的地址越低,优先级越高。RESET 具有最高的优先级,第二个为INT0 – 外部中断请求0。通过置位MCU 控制寄存器 (MCUCR) 的IVSEL,中断向量可以移至引导Fla
[单片机]
嵌入式ARM系统异常和中断处理知识总结
关于异常处理,分为三部分: 1. ARM异常和模式:core处理异常时的操作,几种模式介绍。 2. Vector table: 3. 异常优先级 4. lr偏移:几种异常如何返回 异常和中断处理简介 在嵌入式系统中异常处理是核心之一。高效的处理能够极大的提升系统的性能。 ARM处理器一共有7种可以暂停指令的执行序列的异常。 主要分为三个部分: 点击这里 1小时彻底掌握中断 创客学院带你搞定异常和中断处理 1. Exception handling 2. Interrupts 3. Interrupt handling schemes 今天我们主要介绍第一部分 Exception Handling 1.ARM Pro
[单片机]
2440test按键的分析
这里分析了4个按键,还有 ENIT8 和 EINT19没有,不过原理一样的。 4个按键,分别是 EINT11 / GPG3 EINT13 / GPG5 EINT14 / GPG6 EINT15 / GPG7 外部上拉电阻。 EINT 8-23 共用一个IRQ向量。 初始化步骤 1,设置IO的功能,00 输入 01输出 02 第二功能 P292 设置的方法是 rGPGCON &= ~(3 3*2 | 3 5*2 | 3 6*2 | 3 7*2); rGPGCON |= (2 3*2 | 2 5*2 | 2 6*2 | 2 7*2); 2,设置 EXTINT 系列寄存器,设定中断触发的类型 P301 其中这次用到的 EIN
[单片机]
MPC860的中断处理技术研究
摘要:MPC860是网络通信设备中应用最广的一款RISC嵌入式处理器。本文介绍MPC860的中断体系结果及中断发生后服务程序的处理流程;以SMC1的接收中断为例,阐述在设计操作系统管理的条件下,中断初始化程序和中断服务程序的编写。 关键词:嵌入式处理器 MPC860 中断体系结构 中断处理技术 引言 Motorola公司推出的MPC860 PowerQUICC是目前在通信领域应用得非常广泛的一款嵌入式处理器,被誉为MC68360 PowerQUICC在网络和数据通信领域的新一代产品。与MC68360相比,MPC860 PowerQUICC在各方面的性能,包括器件的适应性、外部扩展能力和芯片集成度等都得到了提高。 MPC8
[嵌入式]
ATmega32 中断向量
本节描述ATmega32的中断处理。更一般的AVR中断处理请参见P11“复位与中断处理” 。 (点击图片放大) Notes: 1. 熔丝位BOOTRST被编程时,MCU复位后程序跳转到Boot Loader。请参见 P228 “支 持引导装入程序 – 在写的同时可以读(RWW, Read-While-Write)的自我编程能力 ” 。 2. 当寄存器GICR的IVSEL置位时,中断向量转移到Boot区的起始地址。此时各个中断向 量的实际地址为表中地址与Boot 区起始地址之和。 Table 19给出了不同的BOOTRST/IVSEL设置下的复位和中断向量的位置。如果程序永远不使能中断,中断向量就没有意义。用户可以在此直接写
[单片机]
ATmega32 中断向量
STM32的timers中断处理函数
1.在固件库函数里面,用来读取中断状态寄存器的值判断中断类型的函数是: ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t) 作用:判断定时器TIMx的中断类型TIM_IT是否发生中断。 比如,我们要判断定时器3是否发生更新(溢出)中断,方法为: if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { } 2.固件库中清除中断标志位的函数是: void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT) 作用:清除定时器TIMx的中断TIM_IT标志位。 比如我们
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved