s3c2440处理IRQ过程

注意ucos代码(也就是main函数中的代码)和启动代码都是在Supervisor模式下工作的。而IRQ是工作在外部中断模式下的,这两种模式用的寄存器组是不一样的,因此切换模式时要注意保持相应的寄存器。

假如我设定定时器2每自减100就产生一次IRQ中断,定时的时间到了之后就产生中断(定时器如何设定这里不讨论):

(1):如果让CPU能够处理中断,即不屏蔽IRQ中断,则需用户手动设定CRPSI_bit位为0,这个过程需要用户用程序控制。

(2):定时器中断(定时器中断的设置是在TCFG1寄存器中,详细请见寄存器设置)产生后,CPU根据INTMODE寄存器的设置(INTMODE &= ~(1<<12),该寄存器的设置详细请见《嵌入式系统原理及接口技术》P250)强制进入IRQ模式。这个过程是由硬件自动进行,无需用户干预。

(3)CPUIRQ模式下把CPSR拷贝到SPSR_irq中,PC值保存在LR_irq中,置CPSR中的I位以关闭IRQ中断。这一过程是由硬件自动处理,无需用户干预。

(4)数据保存之后,CPU强行从0X00000018开始执行,这一过程由硬件自动处理,无需用户干预。

(5)

在启动代码中一开始是:

LDR PC,=HANDLE_ResetInit ;地址是0x00

LDR PC,=HANDLE_HandlerUndef ;地址是0x04

LDR PC,=HANDLE_HandlerSWI ;地址是0x08

LDR PC,=HANDLE_HandlerPabort ;地址是0x0c

LDR PC,=HANDLE_HandlerDabort ;地址是0x10

LDR PC,. ;地址是0x14

LDR PC,=OS_CPU_IRQ_ISR ;地址是0x18

LDR PC,=HANDLE_HandlerFIQ ;地址是0x1c

0X00000018LDR PC,=OS_CPU_IRQ_ISR这条指令,那么CPU就转而到OS_CPU_IRQ_ISR地址处执行。

下面就是OS_CPU_IRQ_ISR函数的设置了,该函数(严格来说不是函数,只是汇编文件的一个地址标号)主要完成以下几个功能:

保存被中断的Supervisor模式下运行的寄存器(任务上下文的保护)到Supervisor模式下任务的栈中,保存栈的指针到任务控制块中,这两步是保存被中断的任务,然后进入中断处理函数。

OS_CPU_IRQ_ISR函数(实际上OS_CPU_IRQ_ISR是个地址吧标号,与函数名是一样的,但是OS_CPU_IRQ_ISR是汇编代码)原型如下:

语句一:STMFD   SP!, {R1-R3}

