AT91SAM9261中断处理过程解析

 

作者:杨硕,华清远见嵌入式学院讲师。

一、AT91SAM9261先进中断控制器(AIC)介绍

ARM核结构采用了一个简单的双中断机制,处理器内核有两个中断接口IRQ(标准中断接口)和FIQ(快速中断接口),而AT91SAM9261片内的先进中断控制器(AIC)利用这两个接口将外设的中断请求有序的传递到内核。如下图所示:

AT91SAM9261中断处理过程解析_第1张图片

AT91SAM9261的先进中断控制器(AIC)是一个8优先级,可独立屏蔽中断的向量中断控制器,可处理32个中断源。它的设计思想是从本质上减少在处理内部和外部中断时的软件和实时系统开销。AIC驱动ARM内核的nIRQ和nFIQ,它的输入来自内部外设中断或产品引脚的外部中断。

AIC的8优先级控制器允许用户对每个中断源定义优先级。即使一个低优先级中断正在被处理,也允许高优先级的中断被服务。

内部中断源可被编程为电平有效或者边沿触发,外部中断源可被编程为上升沿或下降沿触发或者高电平或低电平有效。

快速强制特性可重定向任何内部或外部中断源为一个快速中断而不是一个普通中断。

AIC的详细方块图如下所示:

二、AIC的向量中断机制

AIC 采用“向量中断”的中断跳转机制,这种机制与三星的S3C2410芯片的中断控制器有很大不同。2410在有中断触发之后,首先会进入异常向量表,然后跳转到IRQ或FIQ服务程序,这段代码负责解析具体是哪一个中断源产生了中断,然后才跳转到具体的处理函数去执行。而向量中断的中断跳转机制则不需要软件来识别中断源,也就是不需要IRQ或FIQ服务程序,而完全由硬件自动跳转到对应的中断地址,相当于硬件帮助我们完成了一些必要的处理,减小了中断响应延时。

