深入理解嵌入式Linux设备驱动程序
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 嵌入式ARM系统的中断系统

中断机制是计算机系统中一种非常重要的外部事件处理机制。中断机制是指在计算机执行当前任务的过程中,当外部事件发生时,计算机可以暂停当前正在执行的任务,转去处理外部事件,待外部事件处理完成后,继续恢复原来执行的任务。

ARM9采用二级中断机制。所谓二级中断,是指当外部中断产生时,系统需要通过两次跳转,才能执行真正的中断服务处理程序。下面以按键中断处理过程为例来详细说明ARM9系统中的中断处理过程。

按键使用外部中断EINT8、EINT11、EINT13、EINT14、EINT15、EINT19,它们公用一个外部中断源EINT8_23,中断号为5。

中断向量:中断服务程序的入口地址。

中断向量地址:内存中存放中断服务程序入口地址的地址。

1.3.1 ARM中断机制代码分析

外部中断产生后,要想正确执行相应的中断处理程序,首先需要在内存中建立中断向量表。系统中断向量表中存放中断服务处理程序的起始地址。系统启动代码中建立的中断向量的代码如下所示。

                                        ; interrupt service routing start address
                                        _ISR_STARTADDRESS         EQU      0x33ffff00
                                        ; ^ is same with MAP, the following also can be:
                                        ; MAP _ISR_STARTADDRESS for define interrupt service routing service
                                        ;^_ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00
                                        AREA RamData, DATA, READWRITE
                                        MAP      _ISR_STARTADDRESS
                                        HandleReset               #      4
                                        HandleUndef               #      4
                                        HandleSWI                 #      4
                                        HandlePabort              #      4
                                        HandleDabort              #      4
                                        HandleReserved            #      4
                                        HandleIRQ                 #      4
                                        HandleFIQ           #   4
                                        ;Don' t 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

MAP用于定义一个内存表的首地址,在这里用MAP定义的首地址为_ISR_STARTADDRESS,内存表长度为0xA0,给每一个中断源分配一个占4字节的内存单元,用来存放该中断对应的中断服务程序入口地址。其中标号HandleEINT8_23是存放EINT8_23中断服务程序的入口地址的地址;标号HandleIRQ表示存放外部中断异常处理程序的地址,即HandleIRQ本身是一个地址。在本例中,HandleIRQ的值为0x33FFFF18,内存地址0x33FFFF18开始的4字节的存储单元存放的是外部中断异常处理程序的地址。HandleEINT8_23的值为0x33FFFF34,内存地址为0x33FFFF34,开始的4字节的存储单元存放外部中断EINT8_23的中断服务程序地址。

前面提到过在系统的启动代码中定义了一个HANDLER宏,该宏的作用是把中断处理程序的入口地址加载到PC寄存器中。在接下来的代码中,使用HANDLER宏来定义外部中断异常处理程序的入口,代码如下所示。

宏展开后,代码如下所示。

                                        HandlerIRQ       HANDLER   HandleIRQ
                                        HandlerIRQ
                    sub                          sp, sp, #4              ; decrement sp(to store jump address)
                    stmfdsp! , {r0}                                      ; PUSH the work register to stack
                    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)

HandleIRQ为前面定义的一个地址,其值为0x33FFFF18,里面存放着外部中断异常处理程序的入口地址(即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
                    ldr   r8, [r8]
                    str   r8, [sp, #8]
                    ldmfd     sp! , {r8-r9, pc}

外部中断异常处理程序根据中断号获取该中断对应的中断服务处理程序地址,然后把该中断号对应的中断服务程序地址装载到计算机PC寄存器中,使得CPU转去执行该中断号对应的中断服务处理程序。

外部中断异常处理程序根据外部中断号(INTOFFSET)和外部中断向量首地址(HandleEINT0)来计算对应的中断的中断向量地址,然后从该中断向量地址中取出中断向量(即中断服务程序地址)装到计算机中,开始执行中断服务程序。

思考题

外部中断异常处理程序和宏HANDLER实现的功能差不多,那为什么不用HANDLER来定义外部中断异常处理程序呢?

原因在于HANDLER和IsrIRQ获取中断向量地址方式不一样,HANDLER直接获取中断向量地址,而IsrIRQ是通过中断向量基地址(HandleEINT0)和中断号(INTOFFSET)计算出该中断对应的中断向量地址的。

系统启动代码在初始化堆栈后,开始安装外部中断异常处理程序,代码如下所示。

              ; Setup IRQ handler
                  ;HandleIRQ is for save interrupt service routing address
                  ldr   r0, =HandleIRQ        ; This routine is needed
                  ldr   r1, =IsrIRQ   ; if there isn' t' subs pc, lr, #4' at 0x18,0x1c
                  str   r1, [r0]

安装外部中断异常的流程非常简单,即把外部中断异常处理程序IsrIRQ的地址存放到外部中断异常向量表的HandleIRQ表项中,HandleIRQ表示存放外部中断异常处理程序地址的地址。

安装了外部中断异常处理程序后,还要继续安装外部中断EINT8_23中断处理程序。外部中断EINT8_23的中断服务程序安装在C语言源文件中,其代码如下所示。

              #define pISR_EINT8_23    (*(unsigned*)(_ISR_STARTADDRESS+0x34))

              pISR_EINT8_23=(U32)KeyISR;// set up KeyBoard interrupt service routing

外部中断EINT8_23中断服务程序的安装非常简单,把外部中断EINT8_23中断服务程序的KeyISR的地址存放到外部中断向量表的EINT8_23表项中即可。

1.3.2 ARM中断服务处理程序的响应过程

分析完ARM中断服务处理的相关代码后,下面来看一下按键中断的处理响应过程。

按键的中断处理流程如下:

用户按下按键时,通过外部中断EINT8_23触发外部中断异常,计算机跳转到异常向量表的外部中断异常向量地址处执行。外部中断异常向量地址里只有一条跳转指令,于是CPU开始跳转到HandlerIRQ处执行外部中断异常处理程序。外部中断异常处理程序从中断向量表获取外部中断服务处理程序IsrIRQ的入口地址,开始执行外部中断服务处理程序IsrIRQ。IsrIRQ根据外部中断号EINT8_23和外部中断向量表首地址计算该中断号对应的中断向量地址,然后从该中断向量地址中取出该中断向量(即该中断服务程序的入口地址)装载到计算机寄存器中,于是开始执行真正的中断服务处理程序KeyISR。