WinCE中断结构分析
前一段時間研究了一下WinCE下的中斷結(jié)構(gòu),整理了一下,希望與大家討論。
最下面有PDF版本下載,便于保存
版權(quán)申明:本文版權(quán)歸ARMCE所有,轉(zhuǎn)載請保留所有原文內(nèi)容及 ARMCE標(biāo)識并注明出
自 ARMCE,禁止任何未經(jīng)作者同意的內(nèi)容修改及再發(fā)布,ARMCE保留所有權(quán)利。
Windows Embedded CE??中斷結(jié)構(gòu)分析
關(guān)鍵字:WinCE,中斷,體系,結(jié)構(gòu)
摘要:本文主要以WinCE??.NET 5.0 為操作系統(tǒng)平臺,ARM為硬件平臺,分析了WinCE下
中斷的結(jié)構(gòu)與實(shí)現(xiàn)方式
作者:ETDrawer@ARMCE
前言
??在嵌入式系統(tǒng)當(dāng)中,對于中斷的處理是非常重要的一部分內(nèi)容。許多外圍設(shè)備都需要通
過中斷來實(shí)現(xiàn)自己的功能或者與系統(tǒng)內(nèi)核交互,系統(tǒng)時鐘本身也是由時鐘中斷產(chǎn)生的。所以
本文旨在分析WinCE下的中斷的結(jié)構(gòu),以及常用的幾種實(shí)現(xiàn)方式,來幫助讀者了解WinCE
中斷體系及實(shí)現(xiàn)自己的中斷處理結(jié)構(gòu)。
??下面的介紹如非特殊說明,均以 ARM 架構(gòu)為硬件基礎(chǔ),操作系統(tǒng)代碼使用 Windows
embedded CE 5.0。
一 WinCE中斷體系結(jié)構(gòu)
??先看圖 1:
下載 (30.76 KB)
剛才
圖 1 WinCE中斷體系結(jié)構(gòu)
??這張是經(jīng)典的說明中斷體系的圖,我們可以通過分析這張圖來了解WinCE的中斷體系。??
??從結(jié)構(gòu)上看,WinCE中斷涉及4 層,即:硬件層、內(nèi)核層、OAL層、IST處理層。了
解這 4層之間的交互傳遞將對我們了解WinCE中斷處理很有幫助。
1 硬件層:
??硬件層就是實(shí)際觸發(fā)中斷的硬件,這里主要有兩方面作用,一個是觸發(fā)中斷,第二個是
enable/disable硬件中斷。
2 內(nèi)核層:
??這一層由內(nèi)核來處理,包括中斷異常產(chǎn)生后跳轉(zhuǎn)到相應(yīng)的ISR,以及根據(jù)SYSINTR 來
觸發(fā)相應(yīng)的Event。關(guān)于SYSINTR 和 IRQ 的概念后面會說明。
3 OAL層
??這一層主要就是我們需要實(shí)現(xiàn)的代碼了,來識別硬件IRQ,對應(yīng)到SYSINTR。
4 IST處理層
??一般使用 IST 來做實(shí)際的中斷處理,這樣不會占用很多的鎖定系統(tǒng)時間來處理中斷,
但是對中斷的實(shí)時性大打折扣
二??IRQ,ISR,IST和 SYSINTR
說到這里先解釋下IRQ,ISR,IST及 SYSINTR 的概念、意義及相互關(guān)系。
IRQ:
IRQ (Interrupt request),中斷請求。
這里就是外設(shè)或其它請求服務(wù)的設(shè)備發(fā)出來的中斷。屬于硬件中斷,可能是一個電平觸發(fā)的
GPIO 中斷,也可能是內(nèi)部DMA的一個中斷。
ISR:
ISR (Interrupt service routine),??中斷處理程序。
WinCE實(shí)際上使用 ISR來處理中斷,即默認(rèn)的中斷入口函數(shù),在 ARM體系中,系統(tǒng)默認(rèn)的
ISR就是 OEMInterruptHandler
IST:
IST (Interrupt service thread),??中斷服務(wù)線程。
在 ARM 的結(jié)構(gòu)中,ISR 一般不會用來進(jìn)行任何實(shí)際的操作,而只是返回一個 SYSINTR,
實(shí)際的操作全部在IST中完成,IST一般是在Device Manager 的一個線程中運(yùn)行的一段
高優(yōu)先級的應(yīng)用程序代碼,用來服務(wù)實(shí)際的中斷請求。
SYSINTR:
在 WinCE中,SYSINTR 就是 system interrupt,就是一個操作系統(tǒng)的邏輯中斷。
一般對于中斷的處理方式都是將一個IRQ映射為一個或者多個(中斷共享)SYSINTR,而后,
在實(shí)際的ISR中根據(jù)IRQ返回一個對應(yīng)的SYSINTR用來告訴操作系統(tǒng)需要服務(wù)的邏輯對
象。
使用邏輯中斷的好處當(dāng)然就是可以實(shí)現(xiàn)虛擬的中斷(一個 SYSINTR 就被 OS 認(rèn)為是一個獨(dú)
立中斷)和中斷共享(單 IRQ對應(yīng)多 SYSINTR)。
邏輯中斷是WinCE需要處理的實(shí)際對象,至于這個對象是一個共享的IRQ,還是一個虛擬
的中斷,還是獨(dú)立的物理中斷,系統(tǒng)并不過問,從而隔離了硬件與邏輯,我們的 ISR 需要
做的也正是這種物理中斷到邏輯中斷的映射。
三 WinCE中斷處理原理
??下面基于 ARM 體系,來介紹 WinCE 中斷處理的流程與原理。
??對于一個硬件IRQ中斷,系統(tǒng)內(nèi)核在捕獲之后,會交給 OEMInterruptHandler 處理,
這個函數(shù)就是我們實(shí)現(xiàn)中斷處理的中心函數(shù),首先我們從CPU 的寄存器里獲得中斷的信息,
這些信息告訴我們到底是哪個 IRQ 源觸發(fā)了中斷。
一般實(shí)現(xiàn)中斷服務(wù)的方式有以下幾種:
1. 簡單中斷處理——ISR模型
??最簡單的中斷處理辦法就是在ISR中直接處理,這里就是指在OEMInterruptHandler
中直接對中斷源進(jìn)行判斷,然后調(diào)用服務(wù)程序。
??這種方式的優(yōu)點(diǎn)和缺點(diǎn)一樣明顯。
??優(yōu)點(diǎn):快速的響應(yīng)了中斷處理,使系統(tǒng)的實(shí)時性能增加不少
??缺點(diǎn):由于進(jìn)入OEMInterruptHandler的時候關(guān)閉了系統(tǒng)中斷(當(dāng)然你可以在ISR中
自己打開中斷,不過處理起來較麻煩),所以如果處理時間過長,其他中斷很可能被忽略,
造成中斷丟失。并且系統(tǒng)被鎖定,使得系統(tǒng)任務(wù)的響應(yīng)變慢。
2. 中斷處理的一般流程——IST模型
??前面看到了 ISR 模型的優(yōu)缺點(diǎn)。作為WinCE,主要定位還是民用的消費(fèi)類電子產(chǎn)品,
所以,對于中斷響應(yīng)的實(shí)時性不是特別高,所以系統(tǒng)的運(yùn)行響應(yīng)速度就顯得更加重要。而且
目前的嵌入式設(shè)備的處理速度越來越高,已經(jīng)幾乎達(dá)到了當(dāng)時奔 3 的水平。所以 ISR 的模
型并不適用于WinCE。
如果把中斷服務(wù)程序當(dāng)作一個系統(tǒng)進(jìn)程或者線程來處理,這樣就不會造成系統(tǒng)被鎖定,
中斷被屏蔽等問題,使得中斷服務(wù)程序和其它進(jìn)程、線程一樣被系統(tǒng)管理調(diào)度。于是就有了
IST的概念
IST 模型的想法是,在 ISR 中判斷到具體的中斷源 IRQ,就返回一個標(biāo)志,用來標(biāo)記
需要哪個程序來服務(wù)中斷,然后重新打開中斷,恢復(fù)系統(tǒng)調(diào)度,系統(tǒng)根據(jù)這個標(biāo)志來激活相
應(yīng)的程序來服務(wù)中斷。
這個就是最常用的中斷處理模型。使得中斷被關(guān)閉,系統(tǒng)被鎖定的時間最短。
在 WinCE中,經(jīng)常使用的就是建立中斷服務(wù)線程(IST),然后以IRQ 來申請一個系統(tǒng)
邏輯中斷號(SYSINTR),創(chuàng)建一個事件(Event),將 Event 與 SYSINTR 綁定,隨后 IST
阻塞在等待Event上面。
ISR 中只給系統(tǒng)返回一個 SYSINTR,系統(tǒng)根據(jù)綁定關(guān)系激活相應(yīng)的Event,使得隨后
的 IST得以運(yùn)行。
這就是 IST的一般服務(wù)流程
IST模型的缺點(diǎn)就是中斷服務(wù)的延遲較大,從 ISR 返回,到 IST開始運(yùn)行,中間需要
花費(fèi)一定的時間,相比 ISR 模型的中斷實(shí)時性較差,但是這種代價對于我們的應(yīng)用是值得
的。
四??IST模型的實(shí)現(xiàn)
??下面我們來看IST模型具體在我們的驅(qū)動中是如何實(shí)現(xiàn)的。
??上面已經(jīng)介紹了 IST 模型的一般服務(wù)流程,下面我們針對驅(qū)動程序?qū)嵗?#xff0c;來分析具體
的實(shí)現(xiàn)步驟。
1 驅(qū)動程序中 IST的構(gòu)建與中斷初始化
上面介紹的 IST流程中,很多步驟都是WinCE的內(nèi)置支持,也就是說你只要調(diào)用相應(yīng)
的 API就可以實(shí)現(xiàn)功能了,不需要自己編寫太多的代碼。只需要實(shí)現(xiàn)一些流程代碼。
首先是驅(qū)動程序端的中斷初始化。假設(shè)現(xiàn)在有一個驅(qū)動程序,需要服務(wù)中斷源,IRQ
為 0x12。
a)??以 IRQ 為參數(shù),申請 SYSINTR,方法為調(diào)用
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &(dwIrq),? ?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?sizeof(UINT32), &dwSysIntr,? ?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?sizeof(UINT32), NULL)
其中 dwIrq為IRQ號,即0x12
dwSysIntr 為系統(tǒng)中斷號,也就是調(diào)用返回的結(jié)果存放的位置
/* ARMCE深入
實(shí)際這個 IOControl調(diào)用的是函數(shù) OALIoCtlHalRequestSysIntr,該函數(shù)的位置
在
WINCE500\PLATFORM\COMMON\SRC\COMMON\INTR\COMMON\Ioctl.c
最終調(diào)用 OALIntrRequestSysIntr,這個由 OEM 自己實(shí)現(xiàn),一般就是維護(hù)了 2
張表,一張是從 SYSINTR 到 IRQ 號碼的映射表,另一張是 IRQ 到 SYSINTR 的
映射表,兩者是對應(yīng)的關(guān)系。
這里注意一點(diǎn),一個IRQ 映射為多個SYSINTR 是支持的,也就是共享中斷,但是
系統(tǒng)默認(rèn)的實(shí)現(xiàn)并沒有支持一個 SYSINTR 映射為多個 IRQ,你可以自己實(shí)現(xiàn)。
這兩張表對于后面在OEMInterruptHandler中由IRQ查找SYSINTR提供了方便
*/
b)??創(chuàng)建與 SYSINTR 綁定的Event
由于我們的IST是需要Event激活的,所以這里申請一個 Event。
申請 Event的步驟比較簡單和標(biāo)準(zhǔn)
hISTEvent = CreateEvent(0,FALSE,FALSE,NULL);
c)??將SYSINTR 與Event綁定
調(diào)用 InterruptInitialize(dwSysIntr,hISTEvent,0,0)將 SYSINTR 與 Event 綁
定,用來在OEMInterruptHandler 中返回SYSINTR 時激活相對應(yīng)的 Event
/* ARMCE深入
InterruptInitialize 的 代 碼 參 考 SC_InterruptInitialize , 在
E:\WINCE500\PRIVATE\WINCEOS\COREOS\NK\KERNEL\Intrapi.c中
主要做兩件事情:
#1 將 Event 的 Ptr 填入一個數(shù)組,這個數(shù)組是記錄每個 SYSINTR 對應(yīng)激活的
Event
#2 調(diào)用OEMInterruptEnable,使能SYSINTR所對應(yīng)的IRQ,并且將標(biāo)志 IRQ
被引用次數(shù)的變量加1(WinCE6 的代碼中未見這一變量)
*/
d)??創(chuàng)建一個 IST,并且等待hISTEvent
到了這一步,中斷關(guān)于系統(tǒng)方面的初始化基本結(jié)束,剩下的就是創(chuàng)建一個 IST,然
后等待 Event來運(yùn)行中斷服務(wù)代碼,例如:
? ?
while(TRUE) {
WaitForSingleObject(hISTEvent,INFINITE) ==
WAIT_OBJECT_0)
…
}
這里需要注意的是IST什么時候創(chuàng)建都可以,但是在InterruptInitialize之前不要
運(yùn)行 IST 以等待這個 Event,也就是說在 InterruptInitialize 之前不要使用這個
Event,否則會導(dǎo)致InterruptInitialize失敗。
還有就是不要使用WaitForMultipleObjects來等待Event。
在中斷處理完成之后需要調(diào)用 InterruptDone,參數(shù)為該中斷的SYSINTR。來通
知系統(tǒng),中斷處理完成,系統(tǒng)重新使能該中斷
? ? 到了這里,驅(qū)動的中斷初始化工作就全部完成了。
2??OEM層需要做的工作
OEM 層 主 要 是 控 制 IRQ 的 enable??(BSPIntrEnableIrq) 與 disable
(BSPIntrDisableIrq), 當(dāng)然要初始化 IRQ 的配置,使其在正確的觸發(fā)狀態(tài),比如上升延
觸發(fā)
? ? 至此一個中斷處理的IST模型就實(shí)現(xiàn)了,系統(tǒng)在IRQ 觸發(fā)時調(diào)用映射函數(shù),獲得相應(yīng)
IRQ 的 SYSINTR,然后返回合法的SYSINTR給系統(tǒng),系統(tǒng)查表激活相應(yīng)的Event,對應(yīng)
的 IST進(jìn)行中斷服務(wù),然后再次等待 Event。
??3??中斷資源的釋放
? ? 當(dāng)不需要當(dāng)前中斷繼續(xù)服務(wù)的時候可以通過調(diào)用 KernelIoControl 來釋放申請到的
SYSINTR,具體格式為:
??KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR,??dwSysIntr,??sizeof(DWORD),
NULL, 0, NULL);
??其中 dwSysIntr 就是需要釋放的 SYSINTR 號碼。
/* ARMCE深入
??這里實(shí)際調(diào)用的是OALIntrReleaseSysIntr這個函數(shù),由OEM自己實(shí)現(xiàn)。所做的動作也
很簡單,就是從SYSINTR 到 IRQ 和 IRQ 到 SYSINTR 的那兩個映射關(guān)系數(shù)組中刪除映射
關(guān)系。
*/
五 可安裝的 ISR
??1? ? 為什么要使用可安裝 ISR(以下簡稱 IISR)
? ? 需要 IISR 的目的有兩種:
I. 動態(tài)的安裝中斷程序
在系統(tǒng)運(yùn)行的過程中注冊中斷,這種中斷一般是不可預(yù)知設(shè)備的中斷,常用在總線設(shè)備
中,比如PCI設(shè)備
II. 中斷共享
當(dāng)多個中斷源使用同一個中斷號(IRQ)時,就需要使用 IISR 來實(shí)現(xiàn)了
當(dāng)然如果是需要動態(tài)安裝的共享中斷就最適合了。
因?yàn)槲覀兊?IST模型中,中斷服務(wù)程序就是驅(qū)動中的IST,IRQ與 IST是一對一的關(guān)
系。所以在需要動態(tài)添加一個中斷服務(wù)程序的時候就沒有辦法處理了。
同樣由于 IRQ 與 IST 的一一對應(yīng)關(guān)系對于一個 IRQ 對應(yīng)多個需要服務(wù)的 IST 就同樣
沒有辦法處理。
基于上面的情況才會有IISR 的出現(xiàn),IISR 從本質(zhì)上是在ISR 中提供了一個接口,當(dāng)
ISR 調(diào)用 NKCallIntChain時,以此IRQ為參數(shù),在鏈表中依次查找是哪一個服務(wù)程序來
服務(wù)這次 IRQ,然后返回相應(yīng)的 SYSINTR,此后的動作與 IST 模型就基本一樣,通過
SYSINTR 來激活Event,從而啟動相應(yīng)的 IST。
??所以 IISR 的實(shí)現(xiàn)就是動態(tài)的向某一個 IRQ服務(wù)程序鏈表添加結(jié)點(diǎn)的過程。
2 IISR的實(shí)現(xiàn)
下面我們來看看IISR 的具體實(shí)現(xiàn)步驟:
??首先我們需要了解IISR 服務(wù)中斷的實(shí)現(xiàn)原理,如上面描述的,根據(jù)IRQ,來順序調(diào)用
鏈表中的中斷處理程序。所以我們可以有兩個選擇,一個就是類似 ISR 模型,直接在鏈表
中的中斷處理程序中判斷是不是自己的中斷,并且做處理。還有一種方式就是類似 IST 模
型,如果判斷是自己的中斷,則返回一個SYSINTR,以此SYSINTR 來激活I(lǐng)ST。
??無論哪種方法,關(guān)于注冊中斷和查詢中斷的方式是一樣的,下面我們來看下如何將中斷
程序添加到鏈表,又如何在中斷來的時候去搜索鏈表。
??Microsoft提供了一個通用的IISR的處理模型,叫做 GIISR,這是一個以 IST模型處
理 IISR 的模塊,源程序可以在WINCE500\PUBLIC\COMMON\OAK\DRIVERS\GIISR
找到。熟悉了 GIISR,想實(shí)現(xiàn)自己的 IISR 處理程序或者基于 ISR 模型的處理,都比較簡
單了。
??下面我們就分析這種比較通用的處理 IISR 的模型。
a)??首先我們需要以 IRQ 來申請 SYSINTR,并且將 SYSINTR 與 Event 綁定,這些
步驟與IST模型中介紹的一樣,這里就不重復(fù)敘述了,IISR 在這里與 IST模型并
沒有任何的不同。其與 IST 模型的唯一不同點(diǎn)就是如何根據(jù) IRQ 來判斷相應(yīng)的
SYSINTR。
在 IST 模型中是 OEM寫死的一個判斷程序,而 IISR 可以動態(tài)來注冊一個判斷程
序給系統(tǒng)調(diào)用,這是唯一的實(shí)現(xiàn)區(qū)別。
b)??下面我們需要注冊可安裝中斷程序的 dll,和 dll中的中斷處理函數(shù),并且將他們與
某一個特定的IRQ相關(guān)聯(lián)
這個過程是通過調(diào)用LoadIntChainHandler函數(shù)來實(shí)現(xiàn)的。
這里我們的中斷服務(wù)dll叫做”giisr.dll”,處理函數(shù)名叫做”ISRHandler”,對應(yīng)IRQ
為0x20,則函數(shù)調(diào)用形式如下:
HANDLE??hIsrHandler??=??LoadIntChainHandler(TEXT(“giisr.dll”),
TEXT(“ISRHandler”), 0x20);
/* ARMCE深入
LoadIntChainHandler的源代碼可以在WINCE500\PRIVATE\WINCEOS\
COREOS\NK\KERNEL\Loader.c中找到,就是函數(shù)SC_LoadIntChainHandler
WinCE6 在 KDriver.c 中的 NKLoadIntChainHandler,兩者功能大致相同,只
是在一些結(jié)構(gòu)體定義上略有不同
其主要功能就是加載 dll 到 NK,并且獲得三個函數(shù)的指針,一個是
CreateInstance,一個是你剛才傳進(jìn)來的處理函數(shù),這里就是 ISRHandler,還
一個就是 IOControl,后面會用到。
首先調(diào)用CreateInstance 來獲得一個實(shí)例的數(shù)據(jù),這個數(shù)據(jù)就是一個 index,用
來標(biāo)示其中的一個中斷處理程序的索引。
這里可能需要解釋下 GIISR 的處理原理。我們所有的可安裝中斷都通過 giisr.dll
里面的 ISRHandler來處理,在NKCallIntChain被調(diào)用的時候,會遍歷所有注冊
到這個 IRQ 的中斷處理函數(shù),這里全部都是同一個函數(shù) ISRHandler。那么就需
要可以區(qū)分每一次調(diào)用,所以就在 giisr 模塊里面維護(hù)一個數(shù)組,每一個中斷服務(wù)
程序占用一個數(shù)組成員,這些數(shù)組成員的Index就是他們在giisr里面的唯一標(biāo)識。
所以 CreateInstance 的任務(wù)就是查找數(shù)組,找到第一個空閑位置,將 Index 返
回。在ISRHandler 被調(diào)用到的時候,會將這個Index傳遞進(jìn)去,根據(jù)這個 Index,
ISRHandler 就能夠知道是數(shù)組中哪個成員正在被查詢,進(jìn)而確定是不是這個成員
需要處理中斷請求,進(jìn)而確定該返回的 SYSINTR。詳細(xì)的步驟下面會一一說明,
大家先有一個概念
在從 CreateInstance 返回了可用的數(shù)組 Index 之后,調(diào)用 HookIntChain,此
函數(shù)在Kdriver.c中。這個函數(shù)的功能比較簡單,我們先了解下共享中斷在系統(tǒng)中
的處理。
前面有所提到,在調(diào)用 NKCallIntChain 時會遍歷一個鏈表,每個鏈表頭就是系統(tǒng)
維護(hù)的一個數(shù)組中的一個成員,每一個 IRQ 號碼都對應(yīng)數(shù)組中的一個成員,這個
數(shù)組就是 pIntChainTable。IRQ 按照其號碼在數(shù)組中對應(yīng)相應(yīng)的數(shù)組成員,注意
pIntChainTable 是一個 256 個成員的數(shù)組,這也就意味著你的 IRQ 號碼數(shù)字不
能超過 255,當(dāng)然這是指你傳遞進(jìn)來的 IRQ 號碼,如果你的 IRQ 號碼都是大于
255 的,可以對實(shí)際 IRQ 號碼做處理,保證其數(shù)字值在 0-255,比如對 256 取模,
這樣當(dāng)然就可以傳遞進(jìn)來了。
pIntChainTable中的每個元素都是一個鏈表的頭,當(dāng)你向一個IRQ 添加中斷處理
的時候,實(shí)際是建立了一個新的PINTCHAIN元素,然后向pIntChainTable中的
IRQ 索引位置去添加,如果該位置不為空,則查找這個元素指向的下一個元素,在
這個單向鏈表的操作下,將新的中斷處理程序加入。
這個加入工作就是HookIntChain做的
*/
c)??上一步在GIISR中通過CreateInstance把這個新的中斷處理程序加入GIISR自
己的管理。GIISR 的主要作用就是判斷當(dāng)中斷來的時候,是不是其內(nèi)部數(shù)組中的某
個成員需要服務(wù)中斷。所以需要更多的信息用來判斷中斷是否匹配當(dāng)前的中斷服務(wù)
程序,所以我們需要把信息傳遞進(jìn)去,這里就是調(diào)用KernelLibIoControl。
具體的方法為:
KernelLibIoControl(hIsrHandler,IOCTL_GIISR_INFO,&giisr_info,
sizeof(GIISR_INFO), NULL, 0, NULL);
這里就是把 giisr_info 的內(nèi)容傳遞給剛才注冊的中斷,giisr_info 是一個
GIISR_INFO的結(jié)構(gòu)體,其內(nèi)容如下:
typedef struct _GIISR_INFO {
? ?? ? DWORD SysIntr;? ?? ?? ?? ?? ?? ?? ?? ?? ? // SYSINTR for ISR handler to return
(if associated device is asserting IRQ)
? ?? ? BOOL CheckPort;? ?? ?? ?? ?? ?? ?? ?? ???// If true, check port to see if device is
asserting IRQ
? ?? ? BOOL PortIsIO;? ?? ?? ?? ?? ?? ?? ?? ?? ? // Port is IO port (possibly true only for
x86)
? ?? ? BOOL UseMaskReg;? ?? ?? ?? ?? ?? ?? ?? ?//??If??true,??read??from MaskAddr??to
obtain mask
? ?? ? DWORD PortAddr;? ?? ?? ?? ?? ?? ?? ?? ???// Port Address
? ?? ? DWORD PortSize;? ?? ?? ?? ?? ?? ?? ?? ???// Port data width in bytes
? ?? ? DWORD Mask;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? // Mask??to??use??on??data??port??to
determine if device is asserting IRQ
? ?? ? DWORD MaskAddr;? ?? ?? ?? ?? ?? ?? ?? ???//??Address??of??register??to??use??as
mask
} GIISR_INFO, *PGIISR_INFO;
這些成員都是需要設(shè)置的,具體含義如下
SysIntr:這個中斷所對應(yīng)的系統(tǒng)中斷號,即第一步申請到的 SYSINTR,系統(tǒng)在
確定是當(dāng)前的設(shè)備出發(fā)的 IRQ 之后會返回這個 SysIntr
CheckPort:一般為 TRUE,如果為 FALSE 則直接返回 SysIntr,而不是判斷是
不是當(dāng)前設(shè)備觸發(fā)的中斷
PortIsIO:是不是IO端口,這個可能只是在 x86 下使用,我們置為 FALSE
UseMaskReg:是否使用地址來獲得 Mask,如果為TRUE,則Mask 字段無意義
PortAddr:實(shí)際上是可以判斷中斷是哪個設(shè)備出發(fā)的那個寄存器的地址
PortSize:PortAddr的位寬,標(biāo)志PortAddr是1字節(jié)(BYTE),2 字節(jié)(WORD),
還是 4字節(jié)(DWORD)的寄存器,其他不支持
Mask:一個掩碼位,在UseMaskReg為FALSE 的情況下與PortAddr的值進(jìn)行
位或運(yùn)算,如果不為0,則確定為當(dāng)前設(shè)備觸發(fā)的中斷
MaskAddr:當(dāng) UseMaskReg為 TRUE 的時候,使用這個地址來獲得掩碼的值,
給動態(tài)的判斷中斷提供了接口
仔細(xì)看了上面各個成員的介紹,大家就應(yīng)該可以了解 GIISR 是如何判斷中斷是不
是當(dāng)前設(shè)備產(chǎn)生的。
所有的判斷依據(jù)就是這個結(jié)構(gòu)體。一般我們會將 CheckPort 置為 TRUE,然后讓
系統(tǒng)去讀取PortAddr地址處的值,這個值可以標(biāo)志是否為當(dāng)前設(shè)備觸發(fā)的中斷。
獲得這個值以后,與一個 mask 值進(jìn)行或運(yùn)算(&),如果值不為 0,則認(rèn)為是當(dāng)前
設(shè)備觸發(fā)的中斷。這個 mask 值在 UseMaskReg 為 FALSE 時是成員 Mask,反
之是從 MaskAddr 地址處獲得。
/* ARMCE深入
KernelLibIoControl 的 源 代 碼 可 以 在 Kdriver.c 中 找 到 , 就 是
SC_KernelLibIoControl 。 SC_KernelLibIoControl 直 接 調(diào) 用 的
NKKernelLibIoControl,這個函數(shù)同樣在 kdriver.c 中。分析源碼可知,就是調(diào)
用了 giisr.dll 中的 IOControl,參數(shù)為 IOCTL_GIISR_INFO,就是傳遞進(jìn)去了
一個 GIISR_INFO 結(jié)構(gòu)體,用來給 Index 標(biāo)示的數(shù)組元素賦值,這個 Index 就
是前面CreateInstance返回的那個數(shù)值,通過 hIsrHandler傳遞進(jìn)去。
*/
d)??下面就是啟動IST,等待Event,這里和IST模型沒有任何區(qū)別。
到這里全部的初始化就完成了,可以看出,和IST模型相比就是多了兩步 b)和 c),
這兩步?jīng)Q定了中斷判斷的方式,這也是 IISR的根本所在。
? ?
3??中斷的判斷
下面詳細(xì)介紹下可安裝中斷在 ISR 中被判斷的過程。
同 樣 , 中 斷 到 來 時 進(jìn) 入 的 函 數(shù) 是 OEMInterruptHandler , 在
OEMInterruptHandler中會調(diào)用NKCallIntChain來遍歷該IRQ對應(yīng)注冊的IISR。
這是一個鏈表結(jié)構(gòu),所以對中斷判斷程序是一個順序調(diào)用的過程,即先注冊的設(shè)備先
判斷,如果判斷到正確的結(jié)果,則返回合法的 SYSINTR,OEMInterruptHandler
也同樣返回這個值。所以即使后面的設(shè)備也符合條件,也不會被執(zhí)行。如果整個鏈表
中都沒找到正確的設(shè)備,則返回 SYSINTR_CHAIN。OEMInterruptHandler 在判
斷到返回結(jié)果為SYSINTR_CHAIN 時,即表示請求中斷的設(shè)備不在鏈表中。
/* ARMCE深入
NKCallIntChain 的代碼在 KDriver.c 中,是個非常簡單的函數(shù),以傳遞進(jìn)來的
IRQ 為 Index 找到鏈表的頭,然后去調(diào)用各鏈表節(jié)點(diǎn)的 hIsrHandler,并且以
CreateInstance 的返回值為參數(shù)。當(dāng)有返回的值不是 SYSINTR_CHAIN 時,就返
回 這個值, 反之繼續(xù) 查找, 直到鏈表 尾部, 如果還沒 有找到, 就返 回
SYSINTR_CHAIN。
*/
4??自定義的 IISR
??我們可以不使用GIISR,而自己實(shí)現(xiàn)IISR功能,只要知道了IISR 的原理。
當(dāng)以某一個寄存器或者地址的值,不足以判斷到底是系統(tǒng)中哪個設(shè)備觸發(fā)的中斷
的時候,GIISR 就不是這么好用了。比如多個不同的外設(shè),使用同一個GPIO 來觸發(fā)
中斷。外設(shè)需要讀取多個寄存器或者需要一個復(fù)雜些的計算(不只是簡單的一個&操作)
才能判定中斷是否是其產(chǎn)生的。這時候我們需要使用自己的一套 IISR的處理方式。
當(dāng)然我們不希望去改動整個微軟對于 IISR 的處理結(jié)構(gòu),所以我們就要區(qū)分開來上
面介紹的 GIISR 的模型里,哪些是微軟的架構(gòu),哪些是GIISR 自己的實(shí)現(xiàn)。
??微軟的 IISR 架構(gòu):
a)??首先需要使用 LoadIntChainHandler 去注冊這個 IISR 的處理判斷程序的
dll,在這個 dll中除了需要一個判斷處理程序(也就是通過
LoadIntChainHandler傳遞進(jìn)去的那個參數(shù)),還需要一個CreateInstance
的函數(shù),這個是必須的。在你不改動微軟內(nèi)核的情況下,名字也是固定的,詳
細(xì)地函數(shù)定義,可以參考 GIISR 的 CreateInstance。至于 IOControl,最
好也參考 GIISR 的定義一個,如果不需要去調(diào)用KernelLibIoControl 的話,
應(yīng)該是可以不實(shí)現(xiàn)的。
b)??在OEMInterruptHandler 中調(diào)用NKCallIntChain去遍歷鏈表,在調(diào)用處理
函數(shù)時將CreateInstance 的返回值作為參數(shù)傳遞進(jìn)去,如果處理函數(shù)在判斷
到不是自己觸發(fā)的中斷,應(yīng)該返回 SYSINTR_CHAIN,否則返回一個有意義
的 SYSINTR 值
c)??在需要注銷這個IISR 的時候,調(diào)用 FreeIntChainHandler,需要在dll中實(shí)
現(xiàn) DestroyInstance 這個函數(shù),被系統(tǒng)調(diào)用,這個是可選的。
GIISR 自己的實(shí)現(xiàn):
使用同一套代碼管理這些中斷處理程序,每個中斷處理程序在內(nèi)部的數(shù)組中占用
一項(xiàng),這些項(xiàng)目記錄著中斷處理程序激活使用的SYSINTR 以及判定其觸發(fā)中斷的標(biāo)
準(zhǔn)。這個標(biāo)準(zhǔn)就是讀取某個寄存器或地址來用掩碼來判斷。同時這個數(shù)組項(xiàng)目對應(yīng)的
結(jié)構(gòu)體數(shù)據(jù)就是通過KernelLibIoControl 傳遞進(jìn)去的。
??所以對于需要使用自己的特殊方式判斷中斷觸發(fā)的程序,可以使用自己的中斷判別
程序。
??我們可以為每一個中斷外設(shè)都實(shí)現(xiàn)自己的處理dll。在調(diào)用LoadIntChainHandler
時傳遞進(jìn)自定義的一個 dll 與處理函數(shù),CreateInstance 一定要實(shí)現(xiàn),不過返回值
可以忽略。
??在處理函數(shù)中,我們直接根據(jù)自己的外設(shè)來判斷中斷條件,然后返回相應(yīng)的
SYSINTR。
??其實(shí)使用 CreateInstance 的含義就是想把同一判斷類型的設(shè)備使用一套統(tǒng)一的
處理函數(shù)來判斷。CreateInstance 的返回值就是區(qū)別不同設(shè)備的這個 Index。
5 IISR資源的釋放
當(dāng)我們需要注銷這個 IISR 的時候,需要調(diào)用 FreeIntChainHandler,來將該中
斷服務(wù)從鏈表中刪除。
FreeIntChainHandler(hIsrHandler);
hIsrHandler 就是LoadIntChainHandler 的返回值。
??/*ARMCE 深入
FreeIntChainHandler 函數(shù)實(shí)現(xiàn)同樣在 loader.c 中的SC_FreeIntChainHandler。
所做的動作就是,如果有 DestroyInstance 函數(shù),則調(diào)用 DestroyInstance。然后
調(diào)用 UnhookIntChain。UnhookIntChain的函數(shù)實(shí)現(xiàn)在 kdriver.c 中,是一個標(biāo)準(zhǔn)
的單向鏈表刪除其中一個元素的操作,這里不再詳細(xì)解釋。
??*/
6 使用 IISR的注意事項(xiàng)
由于 IISR 是動態(tài)的被加載的,也就是說注冊的 dll 會被加載到內(nèi)核空間。所以不
能調(diào)用任何非內(nèi)核的函數(shù)。
并且,如果將 IISR的 dll放在 bib文件的MODULES section里面,需要設(shè)置”K”
屬性,如
giisr.dll? ?? ?? ?? ?? ?$(_FLATRELEASEDIR)\giisr.dll? ?? ? NK? ?SHK
如果放在 FILES section 里面,需要保證沒有 fixup類型的變量。
? ???
六??WinCE 中斷的延遲
??1 造成中斷延遲的原因
圖 1 WinCE 中斷體系結(jié)構(gòu)
重新看圖 1。這里畫出了從硬件中斷發(fā)生,到 IST 運(yùn)行的全部過程,中間就是
我們要研究的延時部分。
圖中的”IST 延遲”標(biāo)志,實(shí)際上就是我們所說的中斷延時。但是將 IST 延時細(xì)
分,可以分為如下幾部分延時:
a)??ISR 延時
這部分延時是指從硬件觸發(fā)中斷到進(jìn)入OEM的 ISR程序的時間,在 ARM 體系
下,就是進(jìn)入OEMInterruptHandler 的時間。
b)??ISR 執(zhí)行時間
在 OEMInterruptHandler 中判斷 IRQ,并且返回相應(yīng)的 SYSINTR 的時間,
也是OEM最主要把握的時間。
c)??從ISR 返回到 IST運(yùn)行的時間
主要就是系統(tǒng)根據(jù)ISR 返回的SYSINTR激活相應(yīng)的Event,系統(tǒng)調(diào)度運(yùn)行,
到 IST執(zhí)行的這段時間
2 如何降低中斷延時
上面的中斷延時原因中,a)是我們一般不去過問的部分,這段代碼是微軟實(shí)現(xiàn)
的,原則上是可以改的,但是收效不大,而且可能引入 bug,建議不要去動。c)是
我們可以部分控制的,關(guān)鍵就是IST的優(yōu)先級,其他部分我們也使用微軟默認(rèn)的實(shí)
現(xiàn)。
b) 這 一 部 分 就 是 我 們 著 重 需 要 管 理 的 了 , 因 為 這 一 塊 就 是
OEMInterruptHandler 函數(shù)的實(shí)現(xiàn),是 OEM 自己實(shí)現(xiàn)的代碼。也就是說,判斷
中斷源,返回SYSINTR的過程是我們唯一較可行的控制中斷延時的地方。
關(guān)于這部分的優(yōu)化,首先就是盡量用較簡單的邏輯判斷 IRQ,來返回相應(yīng)的
SYSINTR,而且 OEMInterruptHandler 會調(diào)用NKCallIntChain,所以 IISR 的
鏈表長度與 IISR處理函數(shù)的效率也是影響中斷延時的重要因素。
一般在判斷 IRQ 的過程中,我們會把最可能出現(xiàn)、最頻繁出現(xiàn)的IRQ 放在最前
面判斷,比如系統(tǒng)時鐘。這樣在剛進(jìn)入OEMInterruptHandler就可以判斷到IRQ,
并返回,節(jié)省了時間,提高了效率。同樣這種方法也適用于 IISR,將最可能出現(xiàn),
最頻繁出現(xiàn)的設(shè)備放在鏈表的前面,可以提高遍歷的效率。
七??總結(jié)
WinCE 下提供了較靈活的中斷處理方式,包括ISR,IST,IISR 三種主要方式。
對于 ARM 架構(gòu)下的系統(tǒng)開發(fā),我們常用的就是 IST 與 IISR 兩種方式。兩種方式
從本質(zhì)上都是通過返回正確的SYSINTR來激活相應(yīng)的 IST來服務(wù)中斷。不同點(diǎn)只是對
IRQ 判斷的方式,IST 是內(nèi)置的 OEM 寫死的判斷程序,而 IISR 是通過一個預(yù)留的接
口,來動態(tài)注冊判斷程序,從而給了系統(tǒng)一個注冊新中斷的機(jī)會。同時 IISR 可以實(shí)現(xiàn)
中斷共享,在一個 IRQ 上通過鏈表的方式不斷添加判斷程序,從而讓多個設(shè)備共享同
一個 IRQ,同時又可以有自己獨(dú)立的中斷判斷程序。
在最后,我們分析了中斷延時的一些因素,這里并沒有詳細(xì)的進(jìn)行分析,只是提出
了一些降低中斷延時的方法。
參考文獻(xiàn):
1.??Microsoft Windows CE .NET 中的中斷體系結(jié)構(gòu)? ? , Nat Frampton
2.??Platform Builder for Microsoft Windows CE 5.0 Help, Microsoft
3.??Microsoft Windows Embedded CE 5.0 source code, Microsoft
總結(jié)
以上是生活随笔為你收集整理的WinCE中断结构分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wince6.0 编译报错:error
- 下一篇: WINCE之“系统事件”——System