Windows Embedded CE 中斷結構分析

Windows Embedded CE  中斷結構分析
關鍵字:WinCE,中斷,體系,結構

摘要:本文主要以WinCE  .NET 5.0 爲操作系統平臺,ARM爲硬件平臺,分析了WinCE

中斷的結構與實現方式
作者:ETDrawer@ARMCE

前言 

  在嵌入式系統當中,對於中斷的處理是非常重要的一部分內容。許多外圍設備都需要通
過中斷來實現自己的功能或者與系統內核交互,系統時鐘本身也是由時鐘中斷產生的。所以
本文旨在分析WinCE下的中斷的結構,以及常用的幾種實現方式,來幫助讀者瞭解WinCE
中斷體系及實現自己的中斷處理結構。

 
下面的介紹如非特殊說明,均以 ARM 架構爲硬件基礎,操作系統代碼使用
Windows
embedded CE 5.0

WinCE中斷體系結構
 
先看圖 1

1 WinCE中斷體系結構

  這張是經典的說明中斷體系的圖,我們可以通過分析這張圖來了解WinCE的中斷體系。 

  從結構上看,WinCE中斷涉及4 層,即:硬件層、內核層、OAL層、IST處理層。了
解這 4層之間的交互傳遞將對我們瞭解WinCE中斷處理很有幫助。

1 硬件層:
 
硬件層就是實際觸發中斷的硬件,這裏主要有兩方面作用,一個是觸發中斷,第二個是

enable/disable
硬件中斷。

2 內核層:
 
這一層由內核來處理,包括中斷異常產生後跳轉到相應的ISR,以及根據SYSINTR

觸發相應的Event。關於SYSINTR IRQ 的概念後面會說明。

3 OAL
 
這一層主要就是我們需要實現的代碼了,來識別硬件IRQ,對應到SYSINTR

4 IST處理層
 
一般使用 IST 來做實際的中斷處理,這樣不會佔用很多的鎖定系統時間來處理中斷,

但是對中斷的實時性大打折扣

IRQISRIST SYSINTR

說到這裏先解釋下IRQISRIST SYSINTR 的概念、意義及相互關係。

IRQ
IRQ (Interrupt request)
,中斷請求。

這裏就是外設或其它請求服務的設備發出來的中斷。屬於硬件中斷,可能是一個電平觸發的

GPIO
中斷,也可能是內部DMA的一箇中斷。

ISR
ISR (Interrupt service routine), 
中斷處理程序。

WinCE
實際上使用 ISR來處理中斷,即默認的中斷入口函數,在 ARM體系中,系統默認的

ISR
就是 OEMInterruptHandler

IST
IST (Interrupt service thread), 
中斷服務線程。

ARM 的結構中,ISR 一般不會用來進行任何實際的操作,而只是返回一個 SYSINTR

實際的操作全部在IST中完成,IST一般是在Device Manager 的一個線程中運行的一段
高優先級的應用程序代碼,用來服務實際的中斷請求。

SYSINTR:
WinCE中,SYSINTR 就是 system interrupt,就是一個操作系統的邏輯中斷。

一般對於中斷的處理方式都是將一個IRQ映射爲一個或者多個(中斷共享)SYSINTR,而後,

在實際的ISR中根據IRQ返回一個對應的SYSINTR用來告訴操作系統需要服務的邏輯對
象。

使用邏輯中斷的好處當然就是可以實現虛擬的中斷(一個 SYSINTR 就被 OS 認爲是一個獨
立中斷)和中斷共享( IRQ對應多 SYSINTR)
邏輯中斷是WinCE需要處理的實際對象,至於這個對象是一個共享的IRQ,還是一個虛擬

的中斷,還是獨立的物理中斷,系統並不過問,從而隔離了硬件與邏輯,我們的 ISR 需要
做的也正是這種物理中斷到邏輯中斷的映射。

WinCE中斷處理原理 
 
下面基於 ARM 體系,來介紹 WinCE 中斷處理的流程與原理。
 
對於一個硬件IRQ中斷,系統內核在捕獲之後,會交給 OEMInterruptHandler 處理,

這個函數就是我們實現中斷處理的中心函數,首先我們從CPU 的寄存器裏獲得中斷的信息,
這些信息告訴我們到底是哪個 IRQ 源觸發了中斷。
一般實現中斷服務的方式有以下幾種:

