Windows CE 中断管理 操作系统对外设的请求都是通过中断来处理的。大多数情况,操作系统都不主动去查看外围设备的请求,只有当中断发生的情况下,操作系统才会通过中断来向外围设备提供服务。首先需要解释几个概念: IRQ(Interrupt Request):物理中断请求,这是外部设备通过CPU的中断引脚向CPU发送的中断信号。 SYSINTR:逻辑中断,这是操作系统或者驱动程序直接调用的中断号,它是通过OAL把物理中断信号映射成为了OEM定义的逻辑中断号。 ISR(Interrupt Service Routine):中断服务例程,处于内核模式,ISR的主要职责就是判断输入的物理中断号,转化为相应的逻辑中断号。 IST(Interrupt Service Thread):中断服务线程,处于用户模式,IST是真正的中断处理程序,处理相关的中断请求就是在IST中实现的。 Windows CE 的中断处理模型如下图所示。简单来说,外围设备向内核发出了一个物理中断,ISR捕捉到这个物理中断,并且转化为相应的逻辑中断,将逻辑事件与一个事件关联,IST通过事件响应,进入中断处理程序,一次中断结束。
一. 中断处理过程 当一个中断发生时,处理器将控制内核中的一个异常操作,然后调用ISR注册到当前的中断。ISR负责把当前的物理中断转化为一个逻辑中断,同时给内核返回一个逻辑中断号。内核将设置一个事件与该逻辑中断相关联。IST将等到这个事件,直到该事件触发,IST处理中断请求。 下图是一个中断处理内部结构图:
上图的整个流程为:
(1). 如果内核的捕捉异常代码接收到一个硬件中断,那么内核接着就会识别一个异常,并且提交相应的硬件中断。 (2). 内核的中断管理器通知ISR禁用当前中断,直到中断处理完成,才能再重新启动当前中断。这个阶段过程中,允许中断嵌套,也就是允许高优先级的中断触发。 (3). 异常管理器调用ISR来响应这个中断。 (4). 内核接收到ISR的返回值后,依据该返回值来决定如何处理中断。 (5). 内核触发中断支持处理器来唤醒IST并激活该线程。 (6). IST响应相应的中断,如果有需要,IST调用个中I/O函数来访问响应的硬件来完成操作。 (7). 当IST完成中断处理工作后,调用InterruptDone()函数来通知内核。 (8). 内核调用OAL中的函数OEMInterruptDone()函数来宣告所有中断处理工作已经完成。OAL通知硬件重新打开该中断。至此,一次中断处理结束。
二. 中断服务例程ISR ISR是运行在内核当中的一段代码,通常通过OEM实现。ISR主要是用于相应物理中断,并决定如何处理该中断。如果中断需要被内核和IST进一步处理,那么ISR会返回一个逻辑中断号SYSINTR_XXX;如果该中断不需要被进一步的处理,那么ISR只需要返回SYSINTR_NOP给内核。一个ISR程序必须非常的高效,从而避免对硬件中断相应的延迟。如果一个ISR响应太慢,从用户来看,就会认为这个设备死机了。 从上面看来,我们也可以在ISR中直接完成对中断的请求,并且完成相应的操作。但是这不是很好的办法,我们应该让IST来完成大多数的工作。 下面以一个SMDK2410的例子,该函数位于%WINCEROOT% /PLATFORM/COMMON/SRC/ARM/SAMSUNG/S3C2410X/INTR下,在SMDK2410种,OEMInterruptHandler()函数充当了ISR的角色: ULONG OEMInterruptHandler(ULONG ra) { //得到正在处理的物理中断号 irq = INREG32(&g_pIntrRegs->INTOFFSET);
//系统时钟处理 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();
} else {
//禁止同类的中断 mask = 1 << irq; SETREG32(&g_pIntrRegs->INTMSK, mask);
}
//清空中断寄存器 OUTREG32(&g_pIntrRegs->SRCPND, mask); OUTREG32(&g_pIntrRegs->INTPND, mask);
//可挂载ISR 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; }
三. 中断服务线程IST IST其实就是一个普通的用户态线程,它负责处理相应中断的大多数操作。前面的内容中,我们提到过IST线程是在等待到相应事件才会进行处理的,所以在大多数情况下,IST都是空闲的。实现IST的常见函数如下: InterruputInitialize():该函数负责把某个逻辑中断与一个Event内核对象关联起来。 WaitForSingleObject():该函数阻塞当前进程,等待某个EVENT内核对象事件发生。 InterruptDone():该函数用来告诉操作系统,对该中断已经处理完成,操作系统可以重新开启该中断。 实现IST必须的步骤: (1).创建一个结构体用来保存中断处理的相关数据。 (2).当IST触发时使用CreateEvent()函数。 (3).通过注册表读取物理中断号和逻辑中断号,在驱动加载前允许OAL把物理中断号映射成逻辑中断号。 (4).保存创建线程句柄。 具体例子就不在这里列举出来了。
参考书籍: Windows CE 嵌入式操作系统。 Windows CE 设备驱动及BSP开发指南。 |
|