6410BSP 中断的大bug
最近发现新加的电容屏,有一定的概率会出现永远无法触摸的情况。经过调试,发现中断被屏蔽,但是确认驱动已经调用了InterruptDone,并且在调用之后打印,发现触摸屏中断屏蔽位是0(未屏蔽),说明是被其他地方设置成屏蔽的。后面经过一系列的调试和分析,终于找到了可能的原因,是设置屏蔽中断的地方没有同步引起的。我们触摸屏用是EINT15,DM9000用的是EINT10,对应的中断屏蔽寄存器是EINT0MASK ,一个是第15位,一个是第10位。屏蔽中断的代码为 g_pGPIOReg->EINT0MASK |= (1<<(VirtualIRQ-30)); 问题就出现在这里,因为这一句有几个地方会执行,首先是OALIntrDoneIrqs(驱动代码里面调用的InterruptDone会调用它),还有OEMInterruptHandler里面也会调用。这几个地方完全没有做同步处理,所以会导致并发执行。比如两个驱动的InterruptDone会同时执行,InterruptDone和OEMInterruptHandler也会同时执行。下面列举一个可能的执行序列:
1,DM9000->InterruptDone->OALIntrDoneIrqs -> 读取EINT0MASK
2,TOUCH->InterruptDone->OALIntrDoneIrqs ->保存(EINT0MASK &= ~(1<<(15))
3,DM9000->InterruptDone->OALIntrDoneIrqs ->保存 (EINT0MASK &= ~(1<<(10))
这里就可以看出问题来了。第一步DM9000读取的EINT0MASK里面,第15位是为1,TOUCH中断被屏蔽。第二步时触摸屏驱动处理完毕,打开TOUCH中断,此时EINT0MASK第15位为0。第三步时,此时因为是在旧的数据(EINT0MASK第15位为1)上做的运算,所以最后的结果是打开了DM9000中断,但是屏蔽了TOUCH中断。
简单的说,就是类似于多线程里面并发的 i = i +1;的问题。
另外,网上有些人疑惑VICxINTENABLE寄存器为什么要通过VICxINTENCLEAR这个寄存器设置值,比如想把VICxINTENABLE某一位置位,并不是直接向VICxINTENABLE的对应位写1,而是通过向VICxINTENCLEAR对应位写1,其他位为0(0为不影响)来实现,估计就是考虑到同步问题。通过这种方式操作就一定不会有以上的问题了,因为修改VICxINTENCLEAR的指令一定是顺序执行的,并且只需要对应的位是1,其他位为0,不会造成覆盖别的位。
以下是BSP 中断处理的部分代码
OALIntrDoneIrqs函数的处理
else if (PhysicalIRQ == PHYIRQ_EINT2 || // IRQ_EINT12 ~ IRQ_EINT19
PhysicalIRQ == PHYIRQ_EINT3 ) // IRQ_EINT20 ~ IRQ_EINT27
{
g_pGPIOReg->EINT0MASK &= ~(1<<(VirtualIRQ-30)); // Enable Sub Interrupt
}
OEMInterruptHandler函数的处理
// Check Sub Source
uiPending = g_pGPIOReg->EINT0PEND;
uiMask = g_pGPIOReg->EINT0MASK;
for (nNumber=12; nNumber<20; nNumber++) // EINT12~EINT19
{
if ((uiPending & (1<<nNumber)) && !(uiMask & (1<<nNumber)))
{
g_pGPIOReg->EINT0MASK |= (1<<nNumber); // Mask Sub Interrupt
g_pGPIOReg->EINT0PEND = (1<<nNumber); // Clear Sub Pending
VirtualIRQ = (IRQ_EINT12+(nNumber-12)); // Set Virtual IRQ
break;
}
}