1. 簡單中斷處理——ISR模型
 
最簡單的中斷處理辦法就是在ISR中直接處理,這裏就是指在
OEMInterruptHandler
中直接對中斷源進行判斷,然後調用服務程序。

  這種方式的優點和缺點一樣明顯。
 
優點:快速的響應了中斷處理,使系統的實時性能增加不少

 
缺點:由於進入OEMInterruptHandler的時候關閉了系統中斷(當然你可以在ISR

自己打開中斷,不過處理起來較麻煩),所以如果處理時間過長,其他中斷很可能被忽略,
造成中斷丟失。並且系統被鎖定,使得系統任務的響應變慢。

2. 中斷處理的一般流程——IST模型
 
前面看到了 ISR 模型的優缺點。作爲WinCE,主要定位還是民用的消費類電子產品,

所以,對於中斷響應的實時性不是特別高,所以系統的運行響應速度就顯得更加重要。而且
目前的嵌入式設備的處理速度越來越高,已經幾乎達到了當時奔 3 的水平。所以 ISR 的模
型並不適用於WinCE

如果把中斷服務程序當作一個系統進程或者線程來處理,這樣就不會造成系統被鎖定,
中斷被屏蔽等問題,使得中斷服務程序和其它進程、線程一樣被系統管理調度。於是就有了
IST
的概念

IST 模型的想法是,在 ISR 中判斷到具體的中斷源 IRQ,就返回一個標誌,用來標記
需要哪個程序來服務中斷,然後重新打開中斷,恢復系統調度,系統根據這個標誌來激活相
應的程序來服務中斷。

這個就是最常用的中斷處理模型。使得中斷被關閉,系統被鎖定的時間最短。
WinCE中,經常使用的就是建立中斷服務線程(IST),然後以IRQ 來申請一個系統

邏輯中斷號(SYSINTR),創建一個事件(Event),將 Event SYSINTR 綁定,隨後 IST
阻塞在等待Event上面。

ISR 中只給系統返回一個 SYSINTR,系統根據綁定關係激活相應的Event,使得隨後
IST得以運行。

這就是 IST的一般服務流程
IST
模型的缺點就是中斷服務的延遲較大,從 ISR 返回,到 IST開始運行,中間需要

花費一定的時間,相比 ISR 模型的中斷實時性較差,但是這種代價對於我們的應用是值得
的。

IST模型的實現 

  下面我們來看IST模型具體在我們的驅動中是如何實現的。
 
上面已經介紹了 IST 模型的一般服務流程,下面我們針對驅動程序實例,來分析具體

的實現步驟。
1
驅動程序中 IST的構建與中斷初始化

上面介紹的 IST流程中,很多步驟都是WinCE的內置支持,也就是說你只要調用相應

API就可以實現功能了,不需要自己編寫太多的代碼。只需要實現一些流程代碼。
首先是驅動程序端的中斷初始化。假設現在有一個驅動程序,需要服務中斷源,
IRQ
0x12

a)  IRQ 爲參數,申請 SYSINTR,方法爲調用
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &(dwIrq),  
                                 sizeof(UINT32), &dwSysIntr,  
                                 sizeof(UINT32), NULL)
其中 dwIrqIRQ號,即
0x12
dwSysIntr
爲系統中斷號,也就是調用返回的結果存放的位置

/* ARMCE深入
實際這個 IOControl調用的是函數 OALIoCtlHalRequestSysIntr,該函數的位置


WINCE500\PLATFORM\COMMON\SRC\COMMON\INTR\COMMON\Ioctl.c
最終調用 OALIntrRequestSysIntr,這個由 OEM 自己實現,一般就是維護了
2
張表,一張是從 SYSINTR IRQ 號碼的映射表,另一張是 IRQ SYSINTR

映射表,兩者是對應的關係。
這裏注意一點,一個IRQ 映射爲多個SYSINTR 是支持的,也就是共享中斷,但是

系統默認的實現並沒有支持一個 SYSINTR 映射爲多個 IRQ,你可以自己實現。
這兩張表對於後面在OEMInterruptHandler中由IRQ查找SYSINTR提供了方便

*/

b)  創建與 SYSINTR 綁定的Event
由於我們的IST是需要Event激活的,所以這裏申請一個 Event

申請 Event的步驟比較簡單和標準

