gun-ucos 的s3c2440处理器 中断部分

      首先说下2440中断处理过程:

       中断请求由硬件产生,根据中断源类型分别将中断信号送到SUBSRCPND(SubSourcePending)和SRCPND(SourcePending)寄存器,SUBSRCPND是子中断源暂存寄存器,用来保存子中断源信号,SRCPND是中断源暂存寄存器,用来保存中断源信号。中断信号可通过编程方式屏蔽掉,是子中断源屏蔽寄存器,可以屏蔽指定的子中断信号, MASK功能同SUBMASK用来屏蔽中断源信号。

    中断分为两种模式:一般中断的和快速中断,MODE是中断模式判断寄存器,用来判断当前中断是否为快速中断,如果为快速中断直接将快速中断信号送给ARM内核,如果不是快速中断,还要将中断信号进行仲裁选择。S3C2440A支持多达60种中断,很有可能多个硬件同时产生中断请求,这时要求中断控制器做出裁决,Priority是中断源优先级仲裁选择器,当多个中断产生时,选择出优先级最高的中断源进行处理,INTPND是中断源结果寄存器,里面存放优先级仲裁出的唯一中断源。

gun-ucos 的s3c2440处理器 中断部分_第1张图片

    中断控制器要支持60个中断源,每个寄存器只有32bit,所以需要2个寄存器,2440采用分层 控制的方式。对于挂起寄存器分两层

     第一层是源挂起寄存器SRCPND 由 32 位组成,其每一位都涉及一个中断源。如果中断源产生了中断则相应的位被设置为 1 并且等待中断服务。因此此寄存器指示出是哪个中断源正在等待请求服务。注意 SRCPND 寄存器的每一位都是由中断源自动置位,其不顾 INTMASK 寄存器中的屏蔽位。另外 SRCPND 寄存器不受中断控制器的优先级逻辑的影响。在指定中断源的中断服务程序中,必须通过清除 SRCPND 寄存器的相应位来正确的获得来自相同源的中断请求。

        清除相应位的时间依赖于用户的需要。如果希望收到来自相同源的其它有效请求,则应该首先清除相应位,并且接着使能中断。可以通过写入一个数据到此寄存器来清除 SRCPND 寄存器的指定位。

      第二层是次级源挂起(SUBSRCPND)寄存器可以通过写入数据到此寄存器来清除 SUBSRCPND 寄存器的指定位。由于这种特性SRCPND 和 SUBSRCPND 寄存器可以在同一时刻多个位同时置位为1。


    对于次级中断源的中断,发生后SUBSRCPND相应位置1,如果没有被SUBINTSUBMSK屏蔽,那么SRCPND相应位置1,等待进一步处理,几个SUBSRCPND可能对应同一个SRCPND  ,可以在ISR里面通过读取 SUBSRCPND  来判断究竟是哪个次级中断。

SUBSRCPND 寄存器有15位,与中断对应关系如下:

gun-ucos 的s3c2440处理器 中断部分_第2张图片

gun-ucos 的s3c2440处理器 中断部分_第3张图片



中断屏蔽

    中断屏蔽也包括两层,第二层中断次级屏蔽(INTSUBMSK)寄存器此寄存器有 15 位,其每一位都与一个次级中断源相联系。如果某个指定位被设置为 1,则相应中断源的中断请求不会被 CPU 所服务(请注意即使在这种情况中,SUBSRCPND 寄存器的相应位也设置为 1)。如果屏蔽位为 0,则可以服务中断请求。

注意SUBMSK 和SUBPND 位数一样,每个位对应的次级中断类型也是一样的。

系统上电默认值为全部子中断都被屏蔽掉,因此要想处理某个硬件中断,必须要打开中断屏蔽位,通过写入0来取消屏蔽中断。


中断优先级
    S3C2440A支持60种中断,多个硬件可能同时产生中断请求,由于CPU只能处理一个中断,中断控制器怎么选择出一个最佳的中断,交给ARM内核进行处理呢?中断控制器采用优先级仲裁比较的方式进行选择,找出优先级最高的中断源。中断控制器将60种中断源分成7组,如下图所示:


gun-ucos 的s3c2440处理器 中断部分_第4张图片

     中断信号在7个分组里仲裁时的优先级是可编程的,通过PRIORITY寄存器进行优先级设置,这个寄存器可以分别设置每一组内部的优先级 ,和组的优先级轮换使能。 这个一般被设置为0x00,也就是使用刚上电数值。


中断源结果寄存器
   经过设置SRCPND 位,MSK位屏蔽判断,和优先级模块仲裁得到一个唯一中断源放在中断源结果寄存器也就是INTPND 寄存器。这个寄存器在一个时刻只能一个位被置1.
与此同时中断系统还修改  INTOFFSET 中断号偏移量寄存器---是一个0~31之间的整数,其实它就是INTPND里对应的位号,通过读这个寄存器可以快速的知道发生了哪种类型中断。
   注意对于INTPND 寄存器和 SRCPND SUBSRCPND 寄存器,当中断发生时,它们相应位会被置1,这个需要软件清除相应的位。当清除的时候要向相应位写1,而不是写0。这点在文档上没有详细说明。


   下面按照上面的过程 来分析一遍ucos 中断的处理过程。

