ARM中断分析之四:WinCE的OAL层的中断分析
一、在OAL層的初始化函數(shù),在系統(tǒng)啟動(dòng)的過程中被調(diào)用,如下所示:
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(); // 配置某些外設(shè)的中斷配置,比如以太網(wǎng),但這不是必須的,也可以在以太網(wǎng)驅(qū)動(dòng)內(nèi)實(shí)現(xiàn)。
return rc;
}
實(shí)際上,上面這部份的初始化是超簡單的,總結(jié)為完成的事情如下:
1、外設(shè)引腳功能的配置及中斷觸發(fā)方式,圖中所示是:GPFCON、EXTINT0
2、中斷mask碼、優(yōu)先級(jí)、模式的設(shè)定,包括子級(jí)掩碼、主級(jí)掩碼,圖中所示是:EINTMASK、MASK、MODE、Priority。
二、中斷服務(wù)程序,它會(huì)在硬件IRQ產(chǎn)生時(shí)被調(diào)用
中斷服務(wù)程序是IRQ中斷的入口點(diǎn),代碼如下所示:
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? 系統(tǒng)時(shí)間中斷?
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;
}
從上面代碼總結(jié)出,中斷服務(wù)程序主要做的事情是:
1、從INTPND得知主級(jí)中斷中的中斷號(hào)。
2、從EINTPEND得知次級(jí)中斷中的中斷號(hào)。
3、置位或者清零中斷控制器的寄存器
·置位次級(jí)的mask
·置位主級(jí)的mask
·清理次級(jí)中斷PND,圖中所示為:EINTPEND
·清理主級(jí)中斷PND,圖中所示為:SRCPND
4、最后返回一個(gè)中斷號(hào)sysIntr,這個(gè)是邏輯上面的中斷號(hào),和WINCE驅(qū)動(dòng)的邏輯中斷號(hào)相對(duì)應(yīng)。
三、OALIntrDoneIrqs函數(shù)
WinCE驅(qū)動(dòng)在處理中斷完畢后,會(huì)調(diào)用InterruptDone函數(shù),此函數(shù)會(huì)調(diào)用OALIntrDoneIrqs函數(shù),OALIntrDoneIrqs函數(shù)內(nèi)容如下:
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所做的事情是在中斷處理完畢之后的清零主級(jí)的mask或者清零次級(jí)的mask。
現(xiàn)在回顧一下整理,在OEMInterruptHandler函數(shù)內(nèi)清零PND,這樣做是有好處的。不然,在驅(qū)動(dòng)內(nèi)實(shí)現(xiàn)的話,就要嚴(yán)格按照步驟來清零,即:先清零PND,然后再清零外設(shè)PND。為什么要先清零PND,然后再清零外設(shè)PND,可以參考我寫的另一篇文章“應(yīng)該怎么樣清理中斷的PND位?”。
結(jié)論是,WinCE在默認(rèn)情況下,適合邊緣觸發(fā)的中斷,如果需要處理電平觸發(fā)的話,需要同時(shí)修改OEMInterruptHandler函數(shù)和OALIntrDoneIrqs函數(shù),在OEMInterruptHandler函數(shù)內(nèi)不要清零PND而在OALIntrDoneIrqs內(nèi)清零PND,這樣才能避免重復(fù)處理同一個(gè)中斷。
總之,WINCE的OAL層和中斷相關(guān)的函數(shù)是:
ULONG OEMInterruptHandler(ULONG ra); //中斷服務(wù)函數(shù)
void OEMInterruptDone( DWORD idInt ); //中斷處理完畢的函數(shù)
void OEMInterruptDisable( DWORD idInt ); //禁用中斷的函數(shù)
BOOL OEMInterruptEnable( DWORD idInt, LPVOID pvData, DWORD cbData ); //啟用中斷的函數(shù)
當(dāng)然,還有中斷初始化函數(shù),但那是非OEM函數(shù),即自己定義。以上幾個(gè)函數(shù)的源碼,可以參考相應(yīng)的BSP包。
僅管OEMInterruptDone和OEMInterruptEnable,在功能上都是把指定IRQ的MASK給清零,但是OEMInterruptEnable需要多做一件事情,它需要把前一級(jí)的相應(yīng)的PND寄存器給清零,意思是:清除以前發(fā)生的中斷,現(xiàn)在開始處理新的中斷。
OEMInterruptDone會(huì)調(diào)用OALIntrDoneIrqs,OALIntrDoneIrqs調(diào)用BSPIntrDoneIrq,主要的功能處理都在OALIntrDoneIrqs函數(shù)內(nèi)實(shí)現(xiàn)。?
?
總結(jié)
以上是生活随笔為你收集整理的ARM中断分析之四:WinCE的OAL层的中断分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: aliyun centos6 安装mys
- 下一篇: ARM9之NAND FLASH总结