hISTEvent = CreateEvent(0,FALSE,FALSE,NULL);

c)  SYSINTR Event綁定
調用 InterruptInitialize(dwSysIntr,hISTEvent,0,0) SYSINTR Event

定,用來在OEMInterruptHandler 中返回SYSINTR 時激活相對應的 Event

/* ARMCE深入
InterruptInitialize
SC_InterruptInitialize

E:\WINCE500\PRIVATE\WINCEOS\COREOS\NK\KERNEL\Intrapi.c

主要做兩件事情:

#1
Event Ptr 填入一個數組,這個數組是記錄每個 SYSINTR 對應激活的

Event

#2 調用OEMInterruptEnable,使能SYSINTR所對應的IRQ,並且將標誌 IRQ
被引用次數的變量加1(WinCE6 的代碼中未見這一變量
)
*/


d) 
創建一個 IST,並且等待hISTEvent
到了這一步,中斷關於系統方面的初始化基本結束,剩下的就是創建一個 IST,然

後等待 Event來運行中斷服務代碼,例如:
  
while(TRUE) {
WaitForSingleObject(hISTEvent,INFINITE) ==
WAIT_OBJECT_0)


}

這裏需要注意的是IST什麼時候創建都可以,但是在InterruptInitialize之前不要
運行 IST 以等待這個 Event,也就是說在 InterruptInitialize 之前不要使用這個
Event
,否則會導致InterruptInitialize失敗。
還有就是不要使用WaitForMultipleObjects來等待Event

在中斷處理完成之後需要調用 InterruptDone,參數爲該中斷的SYSINTR。來通

知系統,中斷處理完成,系統重新使能該中斷


   
到了這裏,驅動的中斷初始化工作就全部完成了。


2  OEM
層需要做的工作
OEM
IRQ enable  (BSPIntrEnableIrq)
disable
(BSPIntrDisableIrq),
當然要初始化 IRQ 的配置,使其在正確的觸發狀態,比如上升延

觸發

    至此一箇中斷處理的IST模型就實現了,系統在IRQ 觸發時調用映射函數,獲得相應
IRQ
SYSINTR,然後返回合法的SYSINTR給系統,系統查表激活相應的Event,對應
IST進行中斷服務,然後再次等待 Event

  3  中斷資源的釋放
   
當不需要當前中斷繼續服務的時候可以通過調用 KernelIoControl 來釋放申請到的

SYSINTR
,具體格式爲:
  KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR,  dwSysIntr,  sizeof(DWORD),
NULL, 0, NULL);

  其中 dwSysIntr 就是需要釋放的 SYSINTR 號碼。

/* ARMCE深入
 
這裏實際調用的是OALIntrReleaseSysIntr這個函數,由OEM自己實現。所做的動作也

很簡單,就是從SYSINTR IRQ IRQ SYSINTR 的那兩個映射關係數組中刪除映射
關係。
*/

可安裝的 ISR 
  1   
爲什麼要使用可安裝 ISR(以下簡稱 IISR)
   
需要 IISR 的目的有兩種:

I.
動態的安裝中斷程序

在系統運行的過程中註冊中斷,這種中斷一般是不可預知設備的中斷,常用在總線設備

中,比如PCI設備

II. 中斷共享
當多箇中斷源使用同一個中斷號(IRQ)時,就需要使用 IISR 來實現了

當然如果是需要動態安裝的共享中斷就最適合了。

因爲我們的 IST模型中,中斷服務程序就是驅動中的ISTIRQ IST是一對一的關
系。所以在需要動態添加一箇中斷服務程序的時候就沒有辦法處理了。
同樣由於 IRQ IST 的一一對應關係對於一個 IRQ 對應多個需要服務的 IST 就同樣

沒有辦法處理。
基於上面的情況纔會有IISR 的出現,IISR 從本質上是在ISR 中提供了一個接口,當

ISR
調用 NKCallIntChain時,以此IRQ爲參數,在鏈表中依次查找是哪一個服務程序來
服務這次 IRQ,然後返回相應的 SYSINTR,此後的動作與 IST 模型就基本一樣,通過
SYSINTR
來激活Event,從而啓動相應的 IST
 
所以 IISR 的實現就是動態的向某一個 IRQ服務程序鏈表添加結點的過程。

2 IISR的實現
下面我們來看看IISR 的具體實現步驟:

 
首先我們需要了解IISR 服務中斷的實現原理,如上面描述的,根據IRQ,來順序調用