也就是说,AIC对于各个中断源的管理是采用硬件和软件两个方面的分发处理来完成的。首先AIC为每一个中断源向量寄存器(AIC_SVR0~ AIC_SVR31),在配置使能相应外设时用户将设备的中断处理函数地址写入对应的中断源向量寄存器,而当中断请求发生时AIC将对应的源向量寄存器中的地址复制到中断向量寄存器(AIC_IVR)并向内核发出IRQ请求,以上都是硬件实现的中断分发过程;在软件层面也必须做相应的处理才能完成IRQ的分发处理,对于ARM内核来讲,IRQ只是一个中断向量,当IRQ中断请求时内核会跳到其在异常向量表中所处的位置去处理请求,ARM的启动代码必须在这个IRQ中断处理程序中分发处理才能分辨不同设备的中断请求,但有了上面的硬件的预处理过程,软件分发变得十分简单,一条语句就够了:即将AIC中断向量寄存器(AIC_IVR)里的值赋给PC就可以了。下面是AT91SAM9261的启动代码中定义的异常向量表:
        Vectors        LDR PC,Reset_Addr
                              LDR PC,Undef_Addr
                              LDR PC,SWI_Addr
                              LDR PC,PAbt_Addr
                              LDR PC,Dabt_Addr
                              ; Reserved vector is used as size information for 2-nd level
                              ; bootloader to use when copying program code to External SDRAM
                              IF :DEF:SIZE_INFO
                                     DCD ||Image$$ER_ROM1$$RO$$Length||+\
                                                 ||Image$$RW_RAM1$$RW$$Length||
                              ELSE
                                     NOP
                              ENDIF
                              LDR PC,[PC,#-0xF20] ; Vector From AIC_IVR
                              LDR PC,[PC,#-0xF20] ; Vector From AIC_FVR

可以看到:IRQ对应异常处理指令是LDR PC,[PC,#-0xF20],当执行这条指令时,由ARM流水线结构,此时的PC = 0x18 + 2*4 = 0x20,因此执行之后PC被赋予地址:0x20 – 0xF20 = 0xFFFFF100里的内容,即IRQ向量寄存器AIC_IVR的内容,程序跳转到相应的中断例程中。
对比一下S3C2410的启动代码:

Vectors LDR PC, Reset_Addr
                       LDR PC, Undef_Addr
                       LDR PC, SWI_Addr
                       LDR PC, PAbt_Addr
                       LDR PC, DAbt_Addr
                       NOP ; Reserved Vector
                       LDR PC, IRQ_Addr
                       LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
        Undef_Addr DCD Undef_Handler
        SWI_Addr DCD SWI_Handler
        PAbt_Addr DCD PAbt_Handler
        DAbt_Addr DCD DAbt_Handler
                                  DCD 0 ; Reserved Address
        IRQ_Addr DCD IRQ_Handler
        FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
        SWI_Handler B SWI_Handler
        PAbt_Handler B PAbt_Handler
        DAbt_Handler B DAbt_Handler

                                  IF IntVT_SETUP <> 0
        IRQ_Handler B IRQ_Entry
                                  ENDIF

        FIQ_Handler B FIQ_Handler

在异常向量表中,IRQ对应的处理指令是:LDR PC, IRQ_Addr,最终PC会跳到IRQ_Entry去执行。IRQ_Entry里面做了相应处理,即找到中断源,跳转到中断向量表的合适位置去执行具体的中断处理程序(当然之前要定义中断向量入口表)。如下所示:

;Interrupt Vector Table Address
        HandleEINT0 EQU IntVTAddress
        HandleEINT1 EQU IntVTAddress +4
        HandleEINT2 EQU IntVTAddress +4*2
        HandleEINT3 EQU IntVTAddress +4*3
        HandleEINT4_7 EQU IntVTAddress +4*4
        HandleEINT8_23 EQU IntVTAddress +4*5
        HandleReserved EQU IntVTAddress +4*6
        HandleBATFLT EQU IntVTAddress +4*7
        HandleTICK EQU IntVTAddress +4*8
        HandleWDT EQU IntVTAddress +4*9
        HandleTIMER0 EQU IntVTAddress +4*10
        HandleTIMER1 EQU IntVTAddress +4*11
        HandleTIMER2 EQU IntVTAddress +4*12
        HandleTIMER3 EQU IntVTAddress +4*13
        HandleTIMER4 EQU IntVTAddress +4*14
        HandleUART2 EQU IntVTAddress +4*15
        HandleLCD EQU IntVTAddress +4*16
        HandleDMA0 EQU IntVTAddress +4*17
        HandleDMA1 EQU IntVTAddress +4*18
        HandleDMA2 EQU IntVTAddress +4*19
        HandleDMA3 EQU IntVTAddress +4*20
        HandleMMC EQU IntVTAddress +4*21
        HandleSPI0 EQU IntVTAddress +4*22
        HandleUART1 EQU IntVTAddress +4*23
        ;HandleReserved EQU IntVTAddress +4*24
        HandleUSBD EQU IntVTAddress +4*25
        HandleUSBH EQU IntVTAddress +4*26
        HandleIIC EQU IntVTAddress +4*27
        HandleUART0 EQU IntVTAddress +4*28
        HandleSPI1 EQU IntVTAddress +4*39
        HandleRTC EQU IntVTAddress +4*30
        HandleADC EQU IntVTAddress +4*31

IRQ_Entry
                                        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}

                                        ENDIF

AT91SAM9261和S3C2410两款芯片对中断处理的过程可以用下图来说明,我们可以更加清晰的看到两种中断处理过程的异同:

三、AT91SAM9261按键中断示例

下面以按键中断为例,具体解析AT91SAM9261的中断处理。

1、首先查看硬件原理图,确定按键和CPU的连接方式:

硬件原理图显示:按键SW7和PB27连接,PB27是由PIO(Perihepheral Input/Output)Controller管理的。我们由芯片手册可知:每个PIO Controller管理的引脚可以配置为通用I/O口线,或复用为外设I/O。中断信号FIQ和IRQ0~IRQn通过PIO控制器多路复用。然而,由于PIO控制器对于输入无效并且中断口线仅被用为输入,所以没有必要分配I/O口线给中断功能。因此我们需要将PB27配置为输入模式

2、使能PIO控制器的时钟
        AT91F_PMC_EnablePeriphClock ( AT91C_BASE_PMC, 1 << AT91C_ID_PIOB ) ;
        只有PIO控制器时钟被使能才能产生PIO控制器中断。

3、配置PB27为输入模式
        AT91F_PIO_CfgInput( AT91C_BASE_PIOB, SW_MASK ) ;
        这里的SW_MASK = 1<<27,因为我们配置的是PB27,所以没有必要改变其他引脚的属性。

4、使能PB27的上拉电阻
        AT91F_PIO_CfgPullup( AT91C_BASE_PIOA, SW_MASK ) ;
        使能相应管脚的上拉电阻,提高输出电平,从而提高芯片输入信号的噪声容限增强抗干扰能力。

5、暂时禁止PIO中断
        AT91F_AIC_DisableIt(AT91C_BASE_AIC ,AT91C_ID_PIOA);
        暂时禁止PIO中断,等到配置结束之后再使能中断。

6、配置AIC相应寄存器
        AT91C_BASE_AIC->AIC_IDCR = 0x1 << AT91C_ID_PIOB;
        AT91C_BASE_AIC->AIC_SVR[AT91C_ID_PIOB] = (unsigned int) SW_Handler;
        AT91C_BASE_AIC->AIC_SMR[AT91C_ID_PIOB]= AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE ;
        AT91C_BASE_AIC->AIC_ICCR = 0x1 << AT91C_ID_PIOB;
        通过写中断源向量寄存器AIC_SVR[AT91C_ID_PIOB]注册中断处理函数SW_Handler。通过写源模式寄存器AIC_SMR[AT91C_ID_PIOB]来设定中断触发方式和优先级。

7、使能PIO中断
        AT91C_BASE_AIC->AIC_IECR = (1<<AT91C_ID_PIOB);
        AT91F_PIO_InterruptEnable(AT91C_BASE_PIOA, SW_MASK);
        重新使能PIO中断,这里需要设置两个地方,一个是AIC的中断使能命令寄存器,另一个还要使能PIO相应的寄存器来使能中断。

8、填写中断服务程序:SW_Handler
        __irq void SW_Handler (void)
        {
                AT91F_DBGU_Printk("SW1 Was Pressed...\r\n");
                *AT91C_AIC_EOICR = AT91C_BASE_PIOB->PIO_ISR;
        }
        注意在中断服务程序的最后,一定要向AIC的中断结束命令寄存器里写相应中断状态的值,以告诉AIC当前中断以完成,否则CPU不会再一次相应中断。

到此为止,一个按键中断软件部分的编写就结束了。写这种代码主要是要弄清楚中断处理的流程以及各个寄存器的作用,这样写起来就比较容易了。

你可能感兴趣的:(职场,ARM,休闲)