详解:完成R1-R3的入栈操作,即R3-->[SP-4] R2-->[SP-8]  R1-->[SP-12]。子所以要把这三个寄存器入账,是因为下面要用到这三个寄存器。因为在刚进入OS_CPU_IRQ_ISR函数中,CPU是在IRQ模式下,而被中断的任务是在Supervisor模式下工作的,用到的寄存器是不一样的,其中R0-R12PCCPSR是一样的,LRSP(栈指针存放在这里)和SPSR是不一样,其中在IRQ模式中LRLR_irqSPSP_irq,SPSRSPSR_irq。而Supervisor模式下LRLR_ svcSPSP_svc,SPSRSPSR_svc。任务被中断前是工作在Supervisor模式下的,因此要保存Supervisor模式下的寄存器到Supervisor模式下的栈中(注意:ARM不同模式工作的栈的地址是不一样的,例如在我的开发板的启动代码中Supervisor模式下栈的基址是0X33FF0000-10*2*1024,而IRQ栈的基址是0X33FF0000-10*3*1024,大小都是10*1024,因此STMFD   SP!, {R1-R3}是把R1-R3IRQ模式下的栈

第二条语句:MOV     R1, SP

详解:假如SP_irq基址是78,那么STMFD   SP!, {R1-R3}执行后栈中内容如下:

74:[R3] 70:[R2] 66:[R1],此时SP_irq寄存器中的值就是66,执行MOV     R1, SPR1中的值也就是66这个SP的保存主要是为了通过这个值访问IRQ模式下的堆栈空间,实现对数据的访问。

第三条语句:ADD     SP, SP, #12

详解:调整IRQ模式下的堆栈指针SP_irq,将这个指针指向IRQ堆栈的开始位置,方便下一次中断的处理操作。执行后SP_irq又回到原来的栈的基址,即74

第四条语句:SUB     R2, LR, #4

详解:(LR_irq-4)-->R2

在(3)中已经说明了,在发生IRQ中断后,PC值保存在LR_irq,所以这句就是把程序的返回地址存入R2中,子所以减4是因为ARM采用三级流水,即取指,译码,执行。当执行当前语句(例如地址是30000016,实际PC中已经装入30000024,所以第二条指令,地址30000020还没有执行,因此中断返回时要执行30000020处的指令,因此LR_irq要减去4)。

第五条语句:MRS     R3, SPSR

详解:SPSR_irq-->R3

正如(3)所说因为发生了IRQ中断,此时CPU进入IRQ模式中,这时的SPSR_irq中保存了svc模式下的CPSR状态而任务堆栈中保存的刚好是SVC模式下的状态寄存器,因此需要将SVC模式下的状态寄存器首先读出来,然后保存进任务的堆栈中,因此用R3来暂时保存CPSR值,待会还要保存到SVC模式下热任务的栈中

第六条语句: MSR     CPSR_cxsf, #SVCMODE:OR:NOINT

详解:进入SVC模式,这才要真正保存任务的寄存器和栈。正如(3)提到的从开始进入OS_CPU_IRQ_ISR到执行到此处IRQ一直是关着的,因为刚发生中断时系统CPSR中的I位以关闭IRQ中断,执行到此处一直没有开中断,而这条语句也是关中断,因此OS_CPU_IRQ_ISR是一直在关闭中断的条件下执行,不会出现中断套嵌。所以下文中保存任务在SVC模式下的寄存器是有意义的,因为这是第一次中断,如果这条语句是开中断,那么下面在保存被中断的任务在SVC模式下的寄存器之前应该先检查是否有中断套嵌,如果有就不需要保存了,因为在第一次中断发生时已经保存了。

第七条语句:STMFD   SP!, {R2}

详解:R2 -->[SP_svc-4]

注意这才是真正把要保存的任务的寄存器入任务的栈中,R2的内容就是第四条语句中的30000020,即被中断任务的下一条未执行的指令。注意SP_svc的变化,假设一开始SVC模式下SP的基址是1078,那么这条语句执行后的栈中数据变化是107430000020,执行后SP_svc 的值是1074

第八条语句:STMFD   SP!, {R4-R12, LR}

详解:LR_svc-->[1070] R12-->[1066] R11-->[1062]   R10-->[1066] R9-->[1062]

R8-->[1058] R7-->[1054]   R6-->[1050] R5-->[1046] R4-->[1042] ,此时SP_svc的值是1042。至于为什么要保存LR_svc,我一开始不是很明白,现在想明白了,保存任务的寄存器是保存任务的所有寄存器,而我一直是任务只要保存R0-R12,返回地址和CPSR,因为返回地址已经从LR_irq中被保存到[1074]中了,为什么还要保存LR_svc,现在想想返回地址和LR_svc不是一回事,LR_svc也是程序被中断前需要的寄存器,因此需要保存,注意此时LR_svc中的内容仍旧是任务被中断前的数据。

第九条语句: LDMFD   R1!, {R4-R6}

STMFD   SP!, {R4-R6}

STMFD   SP!, {R0}

详解:这三条语句是把R剩下的R0-R4保存到栈中。

第十二条语句:STMFD   SP!, {R3}

详解:保存被中断任务的CPSRR3在第五条语句中已经是把CPSR赋值到R3中了。

寄存器保存完了,接下来就要把SP_svc栈指针保存到任务控制块中了,但是考虑到是否存在中断套嵌,因此要判断一下。因为假如存在中断套嵌,则说明是在中断处理程序中发生的中断,那么就不要保存栈指针到任务控制块了,因为这个工作是在第一次中断发生时干的,也就是在第一次中断的时候已经保存了,后面套嵌的中断自然不需要在保存了。

第十三条语句:

LDR     R0,=OSIntNesting      

LDRB    R1,[R0]

ADD     R1,R1,#1

STRB    R1,[R0]

CMP     R1,#1                  

BNE     %F1

详解:上面的几条语句首先让中断套嵌标志OSIntNesting1,然后判断是否是1,如果是就表明没有中断套嵌。BNE     %F1就是不相等就跳转到后面1标号出处执行。

第十九条语句:

LDR     R4,=OSTCBCur          

LDR     R5,[R4]

STR     SP,[R5]

详解:这几条语句就是把SP_svc赋值到被中断的任务的任务控制块中。但是执行的条件就是没有中断套嵌。

第二十二条:

1

MSR    CPSR_c,#IRQMODE:OR:NOINT

详解:注意1就是标号,如果有中断套嵌就不执行上面三条语句,直接跳到这个位置执行。该语句是进入IRQ模式。但是如果发生中断套嵌,这任然被执行,只不过在19-21执行之后执行的。下面的内容就是进入中断服务函数中执行用户实现的功能。

第二十三条:LDR     LR, =IRQIsrVect

LDR     pc, =IRQ_Dispatch

详解:这条语句是设置LR_svc的值为IRQIsrVect,也就是中断服务函数的返回地址,然后就进入IRQ_Dispatch处执行,注意IRQ_Dispatch是函数地址,这个函数中是用户写的代码。

第二十五条:

IRQIsrVect    

    MSR CPSR_c,#SVCMODE:OR:NOINT  

    BL OSIntExit        

LDMFD   SP!,{R4}             

MSR SPSR_cxsf,R4

LDMFD   SP!,{R0-R12,LR,PC}^

详解:注意这几天语句就是在第二十三条语句中对应的地址,也就是 LDR     pc, =IRQ_Dispatch执行完中断服务函数后返回执行的地址。这几条语句是恢复被中断任务的寄存器,让任务继续运行。

IRQIsrVect 中,首先进入SVC模式,然后调用OSIntExit 函数退出中断处理程序,然后恢复被中断的任务的寄存器。

以上就是一个中断从产生到处理结束后返回的全过程。这段程序并没有判定具体是哪个中断发生了,因此在中断处理程序IRQ_DispatchC语言编写)中需要检查SRCPENDEINTPENDINTOFFSET这三个寄存器来查看是哪个中断然后才能进一步处理,也可以在汇编代码(启动代码中实现具体是哪个中断)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值