最近一直想在MDK下利用自带启动代码实现中断,所以就硬着头皮看了看 ADS下的初始化代码 .现在我来分析一下ADS下的IRQ中断过程,这一部分
主要是参考百度文库,当2440发生中断时就会自动跳到这里自动执行:
ResetEntry ;1)The code, which converts to Big-endian, should be in little endian code. ;2)The following little endian code will be compiled in Big-Endian mode. ; The code byte order should be changed as the memory bus width. ;3)The pseudo instruction,DCD can not be used here because the linker generates error. ASSERT :DEF:ENDIAN_CHANGE [ ENDIAN_CHANGE ASSERT :DEF:ENTRY_BUS_WIDTH [ ENTRY_BUS_WIDTH=32 b ChangeBigEndian ;DCD 0xea000007 ] [ ENTRY_BUS_WIDTH=16 andeq r14,r7,r0,lsl #20 ;DCD 0x0007ea00 ] [ ENTRY_BUS_WIDTH=8 streq r0,[r0,-r10,ror #1] ;DCD 0x070000ea ] | b ResetHandler ] b HandlerUndef ;handler for Undefined mode b HandlerSWI ;handler for SWI interrupt b HandlerPabort ;handler for PAbort b HandlerDabort ;handler for DAbort b . ;reserved b HandlerIRQ ;handler for IRQ interrupt b HandlerFIQ ;handler for FIQ interrupt
如果是IRQ中断,则会跳到HandlerIRQ 这个标识符 .我们找到了HandlerIRQHANDLER 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
这里把 HandlerLabel 换成 HandlerIRQ及为展开过程 . 这里的过程是保存现现场,然后跳转到真正的处理函数 .真正的处理函数是放在HandlerIRQ中的(替换之后)
,我们又找到
AREA RamData, DATA, READWRITE ^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00 HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4 ;Do not use the label 'IntVectorTable', ;The value of IntVectorTable is different with the address you think it may be. ;IntVectorTable ;@0x33FF_FF20 HandleEINT0 # 4 HandleEINT1 # 4 HandleEINT2 # 4 HandleEINT3 # 4 HandleEINT4_7 # 4 HandleEINT8_23 # 4 HandleCAM # 4 ; Added for 2440. HandleBATFLT # 4 HandleTICK # 4 HandleWDT # 4 HandleTIMER0 # 4 HandleTIMER1 # 4 HandleTIMER2 # 4 HandleTIMER3 # 4 HandleTIMER4 # 4 HandleUART2 # 4 ;@0x33FF_FF60 HandleLCD # 4 HandleDMA0 # 4 HandleDMA1 # 4 HandleDMA2 # 4 HandleDMA3 # 4 HandleMMC # 4 HandleSPI0 # 4 HandleUART1 # 4 HandleNFCON # 4 ; Added for 2440. HandleUSBD # 4 HandleUSBH # 4 HandleIIC # 4 HandleUART0 # 4 HandleSPI1 # 4 HandleRTC # 4 HandleADC # 4 ;@0x33FF_FFA0 END
这段代码的意思是 从_ISR_STARTADDRESS 开始预留一个变量,每个变量一个标号,预留空间为4字节 .也就是函数指针 .
现在想想 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 指针所指向的内存,也就是说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+r9*4;lsl是移位运算 ldr r8,[r8] ;取r8地址中的数据给 r8 .r8实际程序运行对应的地址 str r8,[sp,#8] ;入栈 ldmfd sp!,{r8-r9,pc} ;数据出栈,放入r8~r9 ,PC寄存器 .满递减堆栈 LTORG
这段代码的意思是 HandleEINT0 加上 INTOFFSET 就是真正定义 的中断函数的地址,如果这里不太清楚,需要先了解 2440的中断系统 .
最后是在C代码中安装中断向量 ,如 pISR_EINT8_23= (U32)IRQ_key_eint; IRQ_key_eint是定义的函数名,pISR_EINT8_23是定义的一个地址
#define pISR_EINT8_23 (*(unsigned *)(_ISR_STARTADDRESS+0x34)) .总结来说 就是把 IRQ_key_eint的地址放在
HandleEINT8_23中. 我专们把手里的程序调试了一下,当中断产生时 HandleEINT0 中的地址为 0x33FFFF20 ,INTOFFSET 为0x05 即
5*4=0x14; 所以获得的地址为 0x30FFFF20+0x14=0x30FFFF34 为pISR_EINT8_23 之前定义的地址. 这个地址中存的数据为我们所编写的中断函数
的地址 0x30000CE8 .
ADS的中断过程代码分析完了,下面进行一下MDK下中断过程的分析 ,可以先看看MDK下的的启动代码S3C2440.S .在启动代码里我们找到这一段
; Copy Exception Vectors to Internal RAM --------------------------------------- IF :DEF:RAM_INTVEC ADR R8, Vectors ; Source LDR R9, =IRAM_BASE ; Destination LDMIA R8!, {R0-R7} ; Load Vectors STMIA R9!, {R0-R7} ; Store Vectors LDMIA R8!, {R0-R7} ; Load Handler Addresses STMIA R9!, {R0-R7} ; Store Handler Addresses ENDIF
这里的意思是 如何定义了RAM_INTVEC ,则表中断向量表 拷贝到内部RAM ,之前一直没有注意到这个,从而导致在MDK下一中断就跑飞了!!
定义的方法见图:
做这样修改之后程序就不会跑飞了.
这段代码将片上RAM重映射到0x0地址。那么什么情况需要做重映射呢?我们知道CPU启动是从0x0开始的, 也就是异常向量表的起始地址。但是当我们调试
程序的时候,通常是将程序放在片上的SRAM中,在 MINI2440中,片上SRAM的地址是从0x300000开始的(片上SRAM的初始化文件会把0x300000直接赋给
PC)。 当调试的程序需要进入中断的时候,PC会自动跳转到异常向量表去,但是实际上物理地址0x0上并没有异 常向量表,所以需要重映射,将片上RAM重映射到
0x0。否则如果调试过程中产生中断,PC跳到0x0地址找 不到异常向量表,程序就会跑飞.(这里引用自AT91SAM9261启动代码分析) 如果你的程序本身写的没问题
应该是没有问题的.
总结这段时间的学习,网上直接关于MDK下用自带启动代码实现中断的几乎是没有,资料相当难找!有的说自带的启动代码有问题(我目前没发现启动代码有什么问题),需要修改,通过修改自带启动代码实现中断,有的是自己写启动代码.有人发现是 MMU的问题,这个问题也正是问题的所在.有需要工程文件的可以留言 ,也可以发页面上的邮箱.
博文为本人所写,转载请表明出处2440 中断过程分析 (MDK 自带启动代码实现中断)!! .