ARM中断分析之四:WinCE的OAL层的中断分析
一、在OAL層的初始化函數,在系統啟動的過程中被調用,如下所示:
BOOL OALIntrInit()
{
BOOL rc = FALSE;
// Initialize interrupt mapping
OALIntrMapInit();
// First get uncached virtual addresses
g_pIntrRegs = (S3C2410X_INTR_REG*)OALPAtoVA(
S3C2410X_BASE_REG_PA_INTR, FALSE
);
g_pPortRegs = (S3C2410X_IOPORT_REG*)OALPAtoVA(
S3C2410X_BASE_REG_PA_IOPORT, FALSE
);
// Mask and clear external interrupts
OUTREG32(&g_pPortRegs->EINTMASK, 0xFFFFFFFF);
OUTREG32(&g_pPortRegs->EINTPEND, 0xFFFFFFFF);
// Mask and clear internal interrupts
OUTREG32(&g_pIntrRegs->INTMSK, 0xFFFFFFFF);
OUTREG32(&g_pIntrRegs->SRCPND, 0xFFFFFFFF);
// S3C2410X developer notice (page 4) warns against writing a 1 to any
// 0 bit field in the INTPND register. Instead we'll write the INTPND
// value itself.
OUTREG32(&g_pIntrRegs->INTPND, INREG32(&g_pIntrRegs->INTPND));
// Unmask the system tick timer interrupt
CLRREG32(&g_pIntrRegs->INTMSK, 1 << IRQ_TIMER4);
// Give BSP change to initialize subordinate controller
rc = BSPIntrInit(); // 配置某些外設的中斷配置,比如以太網,但這不是必須的,也可以在以太網驅動內實現。
return rc;
}
實際上,上面這部份的初始化是超簡單的,總結為完成的事情如下:
1、外設引腳功能的配置及中斷觸發方式,圖中所示是:GPFCON、EXTINT0
2、中斷mask碼、優先級、模式的設定,包括子級掩碼、主級掩碼,圖中所示是:EINTMASK、MASK、MODE、Priority。
二、中斷服務程序,它會在硬件IRQ產生時被調用
中斷服務程序是IRQ中斷的入口點,代碼如下所示:
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);
// System timer interrupt? 系統時間中斷?
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();
}
// Profiling timer interrupt?
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
{
if (irq == IRQ_EINT4_7 || irq == IRQ_EINT8_23) {
// Find external interrupt number
mask = INREG32(&g_pPortRegs->EINTPEND);
mask &= ~INREG32(&g_pPortRegs->EINTMASK); // Find the effect interrupt number
/*
// g_i[i], g_i[i] ^(g_i[i]-1), g_i[i]^(g_i[i]-1)) >> 5
1: 1 : 0
2: 3 : 0
4: 7 : 0
8: 15 : 0
16: 31 : 0
32: 63 : 1
64: 127 : 3
128: 255 : 7
256: 511 : 15
512: 1023 : 31
1024: 2047 : 63
*/
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 {
// Mask the interrupt
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
}
// clear primary interrupt
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;
}
從上面代碼總結出,中斷服務程序主要做的事情是:
1、從INTPND得知主級中斷中的中斷號。
2、從EINTPEND得知次級中斷中的中斷號。
3、置位或者清零中斷控制器的寄存器
·置位次級的mask
·置位主級的mask
·清理次級中斷PND,圖中所示為:EINTPEND
·清理主級中斷PND,圖中所示為:SRCPND
4、最后返回一個中斷號sysIntr,這個是邏輯上面的中斷號,和WINCE驅動的邏輯中斷號相對應。
三、OALIntrDoneIrqs函數
WinCE驅動在處理中斷完畢后,會調用InterruptDone函數,此函數會調用OALIntrDoneIrqs函數,OALIntrDoneIrqs函數內容如下:
VOID OALIntrDoneIrqs(UINT32 count, const UINT32 *pIrqs)
{
UINT32 i, mask, irq;
for (i = 0; i < count; i++) {
// Depending on IRQ number use internal or external mask register
if (irq <= IRQ_ADC) {
// Use internal interrupt mask register
mask = 1 << irq;
CLRREG32(&g_pIntrRegs->INTMSK, mask);
} else if (irq <= IRQ_EINT23) {
// Use external mask register
mask = 1 << (irq - IRQ_EINT4 + 4);
CLRREG32(&g_pPortRegs->EINTMASK, mask);
}
}
}
從上面的代碼可知,OALIntrDoneIrqs所做的事情是在中斷處理完畢之后的清零主級的mask或者清零次級的mask。
現在回顧一下整理,在OEMInterruptHandler函數內清零PND,這樣做是有好處的。不然,在驅動內實現的話,就要嚴格按照步驟來清零,即:先清零PND,然后再清零外設PND。為什么要先清零PND,然后再清零外設PND,可以參考我寫的另一篇文章“應該怎么樣清理中斷的PND位?”。
結論是,WinCE在默認情況下,適合邊緣觸發的中斷,如果需要處理電平觸發的話,需要同時修改OEMInterruptHandler函數和OALIntrDoneIrqs函數,在OEMInterruptHandler函數內不要清零PND而在OALIntrDoneIrqs內清零PND,這樣才能避免重復處理同一個中斷。
總之,WINCE的OAL層和中斷相關的函數是:
ULONG OEMInterruptHandler(ULONG ra); //中斷服務函數
void OEMInterruptDone( DWORD idInt ); //中斷處理完畢的函數
void OEMInterruptDisable( DWORD idInt ); //禁用中斷的函數
BOOL OEMInterruptEnable( DWORD idInt, LPVOID pvData, DWORD cbData ); //啟用中斷的函數
當然,還有中斷初始化函數,但那是非OEM函數,即自己定義。以上幾個函數的源碼,可以參考相應的BSP包。
僅管OEMInterruptDone和OEMInterruptEnable,在功能上都是把指定IRQ的MASK給清零,但是OEMInterruptEnable需要多做一件事情,它需要把前一級的相應的PND寄存器給清零,意思是:清除以前發生的中斷,現在開始處理新的中斷。
OEMInterruptDone會調用OALIntrDoneIrqs,OALIntrDoneIrqs調用BSPIntrDoneIrq,主要的功能處理都在OALIntrDoneIrqs函數內實現。?
?
總結
以上是生活随笔為你收集整理的ARM中断分析之四:WinCE的OAL层的中断分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: aliyun centos6 安装mys
- 下一篇: ARM9之NAND FLASH总结