鏈表中的中斷處理程序。所以我們可以有兩個選擇,一個就是類似 ISR 模型,直接在鏈表
中的中斷處理程序中判斷是不是自己的中斷,並且做處理。還有一種方式就是類似 IST
型,如果判斷是自己的中斷,則返回一個SYSINTR,以此SYSINTR 來激活IST
 
無論哪種方法,關於註冊中斷和查詢中斷的方式是一樣的,下面我們來看下如何將中斷

程序添加到鏈表,又如何在中斷來的時候去搜索鏈表。
  Microsoft
提供了一個通用的IISR的處理模型,叫做 GIISR,這是一個以 IST模型處

IISR 的模塊,源程序可以在WINCE500\PUBLIC\COMMON\OAK\DRIVERS\GIISR
找到。熟悉了 GIISR,想實現自己的 IISR 處理程序或者基於 ISR 模型的處理,都比較簡

單了。
 
下面我們就分析這種比較通用的處理 IISR 的模型。

a) 
首先我們需要以 IRQ 來申請 SYSINTR,並且將 SYSINTR Event 綁定,這些

步驟與IST模型中介紹的一樣,這裏就不重複敘述了,IISR 在這裏與 IST模型並
沒有任何的不同。其與 IST 模型的唯一不同點就是如何根據 IRQ 來判斷相應的

SYSINTR
IST 模型中是 OEM寫死的一個判斷程序,而 IISR 可以動態來註冊一個判斷程

序給系統調用,這是唯一的實現區別。
b) 
下面我們需要註冊可安裝中斷程序的 dll,和 dll中的中斷處理函數,並且將他們與

某一個特定的IRQ相關聯
這個過程是通過調用LoadIntChainHandler函數來實現的。

這裏我們的中斷服務dll叫做”giisr.dll”,處理函數名叫做”ISRHandler”,對應
IRQ
0x20,則函數調用形式如下:

HANDLE  hIsrHandler  =  LoadIntChainHandler(TEXT(“giisr.dll”),
TEXT(“ISRHandler”), 0x20);

/* ARMCE深入
LoadIntChainHandler
的源代碼可以在
WINCE500\PRIVATE\WINCEOS\
COREOS\NK\KERNEL\Loader.c
中找到,就是函數
SC_LoadIntChainHandler
WinCE6
KDriver.c 中的 NKLoadIntChainHandler,兩者功能大致相同,只

是在一些結構體定義上略有不同
其主要功能就是加載 dll NK,並且獲得三個函數的指針,一個是

CreateInstance
,一個是你剛纔傳進來的處理函數,這裏就是 ISRHandler,還
一個就是 IOControl,後面會用到。
首先調用CreateInstance 來獲得一個實例的數據,這個數據就是一個 index,用

來標示其中的一箇中斷處理程序的索引。
這裏可能需要解釋下 GIISR 的處理原理。我們所有的可安裝中斷都通過
giisr.dll
裏面的 ISRHandler來處理,在NKCallIntChain被調用的時候,會遍歷所有註冊

到這個 IRQ 的中斷處理函數,這裏全部都是同一個函數 ISRHandler。那麼就需
要可以區分每一次調用,所以就在 giisr 模塊裏面維護一個數組,每一箇中斷服務
程序佔用一個數組成員,這些數組成員的Index就是他們在giisr裏面的唯一標識。
所以 CreateInstance 的任務就是查找數組,找到第一個空閒位置,將 Index
回。在ISRHandler 被調用到的時候,會將這個Index傳遞進去,根據這個 Index
ISRHandler
就能夠知道是數組中哪個成員正在被查詢,進而確定是不是這個成員
需要處理中斷請求,進而確定該返回的 SYSINTR。詳細的步驟下面會一一說明,
大家先有一個概念

在從 CreateInstance 返回了可用的數組 Index 之後,調用 HookIntChain,此
函數在Kdriver.c中。這個函數的功能比較簡單,我們先了解下共享中斷在系統中
的處理。
前面有所提到,在調用 NKCallIntChain 時會遍歷一個鏈表,每個鏈表頭就是系統

維護的一個數組中的一個成員,每一個 IRQ 號碼都對應數組中的一個成員,這個
數組就是 pIntChainTableIRQ 按照其號碼在數組中對應相應的數組成員,注意
pIntChainTable
是一個 256 個成員的數組,這也就意味着你的 IRQ 號碼數字不
能超過 255,當然這是指你傳遞進來的 IRQ 號碼,如果你的 IRQ 號碼都是大於
255
的,可以對實際 IRQ 號碼做處理,保證其數字值在 0-255,比如對 256 取模,
這樣當然就可以傳遞進來了。
pIntChainTable
中的每個元素都是一個鏈表的頭,當你向一個IRQ 添加中斷處理

的時候,實際是建立了一個新的PINTCHAIN元素,然後向pIntChainTable中的
IRQ
索引位置去添加,如果該位置不爲空,則查找這個元素指向的下一個元素,在
這個單向鏈表的操作下,將新的中斷處理程序加入。
這個加入工作就是HookIntChain做的

*/

