这段来自:http://hi.baidu.com/ghy0504/blog/item/db90649a0f554bbfc8eaf47a.html
具体写的很详细,后面画个图对流程进行详细解释,方便自己查阅,并结合wince里面底层中断的代码分析下。
S3C2410有24个外部中断引脚:EINT0~EINT23,但是对于EINT0~EINT3这4个外部中断而言,使用比较简单;对于其它20个而言,使用稍稍复杂一些。
首先,来看S3C2410的中断控制器,有6个中断裁决器分为2级,第1级5个,第2级1个。第1级的5个裁决器管理32个中断源,在这32个中断源中有2个是保留的,24个是给内部中断源的,而外部中断只给了6个中断:EINT0、 EINT1、EINT2、EINT3、EINT4~7、EINT8~23。EINT4~EINT7、EINT8~EINT23都是共享一个中断。
中断控制器中主要的相关寄存器有:INTMSK、SRCPND、PRIORITY、INTPND,还有一个INTMOD。但是对于外部中断还有几个寄存器:EXTINTn、EINTMSK、EINTPEND等。
对于外部中断EINT0~EINT3由于没有共享,因此外部寄存器中只有EXTINT0和它们有关,进行中断触发电平信号的设置。
但是对于其他的20个外部中断而言,所有上面提到的寄存器都有关系。实际上对于 EINT4~EINT23,实际上可以看作有3级屏蔽,第1级屏蔽是CPSR中的I-Bit和F-Bit,第2级屏蔽是EINTMASK([4:23]每一位对应一个相应的外部中断),第3级屏蔽是INTMSK(Bit4、Bit5);对于EINT0~EINT3而言,只是INTMSK中的 Bit0~Bit3,因此在变成初始化时应该要特别注意。
在外部中断有中断请求时,由于EINT4~7、EINT8~23分别共享中断,因此在SRCPND中分别对应Bit4、Bit5,最终导致INTPND中的相应位置1(在任何时刻只能有一个位置1),但是如何分辨这些共享的中断?可以通过查询EINTPEND来进行(Bit4~Bit23分别对应1个外部中断)。由于在相应中断后在服务例程中应将挂起寄存器中相应的位清0来清除未处理状态,因此对于EINT4~7、EINT8~23这20个外部中断的清除挂起寄存器的顺序是:
1. EINTPEND(可能多位同事为1),方法:向要清0的位写1,其它位写0,但要注意由于第4位保留,因此不要试图向第4位写1,可能会导致不可预料的结果。
2.SRCPND(可能多位同时为1),方法:向要清0的位写1,其它位写0。
3.INTPND,方法:向其写本身的数据(即INTPND=INTPND)。
下面具体介绍各个寄存器的功能:
SRCPND(Source Pending Register)寄存器有效位32位,可读写,每一位涉及到一个中断源,SRCPND是主中断源引脚寄存器,某个位被置1表示相应的中断被触发,但我们知道在同一时刻内系统可以触发若干个中断,只要中断被触发了,SRCPND的相应位便被置1,也就是说SRCPND 在同一时刻可以有若干位同时被置1,另外,此寄存器不受中断控制器的优先权逻辑的影响。如果此中断没有被INTMSK寄存器屏蔽、或者是快中断(FIQ)的话,它将被进一步处理。通过写数据到这个寄存器能清除SPCPND相应的位。
INTMODE(Interrupt Mode Register)寄存器有效位为32位,可读写,每一位与SRCPND中各位相对应,它的作用是指定该位相应的中断源处理模式(IRQ还是FIQ)。若某位为0,则该位相对应的中断按IRQ模式处理,为1则以FIQ模式进行处理,该寄存器初始化值为0x00000000,即所有中断皆以IRQ模式进行处理。此中断控制器中只有一个中断源能用FIQ mode(在紧急中断下使用FIQ mode),因此INTMODE仅有一位能置1。
INTMSK(Interrupt Mask Register)寄存器有效位为32位,可读写,INTMSK为主中断屏蔽寄存器与SRCPND寄存器对应,它的作用是决定该位相应的中断请求是否被处理。若某位被设置为1,则该位相对应的中断产生后将被忽略(CPU不处理该中断请求),设置为0则CPU对其进行处理。该寄存器初始化值为0xFFFFFFFF,既默认情况下所有的中断都是被屏蔽的。
PRIORITY(IRQ PRIORITY Control Register)寄存器有32位,有效位[20:0],可读写,此寄存器的作用是如果有几个中断源同时触发,按照图2.2.1的流向,假如这几个中断源都没被屏蔽,并且都是IRQ模式,因此就要判定哪个中断源的优先级最高,使其在INTPND寄存器中对应位置1,CPU转向相应的中断服务程序,让中断服务程序来处理相应的中断请求。
INTPND(Interrupt Pending Register)寄存器有效位为32位,可读写,看起来和SRCPND寄存器一样,其实他们在功能上有着重大的区别。他在某一时刻只能有1个位被置1,INTPND 某个位被置1(该位对应的中断在所有已触发的中断里具有最高优先级且该中断没有被屏蔽),则表示CPU即将或已经在对该位相应的中断进行处理。因此SRCPND寄存器说明有什么中断被触发了,而INTPND寄存器说明CPU即将或已经在对某一个中断进行处理。
INTOFFSET(Interrupt Offset Register)寄存器有效位为32位,只读,此寄存器的值显示IRQ mode的哪个中断请求在INTPND寄存器中,通过清SRCPND和INTPND这位能自动清除,FIQ mode中断不会影响INTOFFSET寄存器,因此此寄存器仅对IRQ mode中断有效。
SUBSRCPND(Sub Source Pending Register)寄存器有32位,有效位[10:0],可读写,它们中的每一位分别代表一个中断源,SRCPND是主中断源引脚寄存器,它是副中断源引脚寄存器,情况类似SRCPND。
INTSUBMSK(Interrupt Sub Mask Register)寄存器有32位但有效位为11位,可读写,如果mask bit位是0,此中断请求被服务,情况类似INTMSK。
图上面部分表述内部中断流程,产生了两个中断INT_TXD和INT_RTC,INT_TXD经过SubMask后为INT_UART0,这里SRCPND有两个中断标志,最后假设INT_UART0优先级高,最后在INTPND里面INT_UART0置一了。子屏蔽模块里面其实主要是一些同一控制器有多个中断标志的情况,可以这样认为就是控制器本身的标志位,三星这样做反而有点迷惑人,成熟厂家一般都是作为一个模块控制器本身的寄存器标志来做的。
图下部分为外部中断产生流程,外部24个中断分为3组,在达到内部中断流程入口时候,分为了EINT0/EINT1/EINT2/EINT3/EINT4_7/EINT8_23这六个标志,到达NO_SUB REQ SOURCE,为非子屏蔽掩码部分。故当外部中断的EINT4_7与EINT8_23到来时,需要去读取EINTMASK与EINTPEND来确定为哪个中断源。
INTPND标志置一后:
1、NO_SUB_REQ_SOURCE则直接清楚SRCPND和INTPND对应标志位即可。
2、SUB_REQ_SOURCE则在取得对应子中断号后清除SUBSRCPND、SRCPND和INTPND对应标志位。
3、EXTINT,EINT0~EINT3与NO_SUB_REQ_SOURCE相同,EINT4_7/EINT8_23先清除EINTMASK和EINTPEND后再进行与NO_SUB_REQ_SOURCE相同动作。
这里分析Wince模拟器里面的中断处理代码就很清除了。
ULONG OEMInterruptHandler(ULONG ra)
{
UINT32 sysIntr = SYSINTR_NOP;
UINT32 irq, irq2 = OAL_INTR_IRQ_UNDEFINED, mask;
fInterruptFlag = TRUE; // Signal OemIdle to come out of idle.
// Get pending interrupt(s)
irq = INREG32(&g_pIntrRegs->INTOFFSET);
// 系统心脏脉动定时器,不需要屏蔽中断处理,直接就按照NO_SUB_REQ_SOURCE进行。
if (irq == IRQ_TIMER4) {
// Clear the interrupt
OUTREG32(&g_pIntrRegs->SRCPND, 1 << IRQ_TIMER4);
OUTREG32(&g_pIntrRegs->INTPND, 1 << IRQ_TIMER4);
// Rest is on timer interrupt handler
sysIntr = OALTimerIntrHandler();
}
// 调试中断用,需要屏蔽后再按照NO_SUB_REQ_SOURCE处理,等系统将任务处理结束后再行开启mask。
else if (irq == IRQ_TIMER2)
{
// Mask and clear the interrupt.
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
// The rest is up to the profiling interrupt handler (if profiling
// is enabled).
//
if (g_pProfilerISR)
{
sysIntr = g_pProfilerISR(ra);
}
}
else
{
#ifdef OAL_ILTIMING
if (g_oalILT.active) {
g_oalILT.isrTime1 = OALTimerCountsSinceSysTick();
g_oalILT.savedPC = 0;
g_oalILT.interrupts++;
}
#endif
if (irq == IRQ_EINT4_7 || irq == IRQ_EINT8_23) {
// Find external interrupt number
//外部两组中断源,先找出具体中断号,再将其清除,并将该中断号给屏蔽,等处理结束后再行开启
mask = INREG32(&g_pPortRegs->EINTPEND);
mask &= ~INREG32(&g_pPortRegs->EINTMASK);
mask = (mask ^ (mask - 1)) >> 5;
irq2 = IRQ_EINT4;
while (mask != 0) {
mask >>= 1;
irq2++;
}
// Mask and clear interrupt
mask = 1 << (irq2 - IRQ_EINT4 + 4);
SETREG32(&g_pPortRegs->EINTMASK, mask);
OUTREG32(&g_pPortRegs->EINTPEND, mask);
// calculate mask for primary interrupt
mask = 1 << irq;
// update irq
irq = irq2;
} else {
// 其它NO_SUB_REQ_SOURCE,按照NO_SUB_REQ_SOURCE处理,该处需要屏蔽。
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
}
// 清除中断标志
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
// First find if IRQ is claimed by chain
sysIntr = NKCallIntChain((UCHAR)irq);
if (sysIntr == SYSINTR_CHAIN || !NKIsSysIntrValid(sysIntr)) {
// IRQ wasn't claimed, use static mapping
sysIntr = OALIntrTranslateIrq(irq);
}
// unmask interrupts in case it's NOP or invalid
if (SYSINTR_NOP == sysIntr) {
if (OAL_INTR_IRQ_UNDEFINED == irq2) {
// Unmask the primary interrupt
CLRREG32(&g_pIntrRegs->INTMSK, mask);
} else {
// Unmask the external interrupt
mask = 1 << (irq2 - IRQ_EINT4 + 4);
CLRREG32(&g_pPortRegs->EINTMASK, mask);
}
}
}
return sysIntr;
}
//开启中断
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)
{
BOOL rc = TRUE;
UINT32 i, mask, irq;
OALMSG(OAL_INTR&&OAL_FUNC, (
L"+OALIntrEnableIrqs(%d, 0x%08x)/r/n", count, pIrqs
));
for (i = 0; i < count; i++) {
// Give BSP chance to enable irq on subordinate interrupt controller
irq = BSPIntrEnableIrq(pIrqs[i]);
if (irq == OAL_INTR_IRQ_UNDEFINED) continue;
// Depending on IRQ number use internal or external mask register
if (irq <= IRQ_ADC) {//分为三段,内部中断源与EINT0~EINT3
// Use interrupt mask register
CLRREG32(&g_pIntrRegs->INTMSK, 1 << irq);
} else if (irq <= IRQ_EINT7) {//EINT4~EINT7
// Use external mask register
CLRREG32(&g_pIntrRegs->INTMSK, 1 << IRQ_EINT4_7);
CLRREG32(&g_pPortRegs->EINTMASK, 1 << (irq - IRQ_EINT4 + 4));
} else if (irq <= IRQ_EINT23) {//EINT8~EINT23
// Use external mask register
mask = 1 << (irq - IRQ_EINT4 + 4);
OUTREG32(&g_pPortRegs->EINTPEND, mask);
CLRREG32(&g_pPortRegs->EINTMASK, mask);
mask = 1 << IRQ_EINT8_23;
if ((INREG32(&g_pIntrRegs->INTPND) & mask) != 0) {
OUTREG32(&g_pIntrRegs->INTPND, mask);
}
CLRREG32( &g_pIntrRegs->INTMSK, 1 << IRQ_EINT8_23);
} else {
rc = FALSE;
}
}
OALMSG(OAL_INTR&&OAL_FUNC, (L"-OALIntrEnableIrqs(rc = %d)/r/n", rc));
return rc;
}