2440 中断过程分析 (MDK 自带启动代码实现中断)

时间:2021-12-08 17:51:55

   最近一直想在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下一中断就跑飞了!!

定义的方法见图:

2440 中断过程分析 (MDK 自带启动代码实现中断)

做这样修改之后程序就不会跑飞了. 

   这段代码将片上RAM重映射到0x0地址。那么什么情况需要做重映射呢?我们知道CPU启动是从0x0开始的, 也就是异常向量表的起始地址。但是当我们调试

程序的时候,通常是将程序放在片上的SRAM中,在 MINI2440中,片上SRAM的地址是从0x300000开始的(片上SRAM的初始化文件会把0x300000直接赋给

PC)。 当调试的程序需要进入中断的时候,PC会自动跳转到异常向量表去,但是实际上物理地址0x0上并没有异 常向量表,所以需要重映射,将片上RAM重映射到

0x0。否则如果调试过程中产生中断,PC跳到0x0地址找 不到异常向量表,程序就会跑飞.(这里引用自AT91SAM9261启动代码分析) 如果你的程序本身写的没问题

应该是没有问题的.

  总结这段时间的学习,网上直接关于MDK下用自带启动代码实现中断的几乎是没有,资料相当难找!有的说自带的启动代码有问题(我目前没发现启动代码有什么问题),需要修改,通过修改自带启动代码实现中断,有的是自己写启动代码.有人发现是 MMU的问题,这个问题也正是问题的所在.有需要工程文件的可以留言 ,也可以发页面上的邮箱.

博文为本人所写,转载请表明出处2440 中断过程分析 (MDK 自带启动代码实现中断)!!  .