c)  上一步在GIISR中通過CreateInstance把這個新的中斷處理程序加入GIISR
己的管理。GIISR 的主要作用就是判斷當中斷來的時候,是不是其內部數組中的某
個成員需要服務中斷。所以需要更多的信息用來判斷中斷是否匹配當前的中斷服務
程序,所以我們需要把信息傳遞進去,這裏就是調用KernelLibIoControl
具體的方法爲:

KernelLibIoControl(hIsrHandler,IOCTL_GIISR_INFO,&giisr_info,
sizeof(GIISR_INFO), NULL, 0, NULL);
這裏就是把 giisr_info 的內容傳遞給剛纔註冊的中斷,giisr_info 是一個

GIISR_INFO
的結構體,其內容如下:
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;

這些成員都是需要設置的,具體含義如下
SysIntr
:這個中斷所對應的系統中斷號,即第一步申請到的 SYSINTR,系統在

確定是當前的設備出發的 IRQ 之後會返回這個 SysIntr
CheckPort
:一般爲 TRUE,如果爲 FALSE 則直接返回 SysIntr,而不是判斷是

不是當前設備觸發的中斷
PortIsIO
:是不是IO端口,這個可能只是在 x86 下使用,我們置爲
FALSE
UseMaskReg
:是否使用地址來獲得 Mask,如果爲TRUE,則Mask 字段無意義

PortAddr
:實際上是可以判斷中斷是哪個設備出發的那個寄存器的地址

PortSize
PortAddr的位寬,標誌PortAddr1字節(BYTE),2 字節(WORD)

還是 4字節(DWORD)的寄存器,其他不支持
Mask
:一個掩碼位,在UseMaskRegFALSE 的情況下與PortAddr的值進行

位或運算,如果不爲0,則確定爲當前設備觸發的中斷
MaskAddr
:當 UseMaskReg TRUE 的時候,使用這個地址來獲得掩碼的值,

給動態的判斷中斷提供了接口
仔細看了上面各個成員的介紹,大家就應該可以瞭解 GIISR 是如何判斷中斷是不

是當前設備產生的。
所有的判斷依據就是這個結構體。一般我們會將 CheckPort 置爲 TRUE,然後讓

系統去讀取PortAddr地址處的值,這個值可以標誌是否爲當前設備觸發的中斷。
獲得這個值以後,與一個 mask 值進行或運算(&),如果值不爲 0,則認爲是當前
設備觸發的中斷。這個 mask 值在 UseMaskReg FALSE 時是成員 Mask,反
之是從 MaskAddr 地址處獲得。

/* ARMCE深入
KernelLibIoControl
Kdriver.c

SC_KernelLibIoControl
SC_KernelLibIoControl 調
NKKernelLibIoControl
,這個函數同樣在 kdriver.c 中。分析源碼可知,就是調
用了 giisr.dll 中的 IOControl,參數爲 IOCTL_GIISR_INFO,就是傳遞進去了
一個 GIISR_INFO 結構體,用來給 Index 標示的數組元素賦值,這個 Index
是前面CreateInstance返回的那個數值,通過 hIsrHandler傳遞進去。
*/

d)  下面就是啓動IST,等待Event,這裏和IST模型沒有任何區別。

到這裏全部的初始化就完成了,可以看出,和IST模型相比就是多了兩步 b) c)
這兩步決定了中斷判斷的方式,這也是 IISR的根本所在。

  
中斷的判斷

下面詳細介紹下可安裝中斷在 ISR 中被判斷的過程。

