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系統實時性分析,將在另外一篇中描述。
下面結合C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\DRIVERS的PWRBUTTON驅動進行分析。該驅動也是一個流驅動,所以可以用驅動調試助手進行動態加載和卸載,但需要對代碼進行相應的修改,否則會出問題。
首先看PBT_Init()函數,代碼如下:
 Code
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,代碼如下。
 Code
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修改后的代碼如下:
 Code
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()修改后的代碼如下:
 Code
BOOL
PBT_Deinit(DWORD dwContext)
  {
RETAILMSG(1, (TEXT("PBT: PBT_Deinit()\r\n")));
//設置退出線程的標志
g_bThreadExit = TRUE;
//模擬一個中斷事件
SetInterruptEvent(PwrButtonSysIntr);
//延遲500ms,確保IST退出
Sleep(500);

return (TRUE);
}
經過以上修改,該中斷驅動程序就可以通過驅動調試助手動態加載和卸載,并能正常工作了。另外,在模擬器中由于沒有外部中斷按鍵,可以通過創建一個特定名稱的事件與中斷關聯,并在另外一個應用或者驅動中設置該事件以模擬一個外部中斷按鍵的觸發,這種方法也可以在實際平臺中根據需要使用。示例代碼如下:
 Code
//打開與中斷關聯的事件
gIntrEvent = CreateEvent(NULL, FALSE, FALSE, _T("PBTINTR"));
//設置該事件,模擬一個中斷的觸發
SetEvent(gIntrEvent);
IST中創建與中斷關聯的事件代碼修改如下:
PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, _T("PBTINTR"));

總的來說,WinCE中斷處理過程結構清晰,方便開發人員靈活設計IST。在使用驅動調試助手調試有關中斷的驅動程序時,需要善始善終,否則會出現中斷不能正常工作的情況。
|