Windows Embedded CE 6.0的中断处理过程主要分为两部分:
中断服务例程( ISR):处于内核中的低级处理程序,中断发生时首先被调用。 中断服务线程( IST):处于驱动或者应用中的中断处理线程,由系统调度,完成大部分的中断处理工作。
ISR的实现在 OAL( OEM适配层)中,它只处理最低级的中断响应,通常是获取 IRQ和 SYSINTR并设置 MCU内部的中断控制寄存器。中断处理的主要部分在驱动或者应用的中断处理线程中。中断处理线程与其他普通线程一样,使用同一个线程优先级管理系统。 ISR和 IST之间通过事件对象进行同步。 IST中创建一个事件对象,并使用函数 WaitForSingleObject()等待该事件被触发。 ISR中通知内核触发相应的事件对象。 Windows Embedded CE 6.0的中断处理的过程如下图 所示。
Windows Embedded CE 6.0
的中断处理过程
在其他的一些嵌入式操作系统中,在介绍中断处理时经常会提到一个中断向量表的概念,如 uC/OS。当中断发生时它会进入 IRQ的处理程序,并根据 IRQ的值跳转到事先分配好的中断向量表相应的中断处理函数中。但在 WinCE中实际上并不存在中断向量表的概念,而只有一个异常向量表,对应于 MCU的几种运行模式。 WinCE的中断处理对应于两个异常 IRQHandler和 FIQHandler,通常我们使用的是 IRQHandler。当外部中断产生时,系统执行 IRQHandler(), IRQHandler()中调用 OEMInterruptHandler()获取 IRQ对应的 SYSINTR,然后根据 SYSINTR调用函数 OEMNotifyIntrOccurs()触发与 SYSINTR关联的事件,最后由 IST完成主要的中断处理工作。这种中断处理机制在一定程度上影响了系统的实时性,但提高了 IST的灵活性。有关 WinCE系统实时性分析,将在另外一篇中描述。
下面结合 DEVICEEMULATOR 的 PWRBUTTON 驱动进行分析。该驱动也是一个流驱动,所以可以用驱动调试助手进行动态加载和卸载,但需要对代码进行相应的修改,否则会出问题。
首先看 PBT_Init() 函数,代码如下:
DWORD PBT_Init(DWORD dwContext) { DWORD IDPowerButtonThread; DWORD IDResetButtonThread; HMODULE hmCore; // // 从CORE Library中获取电源管理器"SetSystemPowerState"的函数指针/ pfnSetSystemPowerState = NULL; hmCore = (HMODULE) LoadLibrary(_T( " coredll.dll " )); if (hmCore != NULL) { pfnSetSystemPowerState = (PFN_SetSystemPowerState) GetProcAddress(hmCore, _T( " SetSystemPowerState " )); if (pfnSetSystemPowerState == NULL) { FreeLibrary(hmCore); } } // 初始化相关的虚拟内存地址 InitializeAddresses(); // 创建POWER Button的IST和RESET Button的IST ResetButtonIntrThreadHandle = CreateThread( 0 , 0 , (LPTHREAD_START_ROUTINE) ResetButtonIntrThread, 0 , 0 , & IDResetButtonThread); if (ResetButtonIntrThreadHandle == 0 ) { RETAILMSG( 1 , (TEXT( " PBT: CreateThread() Fail\r\n " ))); } PowerButtonIntrThreadHandle = CreateThread( 0 , 0 , (LPTHREAD_START_ROUTINE) PowerButtonIntrThread, 0 , 0 , & IDPowerButtonThread); if (PowerButtonIntrThreadHandle == 0 ) { RETAILMSG( 1 , (TEXT( " PBT: CreateThread() Fail\r\n " ))); } return (dwContext); }
RESET Button的 IST和 POWER Button的 IST基本一致,所以这里只分析 POWER Button的 IST,代码如下。
static DWORD PowerButtonIntrThread(PVOID pArg) { // 初始化中断寄存器,使能相应的中断 EnablePowerButtonInterrupt(); // 创建一个事件 PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // // 根据IRQ获取一个SYSINTR // if ( ! KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, & PwrButtonIrq, sizeof PwrButtonIrq, & PwrButtonSysIntr, sizeof PwrButtonSysIntr, NULL)) { RETAILMSG( 1 , (TEXT( " PBT: Error! Failed to request sysintr value for power button interrupt.\r\n " ))); return ( 0 ); } // 关联SYSINTR和之前创建的事件 if ( ! (InterruptInitialize(PwrButtonSysIntr, PwrButtonIntrEvent, 0 , 0 ))) { RETAILMSG( 1 , (TEXT( " ERROR: PwrButton: Interrupt initialize failed.\r\n " ))); } // POWER Button按下的处理程序 for (;;) { WaitForSingleObject(PwrButtonIntrEvent, INFINITE); if (PowerButtonIsPushed()) // 确认按键确实被按下,消除抖动 { Sleep( 200 ); // 延迟200ms,排除长按的情况 if ( ! PowerButtonIsPushed()) // 按键被有效释放 { // // 关闭系统 // if (pfnSetSystemPowerState != NULL) { RETAILMSG( 1 ,(TEXT( " PBT: Signalling power manager to suspend \r\n " ))); pfnSetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE); } else { RETAILMSG( 1 ,(TEXT( " PBT: Suspending by calling PowerOffSystem \r\n " ))); PowerOffSystem(); } // // 结束当前线程的时间片 Sleep( 0 ); } else RETAILMSG(1 ,(TEXT( " PBT: Button held too long (ignored)\r\n " ))); } else RETAILMSG(1 ,(TEXT( " PBT: Feeble button press or noise triggered it (ignored)\r\n " ))); InterruptDone(PwrButtonSysIntr); } }
以上代码结构清晰,不再赘述。但这样编译出来的驱动是不能通过驱动调试助手动态加载的,必须进行相应的修改才行。主要原因是没有善始善终,分配的系统逻辑中断没有释放,系统逻辑中断与事件的关联也没有取消。实验现象是,能通过驱动调试助手加载卸载,但中断并不能正常工作了。下面介绍一下解决这个问题的办法。
首先定义一个全局变量 g_bThreadExit初始化为 FALSE。IST修改后的代码如下:
static DWORD PowerButtonIntrThread(PVOID pArg) { // 初始化中断寄存器,使能相应的中断 EnablePowerButtonInterrupt(); // 创建一个事件 PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // // 根据IRQ获取一个SYSINTR // if ( ! KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, & PwrButtonIrq, sizeof PwrButtonIrq, & PwrButtonSysIntr, sizeof PwrButtonSysIntr, NULL)) { RETAILMSG( 1 , (TEXT( " PBT: Error! Failed to request sysintr value for power button interrupt.\r\n " ))); return ( 0 ); } // 关联SYSINTR和之前创建的事件 if ( ! (InterruptInitialize(PwrButtonSysIntr, PwrButtonIntrEvent, 0 , 0 ))) { RETAILMSG( 1 , (TEXT( " ERROR: PwrButton: Interrupt initialize failed.\r\n " ))); } // POWER Button按下的处理程序 for (;;) { WaitForSingleObject(PwrButtonIntrEvent, INFINITE); if (g_bThreadExit) { break ; } if (PowerButtonIsPushed()) // 确认按键确实被按下,消除抖动 { Sleep( 200 ); // 延迟ms,排除长按的情况 if ( ! PowerButtonIsPushed()) // 按键被有效释放 { // // 关闭系统 // if (pfnSetSystemPowerState != NULL) { RETAILMSG( 1 ,(TEXT( " PBT: Signalling power manager to suspend \r\n " ))); pfnSetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE); } else { RETAILMSG( 1 ,(TEXT( " PBT: Suspending by calling PowerOffSystem \r\n " ))); PowerOffSystem(); } // // 结束当前线程的时间片 // Sleep( 0 ); } else RETAILMSG(1 ,(TEXT( " PBT: Button held too long (ignored)\r\n " ))); } else RETAILMSG(1 ,(TEXT( " PBT: Feeble button press or noise triggered it (ignored)\r\n " ))); InterruptDone(PwrButtonSysIntr); } // 取消IRQ与SYSINTR之间的关联 KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, & PwrButtonSysIntr, sizeof (UINT32),NULL, 0 , NULL); // 取消Event与PwrButtonSysIntr之间的关联 InterruptDisable(PwrButtonSysIntr); CloseHandle(PwrButtonIntrEvent); RETAILMSG( 1 , (TEXT( " PowerButtonIntrThread Exit.\r\n " ))); return 0 ; }
PBT_Deinit() 修改后的代码如下:
BOOL PBT_Deinit(DWORD dwContext) { RETAILMSG( 1 , (TEXT( " PBT: PBT_Deinit()\r\n " ))); // 设置退出线程的标志 g_bThreadExit = TRUE; // 模拟一个中断事件 SetInterruptEvent(PwrButtonSysIntr); // 延迟500ms,确保IST退出 Sleep( 500 ); return (TRUE); }
经过以上修改,该中断驱动程序就可以通过驱动调试助手动态加载和卸载,并能正常工作了。另外,在模拟器中由于没有外部中断按键,可以通过创建一个特定名称的事件与中断关联,并在另外一个应用或者驱动中设置该事件以模拟一个外部中断按键的触发,这种方法也可以在实际平台中根据需要使用。示例代码如下:
// 打开与中断关联的事件 gIntrEvent = CreateEvent(NULL, FALSE, FALSE, _T( " PBTINTR " )); // 设置该事件,模拟一个中断的触发 SetEvent(gIntrEvent); IST中创建与中断关联的事件代码修改如下: PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, _T( " PBTINTR " ));
总的来说, WinCE中断处理过程结构清晰,方便开发人员灵活设计 IST。在使用驱动调试助手调试有关中断的驱动程序时,需要善始善终,否则会出现中断不能正常工作的情况。