OEMInterruptHandler

OEMInterruptHandler
中會調用NKCallIntChain來遍歷該IRQ對應註冊的IISR
這是一個鏈表結構,所以對中斷判斷程序是一個順序調用的過程,即先註冊的設備先
判斷,如果判斷到正確的結果,則返回合法的 SYSINTROEMInterruptHandler
也同樣返回這個值。所以即使後面的設備也符合條件,也不會被執行。如果整個鏈表

中都沒找到正確的設備,則返回 SYSINTR_CHAINOEMInterruptHandler 在判
斷到返回結果爲SYSINTR_CHAIN 時,即表示請求中斷的設備不在鏈表中。

/* ARMCE深入
NKCallIntChain
的代碼在 KDriver.c 中,是個非常簡單的函數,以傳遞進來的

IRQ
Index 找到鏈表的頭,然後去調用各鏈表節點的 hIsrHandler,並且以
CreateInstance
的返回值爲參數。當有返回的值不是 SYSINTR_CHAIN 時,就返
這個值,反之繼續查找,直到鏈表尾部,如果還沒有找到,就返
SYSINTR_CHAIN

*/

自定義的 IISR
 
我們可以不使用GIISR,而自己實現IISR功能,只要知道了IISR 的原理。

當以某一個寄存器或者地址的值,不足以判斷到底是系統中哪個設備觸發的中斷
的時候,GIISR 就不是這麼好用了。比如多個不同的外設,使用同一個GPIO 來觸發
中斷。外設需要讀取多個寄存器或者需要一個複雜些的計算(不只是簡單的一個&操作)
才能判定中斷是否是其產生的。這時候我們需要使用自己的一套 IISR的處理方式。

當然我們不希望去改動整個微軟對於 IISR 的處理結構,所以我們就要區分開來上

面介紹的 GIISR 的模型裏,哪些是微軟的架構,哪些是GIISR 自己的實現。

  微軟的 IISR 架構:
a) 
首先需要使用 LoadIntChainHandler 去註冊這個 IISR 的處理判斷程序的

dll
,在這個 dll中除了需要一個判斷處理程序(也就是通過
LoadIntChainHandler
傳遞進去的那個參數),還需要一個
CreateInstance
的函數,這個是必須的。在你不改動微軟內核的情況下,名字也是固定的,詳

細地函數定義,可以參考 GIISR CreateInstance。至於 IOControl,最
好也參考 GIISR 的定義一個,如果不需要去調用KernelLibIoControl 的話,
應該是可以不實現的。

b)  OEMInterruptHandler 中調用NKCallIntChain去遍歷鏈表,在調用處理
函數時將CreateInstance 的返回值作爲參數傳遞進去,如果處理函數在判斷
到不是自己觸發的中斷,應該返回 SYSINTR_CHAIN,否則返回一個有意義
SYSINTR

c)  在需要註銷這個IISR 的時候,調用 FreeIntChainHandler,需要在dll中實
DestroyInstance 這個函數,被系統調用,這個是可選的。

GIISR 自己的實現:
使用同一套代碼管理這些中斷處理程序,每個中斷處理程序在內部的數組中佔用

一項,這些項目記錄着中斷處理程序激活使用的SYSINTR 以及判定其觸發中斷的標
準。這個標準就是讀取某個寄存器或地址來用掩碼來判斷。同時這個數組項目對應的
結構體數據就是通過KernelLibIoControl 傳遞進去的。

  所以對於需要使用自己的特殊方式判斷中斷觸發的程序,可以使用自己的中斷判別
程序。
 
我們可以爲每一箇中斷外設都實現自己的處理dll。在調用
LoadIntChainHandler
時傳遞進自定義的一個 dll 與處理函數,CreateInstance 一定要實現,不過返回值

可以忽略。
 
在處理函數中,我們直接根據自己的外設來判斷中斷條件,然後返回相應的

SYSINTR

  其實使用 CreateInstance 的含義就是想把同一判斷類型的設備使用一套統一的
處理函數來判斷。CreateInstance 的返回值就是區別不同設備的這個 Index

5 IISR資源的釋放
當我們需要註銷這個 IISR 的時候,需要調用 FreeIntChainHandler,來將該中

斷服務從鏈表中刪除。

