Windows Embedded CE 6.0的中斷處理過程主要分為兩部分:
- 中斷服務(wù)例程(ISR):處于內(nèi)核中的低級處理程序,中斷發(fā)生時首先被調(diào)用。
- 中斷服務(wù)線程(IST):處于驅(qū)動或者應(yīng)用中的中斷處理線程,由系統(tǒng)調(diào)度,完成大部分的中斷處理工作。
ISR的實(shí)現(xiàn)在OAL(OEM適配層)中,它只處理最低級的中斷響應(yīng),通常是獲取IRQ和SYSINTR并設(shè)置MCU內(nèi)部的中斷控制寄存器。中斷處理的主要部分在驅(qū)動或者應(yīng)用的中斷處理線程中。中斷處理線程與其他普通線程一樣,使用同一個線程優(yōu)先級管理系統(tǒng)。ISR和IST之間通過事件對象進(jìn)行同步。IST中創(chuàng)建一個事件對象,并使用函數(shù)WaitForSingleObject()等待該事件被觸發(fā)。ISR中通知內(nèi)核觸發(fā)相應(yīng)的事件對象。Windows Embedded CE 6.0的中斷處理的過程如下圖所示。
Windows Embedded CE 6.0的中斷處理過程
在其他的一些嵌入式操作系統(tǒng)中,在介紹中斷處理時經(jīng)常會提到一個中斷向量表的概念,如uC/OS。當(dāng)中斷發(fā)生時它會進(jìn)入IRQ的處理程序,并根據(jù)IRQ的值跳轉(zhuǎn)到事先分配好的中斷向量表相應(yīng)的中斷處理函數(shù)中。但在WinCE中實(shí)際上并不存在中斷向量表的概念,而只有一個異常向量表,對應(yīng)于MCU的幾種運(yùn)行模式。WinCE的中斷處理對應(yīng)于兩個異常IRQHandler和FIQHandler,通常我們使用的是IRQHandler。當(dāng)外部中斷產(chǎn)生時,系統(tǒng)執(zhí)行IRQHandler(),IRQHandler()中調(diào)用OEMInterruptHandler()獲取IRQ對應(yīng)的SYSINTR,然后根據(jù)SYSINTR調(diào)用函數(shù)OEMNotifyIntrOccurs()觸發(fā)與SYSINTR關(guān)聯(lián)的事件,最后由IST完成主要的中斷處理工作。這種中斷處理機(jī)制在一定程度上影響了系統(tǒng)的實(shí)時性,但提高了IST的靈活性。有關(guān)WinCE系統(tǒng)實(shí)時性分析,將在另外一篇中描述。
下面結(jié)合C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\DRIVERS的PWRBUTTON驅(qū)動進(jìn)行分析。該驅(qū)動也是一個流驅(qū)動,所以可以用驅(qū)動調(diào)試助手進(jìn)行動態(tài)加載和卸載,但需要對代碼進(jìn)行相應(yīng)的修改,否則會出問題。
首先看PBT_Init()函數(shù),代碼如下:
 Code
DWORD
PBT_Init(DWORD dwContext)
  {
DWORD IDPowerButtonThread;
DWORD IDResetButtonThread;
HMODULE hmCore;

//
// 從CORE Library中獲取電源管理器"SetSystemPowerState"的函數(shù)指針/
pfnSetSystemPowerState = NULL;

hmCore = (HMODULE) LoadLibrary(_T("coredll.dll"));

if(hmCore != NULL)
 {
pfnSetSystemPowerState = (PFN_SetSystemPowerState) GetProcAddress(hmCore, _T("SetSystemPowerState"));

if(pfnSetSystemPowerState == NULL)
 {
FreeLibrary(hmCore);
}
}

//初始化相關(guān)的虛擬內(nèi)存地址
InitializeAddresses();

// 創(chuàng)建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)
  {
//初始化中斷寄存器,使能相應(yīng)的中斷
EnablePowerButtonInterrupt();

//創(chuàng)建一個事件
PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

//
// 根據(jù)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);
}
//關(guān)聯(lián)SYSINTR和之前創(chuàng)建的事件
if (!(InterruptInitialize(PwrButtonSysIntr, PwrButtonIntrEvent, 0, 0)))
 {
RETAILMSG(1, (TEXT("ERROR: PwrButton: Interrupt initialize failed.\r\n")));
}

//POWER Button按下的處理程序
for (;;)
 {
WaitForSingleObject(PwrButtonIntrEvent, INFINITE);
if (PowerButtonIsPushed()) //確認(rèn)按鍵確實(shí)被按下,消除抖動
 {
Sleep(200); //延遲200ms,排除長按的情況
if (!PowerButtonIsPushed()) //按鍵被有效釋放
 {
//
//關(guān)閉系統(tǒng)
//
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();
}
//
//結(jié)束當(dāng)前線程的時間片
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);
}
}
以上代碼結(jié)構(gòu)清晰,不再贅述。但這樣編譯出來的驅(qū)動是不能通過驅(qū)動調(diào)試助手動態(tài)加載的,必須進(jìn)行相應(yīng)的修改才行。主要原因是沒有善始善終,分配的系統(tǒng)邏輯中斷沒有釋放,系統(tǒng)邏輯中斷與事件的關(guān)聯(lián)也沒有取消。實(shí)驗(yàn)現(xiàn)象是,能通過驅(qū)動調(diào)試助手加載卸載,但中斷并不能正常工作了。下面介紹一下解決這個問題的辦法。
首先定義一個全局變量g_bThreadExit初始化為FALSE。IST修改后的代碼如下:
 Code
static DWORD
PowerButtonIntrThread(PVOID pArg)
  {
//初始化中斷寄存器,使能相應(yīng)的中斷
EnablePowerButtonInterrupt();

//創(chuàng)建一個事件
PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

//
// 根據(jù)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);
}
//關(guān)聯(lián)SYSINTR和之前創(chuàng)建的事件
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()) //確認(rèn)按鍵確實(shí)被按下,消除抖動
 {
Sleep(200); //延遲ms,排除長按的情況
if (!PowerButtonIsPushed()) //按鍵被有效釋放
 {
//
//關(guān)閉系統(tǒng)
//
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();
}
//
//結(jié)束當(dāng)前線程的時間片
//
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之間的關(guān)聯(lián)
KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR,&PwrButtonSysIntr, sizeof(UINT32),NULL,0, NULL);
//取消Event與PwrButtonSysIntr之間的關(guān)聯(lián)
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")));
//設(shè)置退出線程的標(biāo)志
g_bThreadExit = TRUE;
//模擬一個中斷事件
SetInterruptEvent(PwrButtonSysIntr);
//延遲500ms,確保IST退出
Sleep(500);

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

總的來說,WinCE中斷處理過程結(jié)構(gòu)清晰,方便開發(fā)人員靈活設(shè)計IST。在使用驅(qū)動調(diào)試助手調(diào)試有關(guān)中斷的驅(qū)動程序時,需要善始善終,否則會出現(xiàn)中斷不能正常工作的情況。
|