首先调用 函数TickISRInit (现在这个函数名字跟功能有些不相符):

void TickISRInit(void)
{
	// 设置中断控制器
	rPRIORITY = 0x00000000;		// 使用默认的固定的优先级
	rINTMOD = 0x00000000;		// 所有中断均为IRQ中断

	pIRQ_TIMER0=(uint32)OSTickISR;
	rINTMSK &= ~(1<<10);			// 打开TIMER0中断允许
	CLR_IF();
	//	OSTime=0;
}
设置仲裁优先级寄存器使用默认的优先级。 设置中断模式寄存器 ,均为irq中断。 打开一个中断屏蔽位1<<10 也就是timer0 的中断。

当产生中断之后来到代码:

OS_CPU_IRQ_ISR:

	STMFD   SP!, {R1-R3}			@ We will use R1-R3 as temporary registers
	MOV     R1, SP
	ADD     SP, SP, #12             @Adjust IRQ stack pointer
	SUB     R2, LR, #4              @Adjust PC for return address to task

	MRS     R3, SPSR				@ Copy SPSR (Task CPSR)

	MSR     CPSR_cxsf, #SVCMODE|NOINT  @ to SVC mode

	STMFD   SP!, {R2}				@ Push task''s PC
	STMFD   SP!, {R4-R12, LR}		@ Push task''s LR,R12-R4

	LDMFD   R1!, {R4-R6}			@ Load Task''s R1-R3 from IRQ stack
	STMFD   SP!, {R4-R6}			@ Push Task''s R1-R3 to SVC stack
	STMFD   SP!, {R0}			    @ Push Task''s R0 to SVC stack

	STMFD   SP!, {R3}				@ Push task''s CPSR
	LDR     R0,=OSIntNesting        @OSIntNesting++
	LDRB    R1,[R0]
	ADD     R1,R1,#1
	STRB    R1,[R0]

	CMP     R1,#1                   @if(OSIntNesting==1){
	BNE     1f

	LDR     R4,=OSTCBCur            @OSTCBHighRdy->OSTCBStkPtr=SP@
	LDR     R5,[R4]
 	STR     SP,[R5]  
1:
	MSR    CPSR_c,#IRQMODE|NOINT   @ tox use IRQ stack to handle interrupt
	LDR     R0, =INTOFFSET
        LDR     R0, [R0]
	LDR     R1, =INTMSK
	LDR     R1, [R1]
	MOV     R3, #1
	LSL     R4, R3,R0
	TST     R4, R1
	BNE	2f
	
   	LDR     R1, =IRQ_STARTADDRESS
	MOV     LR, PC
        LDR     PC, [R1, R0, LSL #2]

2:
	LDR     R0, =INTOFFSET
        LDR     R0, [R0]
	MOV     R1, #1
	MOV     R1, R1, LSL R0
	LDR     R0, =SRCPND
	LDR     R2, [R0]
	ORR     R2, R1,R2
	STR     R2, [R0]

	LDR     R0, =INTPND	
	LDR     R2, [R0]
	ORR     R2, R1,R2
	STR     R2, [R0]	
	
	MSR		CPSR_c,#SVCMODE|NOINT
    	BL 		OSIntExit

    	LDMFD   SP!,{R4}
    	MSR	     SPSR_cxsf,R4
    	LDMFD   SP!,{R0-R12,LR,PC}^	   @POP new Task''s context
   上面代码首先把发生中断时各个寄存器放入 svc (程序正常运行模式)的栈中,并将svc栈指针放入OSTCBCur->OSTCBStkPtr中。

然后把栈切换到irq模式来处理中断。

   从 INTOFFSET 寄存器里面读出中断偏移量放入R0中,然后再读INTMSK 判断是否是我们允许的中断,如果是那么就从 IRQ_STARTADDRESS中断数组里面取相应的中断函数来执行。

   IRQ_STARTADDRESS 表示中断向量的首地址:

#define _IRQ_STARTADDRESS       0x33ffff00

如果是0xa号中断对应的地址是  0x33ffff28

#define pIRQ_TIMER0         (*(unsigned *)(_IRQ_STARTADDRESS+0x28))

注意到这个值在前面 ISR 初始化的时候已经被赋值为 OSTickISR ,跳转到 OSTickISR 去执行这个是系统的时钟脉冲函数。


    当从中断处理函数 OSTickISR  中出来之后把 SRCPND INTPND相应的位清除。进入svc模式,调用 OSIntExit -> OSIntCtxSw 做一些中断末尾的清理工作,如果有需要就切换

任务。当从中断返回的时候按照入栈相反的顺序把栈恢复。




参考资料:

博文   http://blog.csdn.net/mr_raptor/article/details/6556186

s3c2440 文档关于中断章节。




你可能感兴趣的:(ARM,s3c2440,ucos,架构中断过程)