1. 介面
WindowsCe.net要求的觸控螢幕驅動DDI列表如下:
TouchPanelGetDeviceCaps
TouchPanelEnable
TouchPanelDisable
TouchPanelSetMode
TouchPanelReadCalibrationPoint
TouchPanelReadCalibrationAbort
TouchPanelSetCalibration
TouchPanelCalibrateAPoint
TouchPanelPowerHandler
如果在WCESHELLFE_MODULES_TRANSCRIBER或SHELLW_MODULES_CGRTOUCH模組參與編譯的時候還需要有如下的DDI擴充介面
TouchReset
TouchRegisterWindow
TouchUnregisterWindow
TouchSetValue
TouchGetValue
TouchCreateEvent
TouchGetFocusWnd
TouchGetLastTouchFocusWnd
TouchGetQueuePtr
對於標準的MDD-PDD分層模型,還定義了如下的DDSI介面,用於MDD與PDD之間的串連。
DdsiTouchPanelAttach
DdsiTouchPanelDetach
DdsiTouchPanelDisable
DdsiTouchPanelEnable
DdsiTouchPanelGetDeviceCaps
DdsiTouchPanelGetPoint
DdsiTouchPanelPowerHandler
2. 驅動構成
對於windowsCE.net的典型驅動而言,驅動link了tch_cal.lib和tchmdd.lib兩個靜態連結庫,這兩個靜態連結庫的原始碼分別在:
PUBLIC\COMMON\OAK\DRIVERS\TCH_CAL(tch_cal.lib) 該模組負責觸控螢幕的校準演算法。
有意思的是tchmdd.lib則比較特殊,在OAK的lib檔案夾中居然找不到這個檔案,而在專案檔的OAK目錄下該檔案確是存在的,也就是說該檔案在CESYSGEN的過程中產生。根據該連結庫提供的介面來看,可以猜測該lib應當由另外幾個零散的lib組成,這些連結庫的source檔案夾在OAK\COMMON\Drivers\Touch目錄下。後來在PUBLIC\COMMON\CESYSGEN下的makefile檔案內找到如下內容
tchmdd::
@set TARGETTYPE=LIBRARY
@set TARGETNAME=$@
@set RELEASETYPE=OAK
@set TARGETLIBS=
!IFDEF SYSGEN_TRANSCRIBER
@set SOURCELIBS=$(SG_INPUT_LIB)\tchmain.lib $(SG_INPUT_LIB)\tch_trns.lib
echo touch includes transcriber hooks
!ELSE
@set SOURCELIBS=$(SG_INPUT_LIB)\tchmain.lib $(SG_INPUT_LIB)\tchbasic.lib
echo touch is minimal
!ENDIF
確定了Tchmdd.lib的內容就是tchmain.lib+ tch_trns.lib或tchbasic.lib.因為tchmdd需要通過專門的Nmake指令完成組建的動作,所以在Touch驅動目錄下都會有一個bat檔案用於產生tchmdd.lib檔案用於後面的連結庫。
之所以需要將tchmdd.lib設定為這種方式產生,而不直接產生可以猜測為不同的編譯條件下DDI的介面不同(見第一部分),所以通過這裡來選擇不同的DDI介面實現。
這樣一來Touch驅動就分成兩種不同的類型了:
1. 不使用擴充DDI介面的Touch驅動
2. 在TRANSCRIBER編譯條件下產生的帶擴充的Touch驅動。
3. DDI函數概況和DDSI的調用關係:
在分層的軟體模型中需要明確的內容有下面幾點:
1. 層與層之間的調用介面和調用關係
2. 介面外的參數傳遞
3. 邏輯時序關係
4. 約束
為基本的驅動調用關聯式模式,Touch驅動由GWES載入,通過DDI調用驅動程式擷取裝置狀態,設定驅動功能等,而驅動本身直接獲得硬體資訊來確定當前Touch的狀態。驅動本身由兩個部分組成:MDD和PDD。其中MDD通常無需修改直接使用,該部分提供了面向GWES的DDI的介面,而MDD通過指定的DDSI函數介面調用PDD,這部分為需要由HIV或OEM廠商完成硬體相關的特定部分,也就是我們通常驅動需要實現的部分。PDD部分與MDD部分之間出了DDSI外還需要實現一些指定的變數定義或變數初始化動作,也就是說MDD與PDD之間並不一定是以嚴格的分層模型來實現,有時候也需要通過共用變數的方式來完成互動。
下面看看介面的情況。關於DDI和DDSI的介面情況第一部分已經做過介紹了,下面需要的是明確介面的觸發條件和DDI-DDSI的調用關係。由於DDI有兩種實現所以這部分需要分兩個不同的情況來看待,在這裡我僅僅對基本的Touch驅動的情況進行簡單的介紹。
與調用關係相關的還有調用時間邏輯,調用的時間邏輯上決定了公用資源初始化的次序,可用的資源的內容。因此,我們最後還需要以時間為主線。對驅動的調用情況作一個簡單的分析。
TouchPanelDllEntry
該函數不是DDI介面函數,但是為Driver DLL的入口函數.也就是在驅動中第一個被調用到的函數。
被調用到的DDSI函數有:
DdsiTouchPanelAttach
DdsiTouchPanelDetach
這兩個函數分別在DLL載入和卸載的最早和最晚的過程中被載入。
TouchPanelEnable
該函數在驅動的中起到初始化的作用。
調用的DDSI函數為
DdsiTouchPanelEnable
DdsiTouchPanelDisable
該函數的執行動作為:
1. 建立事件hTouchPanelEvent,hCalibrationSampleAvailable
2. 互拆量csMutex
3. 校準器初始化
4. 檢查並初始化所需的中斷gIntrTouch,和gIntrTouchChanged
5. 掛接事件回呼函數
6. 關聯中斷gIntrTouch,gIntrTouchChanged到事件hTouchPanelEvent
7. 建立ISR TouchPanelpISR,並設定ISR的優先順序
TouchPanelDisable
該函數與上面提到的TouchPanelEnable執行條件相反,提供停用裝置的能力
被調用到的DDSI函數有:
DdsiTouchPanelDisable
該函數的執行動作為:
1. 關閉ISR
2. 停止中斷
3. 登出事件及其他同步手段
TouchPanelPowerHandler
該函數在進入或退出PowerOff狀態時產生,由於該內容僅僅與PDD相關內容上僅僅是調用
被調用到的DDSI函數有:
DdsiTouchPanelPowerHandler
TouchPanelpISR
該函數在也不是DDI的介面。用於等待和處理觸控螢幕時間中斷,為整個驅動程式提供唯一的事件來源。
調用到的DDSI函數為
DdsiTouchPanelGetPoint
該函數實現的內容為:
1. 等待迴圈,用於接收Touch中斷訊息,並構成函數的主體
2. 通過DDSI擷取當前觸控螢幕位置和擷取資訊
3. 在擷取有效資料且在校準狀態下,收集/遞交按下的位置資訊
4. 在正常狀態下,校準資料(如果PDD已經校準,則無需此步),並檢查校準後資料的有效性
5. 最後調用由GWES傳入的回呼函數,提交位置資訊和狀態資訊
TouchPanelGetDeviceCaps
該函數為DDI介面函數。用於查詢觸控螢幕裝置支援的具體功能。
調用到的DDSI函數為
DdsiTouchPanelGetDeviceCaps
該函數動作為
1. 通過DDSI函數查詢相應的資訊
2. 當查詢螢幕座標資訊時儲存螢幕資訊,供後面程式映射螢幕座標
TouchPanelSetMode
該函數同樣為DDI介面函數,用於設定觸控螢幕的工作模式。
調用的PDD函數為DdsiTouchPanelSetMode
該函數的動作為
1.當設定IST優先順序時直接通過API完成
2.直接將其他設定轉入PDD完成
TouchPanelReadCalibrationPoint
該函數同樣為DDI介面函數,在效驗互動的過程中讀取相應的輸入值。直到ISR擷取相應的tip事件後,通過傳入的指標返回位置資訊。
TouchPanelReadCalibrationAbort
該函數在效驗取消的時候被調用。僅僅設定狀態位和事件後返回。
TouchPanelSetCalibration
該函數為DDI介面函數,為校準器的實現函數之一。該函數用於產生校準參數。在校準UI結束校準動作後將按鍵資料送至本函數,計算基準參數。
該函數沒有所需要的DDSI.
執行的動作並不為邏輯流控,而是一套數學演算法。具體內容為:
在觸控螢幕資料與其位置位移關係且顯示裝置像素與其位置位移關係同為線性關係假設情況下。觸控螢幕返回的位置資訊與像素位置資訊之間成2D座標變換關係.則對於觸控螢幕按下點TouchCoordinate(Tx,Ty)與其在顯示裝置位置關係上匹配的點ScreenCoordinate(Sx,Sy)之間的轉換關係,可以通過下述座標變換表示:
Sx = A1*Tx + B1*Ty + C1
Sy = A2*Tx + B2*Ty + C2
TouchPanelSetCalibration的具體工作就是通過校準的動作擷取的ScreenCoordinate和TouchCoordinate來確定A1,B1,C1,和A2, B2, C2.
對於已知Tx,Ty和Sx,Sy的情況下。把A B C視為未知數來進行求解。由於未知數為兩組三個,所以需要用兩組,三個方程的方程組來進行求解。
在這裡構造矩陣
|∑Tx*Tx ,∑Tx*Ty , ∑Tx| |A1(A2)| |∑Tx*Sx(Tx*Sy) |
|∑Tx*Ty ,∑Ty*Ty , ∑Ty| |B1(B2)| = |∑Ty*Sx(Ty*Sy) |
|∑Tx ,∑Ty , ∑1 | |C1(C2)| |∑Sx(Sy) |
然後通過克萊姆法則來求解上述方程組。最後獲得校準資料,也就是說校準參數就是線形變換的參數。校準資料中的Delta不具備邏輯意義,僅僅是在求解校準資料的時候未除的數字量而已。
在這裡比較疑惑的是,上面的行列式對應的方陣不滿秩(實際上三個方程的差別僅僅是同乘了一個變數係數而已,這樣也能解出A,B,C來的嗎?),演算法中構造的對稱陣事實上使得Deltra必定為0,為什麼還能得到非零的求解並得到校準參數。我本人數學功底很差,未能想明白其中的原因。
TouchPanelCalibrateAPoint
該函數為DDI介面函數,目的在於校準輸入的座標資訊。
根據TouchPanelSetCalibration內容可知,該函數通過公式
Sx = A1*Tx + B1*Ty + C1
Sy = A2*Tx + B2*Ty + C2
完成資料的校準,其中函數中用到的Deltra變數為求方程過程中的遺留因子,之所以保留到這裡再作除法運算目的在於減少資料的誤差。
到這裡已經基本可以看出驅動構成的輪廓,基本上分為幾個部分
1. 初始化和關閉流程,這部分內容全世界的軟體都有,作用我也不多說了
2. ISR實體,事實上在整個驅動運作的過程中只有一個部分是真正的系統事件源和資訊傳遞手段,該部分對應的PDD程式,用於擷取按鍵的狀態和具體的位置資訊,理想狀態下來說僅僅要這部分整個驅動就可以工作了。(事實上虛擬Touch驅動就可以這樣做)
3. 校準程式集,校準程式在整個Touch驅動中佔據很大的比例,使得整個驅動看起來似乎很複雜。由於該部分程式的驅動源為使用者對GWES的幹涉,該部分不納入驅動運行時許的過程。所以完全可以丟開這個部分也可以對整個觸控螢幕驅動看出個大概來。
4. 校準UI的實現與驅動的關係
事實上校準UI對於Touch驅動而言是完全隔離的,UI部分僅僅是實現了顯示。邏輯上不參與資料擷取的過程。
5. 遺補
在驅動中使用了Mutex作為同步化的手段,事實上使用Mutex是需要進行系統調用的,在這裡不使用CriticalSection而使用Mutex的原因我始終沒有想明白。如有哪位對此有理解請不吝指教。
在很多平台上Touch控制器與AC97控制器是在同一個CHIP上的,並公用一套寄存器介面。因為Touch驅動和Audio驅動分別在不同的進程空間內,不能使用CriticalSection,這種情況下務必要使用Mutex來保障寄存器操作的原子性。
GWES調用的驅動介面和運行方式與流式驅動有很大不同。除去顯示驅動外,這些驅動的PDD實現都很簡單,有時候看某些個別驅動程式內容很長,似乎很複雜。其實拋開系統硬體帶來的某些繁雜特徵,實現這些PDD部分還算是比較容易的