FreeIntChainHandler(hIsrHandler);
hIsrHandler
就是LoadIntChainHandler 的返回值。

  /*ARMCE
深入

FreeIntChainHandler
函數實現同樣在 loader.c 中的SC_FreeIntChainHandler

所做的動作就是,如果有 DestroyInstance 函數,則調用 DestroyInstance。然後
調用 UnhookIntChainUnhookIntChain的函數實現在 kdriver.c 中,是一個標準
的單向鏈表刪除其中一個元素的操作,這裏不再詳細解釋。
  */

6 使用 IISR的注意事項
由於 IISR 是動態的被加載的,也就是說註冊的 dll 會被加載到內核空間。所以不

能調用任何非內核的函數。
並且,如果將 IISR dll放在 bib文件的MODULES section裏面,需要設置
”K”
屬性,如

giisr.dll               $(_FLATRELEASEDIR)\giisr.dll       NK   SHK

如果放在 FILES section 裏面,需要保證沒有 fixup類型的變量。 
    
WinCE 中斷的延遲
 
  1
造成中斷延遲的原因

1 WinCE 中斷體系結構

重新看圖 1。這裏畫出了從硬件中斷髮生,到 IST 運行的全部過程,中間就是
我們要研究的延時部分。
圖中的”IST 延遲標誌,實際上就是我們所說的中斷延時。但是將 IST 延時細

分,可以分爲如下幾部分延時:
a)  ISR
延時

這部分延時是指從硬件觸發中斷到進入OEM ISR程序的時間,在 ARM 體系

下,就是進入OEMInterruptHandler 的時間。

b)  ISR 執行時間
OEMInterruptHandler 中判斷 IRQ,並且返回相應的 SYSINTR 的時間,

也是OEM最主要把握的時間。
c) 
ISR 返回到 IST運行的時間

主要就是系統根據ISR 返回的SYSINTR激活相應的Event,系統調度運行,

IST執行的這段時間

2 如何降低中斷延時
上面的中斷延時原因中,a)是我們一般不去過問的部分,這段代碼是微軟實現

的,原則上是可以改的,但是收效不大,而且可能引入 bug,建議不要去動。c)
我們可以部分控制的,關鍵就是IST的優先級,其他部分我們也使用微軟默認的實
現。
b)

OEMInterruptHandler
函數的實現,是 OEM 自己實現的代碼。也就是說,判斷
中斷源,返回SYSINTR的過程是我們唯一較可行的控制中斷延時的地方。
關於這部分的優化,首先就是儘量用較簡單的邏輯判斷 IRQ,來返回相應的

SYSINTR
,而且 OEMInterruptHandler 會調用NKCallIntChain,所以 IISR
鏈表長度與 IISR處理函數的效率也是影響中斷延時的重要因素。
一般在判斷 IRQ 的過程中,我們會把最可能出現、最頻繁出現的IRQ 放在最前

面判斷,比如系統時鐘。這樣在剛進入OEMInterruptHandler就可以判斷到IRQ
並返回,節省了時間,提高了效率。同樣這種方法也適用於 IISR,將最可能出現,
最頻繁出現的設備放在鏈表的前面,可以提高遍歷的效率。


總結

WinCE 下提供了較靈活的中斷處理方式,包括ISRISTIISR 三種主要方式。
對於 ARM 架構下的系統開發,我們常用的就是 IST IISR 兩種方式。兩種方式

從本質上都是通過返回正確的SYSINTR來激活相應的 IST來服務中斷。不同點只是對
IRQ
判斷的方式,IST 是內置的 OEM 寫死的判斷程序,而 IISR 是通過一個預留的接
口,來動態註冊判斷程序,從而給了系統一個註冊新中斷的機會。同時 IISR 可以實現
中斷共享,在一個 IRQ 上通過鏈表的方式不斷添加判斷程序,從而讓多個設備共享同
一個 IRQ,同時又可以有自己獨立的中斷判斷程序。
在最後,我們分析了中斷延時的一些因素,這裏並沒有詳細的進行分析,只是提出

了一些降低中斷延時的方法。

參考文獻:
1.  Microsoft Windows CE .NET
中的中斷體系結構
    , Nat Frampton
2.  Platform Builder for Microsoft Windows CE 5.0 Help, Microsoft
3.  Microsoft Windows Embedded CE 5.0 source code, Microsoft

 

發佈了7 篇原創文章 · 獲贊 8 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章