劉麗霞,劉志鵬,張 力
(1.中國航天科工集團第二研究院 七〇六所,北京 100854;2.32379部隊第一科室,北京 100072)
VxWorks是Wind River System公司推出的一種嵌入式實時操作系統,以其良好的可靠性和實時性被廣泛應用在通信、軍事、航空、航天等實時性要求較高的領域。Wind River Workbench 3.2是基于VxWorks6.8操作系統的軟件開發環境,它提供了豐富的調試、仿真環境和工具。WindML是VxWorks操作系統下的多媒體庫(wind media library)。它支持多媒體程序運行在嵌入式系統中,提供基本圖形、視頻和音頻技術,以及提供一個通用設備驅動程序架構。
WindML包含兩個組件,軟件開發包(SDK)和驅動程序開發包(DDK)。
SDK組件用來開發應用程序,它提供了一個全面的API接口,包括圖形、輸入處理、多媒體、字體和內存管理。
DDK組件用來實現驅動程序,它提供完整的驅動程序參考集,以使開發者能迅速實現驅動開發。
本文接下來分析了VxWorks6.8操作系統下使用WindML驅動程序開發包(DDK)開發輸入設備驅動程序原理。設計并開發了觸摸屏驅動軟件以及觸摸屏校準算法。
本文實現的驅動和觸摸屏校準算法豐富了WindML庫輸入設備驅動,也填補了WindML庫中觸摸屏校準算法的空白。通過設計應用程序測試驗證驅動的正確性和有效性,也說明了該算法可以進行推廣和應用。
鼠標(PS2鼠標、USB鼠標)、鍵盤、觸摸屏均屬于輸入設備,其中PS2鼠標、USB鼠標、觸摸屏設備等都是指針設備。
本文選用的觸摸屏控制器型號是PenMount PM6500,它通過標準RS232串口接入到主控設備。觸摸屏控制器將觸摸點坐標信息通過RS232串口傳遞給主控設備,主控設備經觸摸屏驅動解析、處理后實現觸摸點顯示可控等功能。
觸摸屏控制器按表1所示數據包格式進行通信:數據包大小是6個字節,其中前5個字節是數據,最后一個字節是校驗和。Byte 0是觸摸狀態,0×70表示按下狀態,0×30表示抬起狀態。Byte 1、Byte 2組合成X坐標位置,Byte 3、Byte 4組合成Y坐標位置,Byte 5為校驗和。

表1 串口通信協議
基于WindML的輸入設備驅動由兩層驅動構成,一層是底層驅動(low-level),用于處理硬件設備控制器,一層是頂層驅動(high-level),用于解析來自硬件設備的原始數據。
WindML底層輸入驅動(low-level)是一種I/O驅動,常常配置到系統內核。初始化時,該驅動掛接到操作系統的IO系統上,它的功能如下:
(1)初始化設備控制器和輸入設備;
(2)處理由設備控制器產生的中斷;
(3)接收來自輸入設備的原始數據,通過IO系統傳遞給high-level輸入驅動;
(4)接收來自high-level輸入驅動的命令并進行響應。
本文使用的觸摸屏通過串口接入IO系統,RS232串口為標準串口,VxWorks6.8自帶該設備驅動,對應的設備名稱為/tyCo/1。
IO系統驅動標準函數有:open、close、read、write、ioctl、select。這些函數被high-level輸入驅動調用,實現輸入設備的原始數據由low-level驅動向high-level輸入驅動傳遞。
high-level輸入驅動主要功能是從low-level輸入驅動中接收原始數據,處理數據,再將其打包成輸入事件消息,最后將消息發送到輸入消息隊列中。
基于WindML觸摸屏驅動,涉及的low-level輸入驅動使用VxWorks6.8系統自帶串口驅動即可,因此本文重點是實現觸摸屏high-level輸入驅動設計開發。
WindML提供了與應用層傳遞信息的可擴展機制。輸入設備(如觸摸屏)通過輸入服務(input service)將輸入信息(坐標)傳遞給應用層。輸入設備、應用層以及窗口均通過消息方式實現信息交互。
在應用層中,每一個顯示端均有輸入服務對應。所有的輸入服務通過輸入任務控制high-level輸入驅動。該任務通過select()函數被掛起,用來等待來自low-level輸入驅動的數據,當輸入設備產生數據后,該任務被底層驅動(low-level)喚醒后,輸入服務(input service)借助high-level驅動處理來自輸入設備的原始數據。high-level輸入驅動從low-level輸入驅動上獲取觸摸屏原始數據,將原始數據封裝成WindML指針類數據(UGL_POINTER_DATA)后,再將指針類數據打包成WindML消息(UGL_MSG_DATA),最后通過WindML標準接口uglInputMsgPost()函數將其放入消息隊列中。應用層通過uglInputMsgGet()函數從缺省的輸入隊列中獲取輸入消息。獲取消息后,通過uglInputCbAdd()函數注冊輸入回調函數,實現消息響應,如鼠標指針移動、鼠標左擊。
WindML輸入消息定義如下:
typedef union ugl_msg_data
{
/* The data for MSG_KEYBOARD */
UGL_KEYBOARD_DATA keyboard;
/* The data for MSG_POINTER */
UGL_POINTER_DATA pointer;
/* The data for MSG_RAW_KBD */
UGL_RAW_KBD_DATA rawKbd;
/* The data for MSG_RAW_PTR */
UGL_RAW_PTR_DATA rawPtr;
/* for compatability with ver. 2.X */
UGL_EVENT event;
char reserved [UGL_MAX_MSG_SIZE];
} UGL_MSG_DATA;
設計觸摸屏驅動源文件uglPM6500ts.c。
將其放置在“installDir/components/windml-5.3/src/ugl/driver/pointer/uglPM6500ts”路徑下。其中“installDir”指WindRiver安裝路徑。在該源文件中定義觸摸屏驅動以及相應函數實現。uglPM6500TsPtrDriver是high-level輸入驅動入口函數,驅動函數包含設備打開、關閉、控制(讀取及處理觸摸點數據、獲取指針設備類型、校正觸摸點等功能)。
UGL_INPUT_DRV uglPM6500TsPtrDriver=
{
(UGL_INPUT_DEV_OPEN)uglPM6500TsPtrOpen,
(UGL_INPUT_DEV_CLOSE)uglPM6500TsPtr-Close,
(UGL_INPUT_DEV_CONTROL)uglPM6500Ts-PtrControl
};
源程序對應的頭文件uglPM6500ts.h放置在“installDir/components/windml-5.3 /h/ugl/driver/pointer”路徑下。該文件定義了PM6500觸摸屏指針設備數據結構UGL_PM6500_TS_PTR_DEVICE以及函數實現聲明。
typedef struct ugl_PM6500_ts_ptr_device
{
/* inherits input device structure輸入設備描述符*/
UGL_INPUT_DEV inputDevice;
/* display rectangle size 觸摸屏分辨率大小*/
UGL_RECT dispRect;
/*與校準點對應的觸摸屏坐標,PM6500_TS_NUM_CALIB_POINTS 設置校準點個數*/
UGL_POINT tcPoint[PM6500_TS_NUM_CALIB_POINTS];
/*標準校準點坐標*/
UGL_POINT calibPoint[PM6500_TS_NUM_CALIB_POINTS];
/*校準系數:依次為A/B/C/H/E/F*/
float calibCoef[6];
} UGL_PM6500_TS_PTR_DEVICE;
下面對uglPM6500ts.c中實現的驅動函數進行詳細闡述。
實現原理是:通過low-level輸入驅動接口open()函數打開觸摸屏控制器RS232串口;設置串口波特率;通過串口從觸摸屏控制器上讀取觸摸校準參數、初始化其它參數等。
UGL_LOCAL UGL_INPUT_DEV_ID uglPM6500Ts-PtrOpen(char* pDevName,UGL_INPUT_DRV * pDriver);
{
…
/* pDevName 是串口名稱*/
pDevice->inputDevice.fd = open (pDevName, O_RDWR, 0); //設置串口參數, 包括波特率
//從控制器的NVRAM中讀取觸摸校準參數A/B/C/H/E/F
//獲取顯示屏中用于校準觸摸點(XTi,YTi)對應的顯示點坐標(XLi,YLi)。
…
}
在應用層,通過調用WindML標準函數UGL_INPUT_DEV_ID uglInputDevOpen(UGL_CHAR *pName,/* device name */UGL_INPUT_DRV * pDriver /* input driver */),實現對uglPM6500TsPtrOpen驅動函數調用。uglInputDevOpen函數返回輸入設備ID,應用層通過將輸入設備ID作為參數,通過調用UGL_STATUS uglInputDevAdd(UGL_INPUT_SERVICE_ID inputServiceId,UGL_INPUT_DEV_ID inputDeviceId)函數實現了輸入設備與輸入服務綁定。
實現原理:根據上層用戶發送的請求類型分別執行控制功能,主要包括讀取及處理原始數據、校準觸摸屏、獲取指針設備類型等。
UGL_LOCAL UGL_STATUS uglPM6500TsPtrControl (UGL_PM6500_TS_PTR_DEVICE *pDevice, UGL_DEVICE_REQ request,void * pArg)
{
/*判斷用戶控制請求類型*/
switch (request)
{
//讀取串口原始數據
case ICR_READ: /* read PTR data */
{
return (uglPM6500TsPtrReadMessages (pDevice));
}
//獲取指針設備類型,有鼠標類型、觸摸屏類型等。UGL_PTR_TYPE_TOUCH_SCREEN是WindML中對觸摸屏設備的宏定義
case ICR_GET_PTR_TYPE:
{
if(pArg != UGL_NULL)
{
*(int *)pArg = UGL_PTR_TYPE_TOUCH_SCREEN;
return(UGL_STATUS_OK);
}else
{
return(UGL_STATUS_ERROR);
}
}
//開始獲取觸摸校準點
case ICR_CALIBRATION_START:
{
//pArg為校正點順序, 對應標準點順序。 獲取觸摸點初始數據, 并保存。
return(GetTcData(pDevice, pArg));
}
//結束獲取觸摸校正點
case ICR_CALIBRATION_STOP:
{
//函數CalGetCoef利用標準校準點和獲取的觸摸點計算校正參數。
return(CalGetCoef(pDevice));
}
…
}
在應用層,使用顯示端對應的輸入服務(input server)找到關聯的輸入設備ID,然后將輸入設備ID作為參數,通過調用uglInputDevControl(inputDevId,requestType,argument)函數,實現對uglPM6500TsPtrControl驅動函數調用。
3.3.1 獲取及解析原始數據
用戶請求類型為ICR_READ:
實現原理:通過low-level輸入驅動接口read()函數讀取串口原始數據,將其解析成UGL_POINTER_DATA數據,然后封裝該數據為輸入消息,再由uglInputMsgPost將消息發送到輸入消息隊列中。應用層用戶通過uglInputMsgGet捕獲該消息后,再通過調用該消息相關的回調函數,實現觸摸點移動或按鍵功能。
3.3.2 實現觸摸屏校準
用戶請求類型為:
ICR_CALIBRATION_START:開始獲取校準觸摸點。
ICR_CALIBRATION_STOP:停止獲取校準觸摸點。
鼠標指示的是相對位置,而觸摸屏指示的是絕對位置。觸摸點數據通過校準參數轉換為屏幕上的坐標,這樣就要求不管在什么情況下,同一點的輸出數據是穩定的,如果不穩定,那么觸摸屏就不能保證絕對坐標定位,因此在定位不準時需要校準觸摸屏幕。校準的核心內容就是獲取觸摸點向屏幕顯示點轉換的校準參數。
如果PT(x,y) 表示觸摸屏上的一個點,PL(x,y) 表示顯示屏LCD上的一個點,校正的結果就是得到一個轉換矩陣M,通過式(1)實現觸摸屏點坐標與顯示屏點坐標的變換。M屬于二維幾何變換包含平移、旋轉和縮放3種變換
PL(x,y)=M·PT(x,y)
(1)
若PL(x,y) 與PT(x,y) 是平移關系,轉換關系通過式(2)表示。MT為如式(3)中描述的矩陣
PL(x,y)=MT·PT(x,y)
(2)
(3)
若PL(x,y) 與PT(x,y) 是縮放關系,轉換關系通過式(4)表示。MS為如式(5)中描述的矩陣
PL(x,y)=MS·PT(x,y)
(4)
(5)
若PL(x,y) 與PT(x,y) 是旋轉關系,轉換關系通過式(6)表示。MR為如式(7)中描述的矩陣
PL(x,y)=MR·PT(x,y)
(6)
(7)
實際情況中,觸摸屏到顯示屏坐標變換涉及到3種變換,如式(8)所示。展開此公式,其結果如式(9)、式(10)所示
PL(x,y)=MT·MS·MR·PT(x,y)
(8)
XL=XT(Sx·cosθ)+YT(-Sy·sinθ)+
(Tx·cosθ-Ty·sinθ)
(9)
YL=XT(Sx·sinθ)+YT(Sy·cosθ)+
(Tx·sinθ+Ty·cosθ)
(10)
在式(9)、式(10)中,顯示屏上的坐標(XL,YL)和觸摸屏上的坐標(XT,YT)是已知的,而其它參數則是需要求解的:θ,Sx,Sy,Tx,Ty,共有5個變量。為了簡化運算,將式(9)、式(10)變為式(11)、式(12)。其中A、B、C、H、E、F為待求解的校準參數,在式(11)、式(12)中雖然增加了未知變量,但是避免了三角函數運算,計算更簡便
XL=A·XT+B·YT+C
(11)
YL=H·XT+E·YT+F
(12)
其中,(XL,YL)是屏幕顯示點坐標,(XT,YT)是觸摸點坐標。至此,觸摸屏校準過程就是求解A、B、C、H、E、F參數的過程。
將式(11)乘以常量XTi, 或YTi后,再執行求和運算等價變換為式(13)~式(15)這3個表達式。n表示觸摸點數量。 (XTi,YTi) 表示第i個觸摸點的絕對坐標,(XLi,YLi) 表示第i個觸摸點對應的屏幕顯示坐標
(13)
(14)

(15)
上述3個方程式中,A、B、C可以作為待求解的未知數,獲取到的每個觸摸點 (XTi,YTi) 與顯示標準點 (XLi,YLi) 的坐標為已知數,n表示坐標點的數量,它可以是任意值,但是采集點過少會影響計算參數精度,過多又會冗余,且增加了計算時間。根據實驗室驗證值,得出n=5,具有較好的表現。
依據克萊姆法則,通過計算上述3個方程式對應的行列式,來求解A、B、C。其中
A=D0/D
(16)
B=D1/D
(17)
C=D2/D
(18)
上述式(16)~式(18)中,D為式(13)~式(15)方程式等號右側已知數系數構成的行列式;Di是把D中第i列元素對應地換成式(13)~式(15)方程式等號左側常數項而其余列保持不變所得到的行列式
(19)
(20)
(21)
(22)
同理可以計算得到H、E、F參數。這樣根據式(11)、式(12),就可以將觸摸點坐標 (XT,YT) 通過A、B、C、H、E、F參數運算后,轉換成屏幕上的顯示坐標 (XL,YL)。
程序初始化時,用戶自定義了n(n=5)個用于校準的顯示點坐標 (XLi,YLi), 在本文3.3節中,使用GetTcData函數用來采集顯示點坐標 (XLi,YLi) 對應的觸摸點 (XTi,YTi), 通過表1中串口協議解析獲得顯示點對應的觸摸點坐標 (XTi,YTi), 本文3.3節中CalGetCoef()函數的實現就是依據式(16)~式(22),求解得到式(11)中的參數A,B,C,同樣也能計算出式(12)中的參數H,E,F。
當用戶去觸摸屏幕時,觸摸坐標點 (XTi,YTi), 就可以通過式(11)、式(12)轉換為對應的顯示坐標點 (XLi,YLi) 供用戶使用。
將轉換的坐標點以及從串口中按表1獲取的按鍵狀態封裝成UGL_MSG消息,通過uglInputMsgPost函數發送到消息隊列中,接著應用層通過調用uglInputMsgGet()函數從缺省的輸入隊列中獲取輸入消息。應用層獲取消息后,通過uglInputCbAdd()函數注冊輸入回調函數,實現消息響應,如鼠標指針移動、鼠標左擊。
實現原理:通過low-level輸入驅動接口close()函數關閉觸摸屏控制器RS232串口;釋放內存、析構相關參數。
UGL_LOCAL UGL_STATUS uglPM6500TsPtrClose (UGL_INPUT_DEV * pDevice)
{
…
if (pDevice != UGL_NULL)
{
//調用low-level層close函數實現串口關閉
close (pDevice->fd);
UGL_FREE (pDevice);
}
return UGL_STATUS_OK;
}
在應用層,使用顯示端對應的輸入服務(input server)找到關聯的輸入設備ID,然后將輸入設備ID作為參數,通過調用UGL_STATUSuglInputDevClose(UGL_INPUT_DEV_ID inDevId),實現對uglPM6500TsPtrClose驅動函數調用。
驅動功能實現后,需要創建一個驅動配置數據庫,供用戶在Wind River Workbench 3.2工程下通過WindML選擇需要配置的輸入設備信息。WindML配置是由一系列數據庫控制的,這些數據庫文件定義驅動特征。它們位于“installDircomponentswindml-5.3configwrmdb”目錄下。在Wind River Workbench 3.2工程中的config.windml配置工具會讀取這些數據庫文件加載相應的信息,用戶可通過下拉列表信息選擇配置對象。
本文,在“installDircomponentswindml-5.3configwrmdb”目錄下創建windML_PTR_DEVICE_PM6500_TS.wrmdb文件,用于配置PM6500觸摸屏驅動。配置內容如下:
//設備名稱
pm6500ts.NAME="PM6500 Touchscreen Pointer"
//定義設備驅動支持的處理器類型
pm6500ts.ARCH=&ARMARCH5:&ARMARCH6:&MIPSI2:&MIPSI3:&MIPSI32:&MIPSI64:&I80486:&PENTIUM:&PENTIUM2:&PENTIUM3:&PENTIUM4:&PPC32
//定義編譯該驅動時處理器字節的順序:大端或小端
pm6500ts.ENDIAN=&le:&be
//是否支持RTPs
pm6500ts.RTPENABLED=true
//指針設備必須定義的宏定義
pm6500ts.DEFINE=INCLUDE_UGL_INPUT
//條件宏定義
pm6500ts.SELECT=INCLUDE_PM6500_TS_POINTER
//對應的high-level輸入驅動入口
pm6500ts.CREATE=uglPM6500TsPtrDriver
//定義缺省的IO設備名稱
pm6500ts.DEVNAME=/tyCo/1
//對應的low-level輸入驅動入口及參數(缺省為系統自帶IO驅動)
pm6500ts.IODRV=/*缺省狀態, 默認使用系統自帶16550串口驅動*/
pm6500ts.IODRV_PARAM1=0
pm6500ts.IODRV_PARAM2=0
本文第3章實現了觸摸屏驅動,那么應用層就可以通過調用WindML標準函數實現觸摸屏操控。在應用層,用戶通過配置WindML驅動庫,實現用戶定制的顯示端配置,包括顯示界面驅動、輸入設備驅動、顯示字體驅動等配置。
Wind River Workbench 3.2軟件是開發VxWorks6.8操作系統下軟件的開發環境,在Wind River Workbench 3.2開發環境下,建立基于Media Libray 5.3組件的配置工程“VxWorks Downloadable Kernel Module Project”。在生成的工程中,在配置信息文件config.windml中對輸入設備進行配置,配置為本文設計的觸摸屏驅動,配置內容如下:Device=PM6500 Touchscreen Pointer;Device Name=/tyCo/1。這些參數的選擇均來自于本文第3.5節驅動配置數據庫,windML_PTR_DEVICE_PM6500_TS.wrmdb。
顯示終端配置完成后,編譯該工程,生成相應Wind Media Libray組件。
用戶新建應用工程后,需要在應用工程中添加使用觸摸屏驅動的組件。
包含Wind Media Libray組件如下:INCLUDE_WINDML、INCLUDE_WINDML_NECESSARY;
包含一個圖像組件:INCLUDE_PCI_WINDML_GRAPHICS;
包含一個識別設備映射方法:INCLUDE_WINDML_GRAPHICS_SHARED_DATA或 INCLUDE_WINDML_GRAPHICS_NO_SHARED_DATA。
添加完成后,編譯該應用工程使WindML組件在該工程中生效。自此,用戶可以在應用程序中調用WindML標準函數實現觸摸可控及觸摸校準等應用功能。
為了驗證觸摸屏驅動的正確性和觸摸屏校準算法的有效性,本文設計了測試用例進行評估。用例中設計了10個不同大小的正方形按鈕,大小從10像素*10像素,20像素*20像素,……100像素*100像素依次遞增10個像素構成10個按鈕,按鈕經編號后隨機分布在全屏幕,顯示屏幕像素為1024*768。
根據人為對觸摸屏與鼠標點擊按鈕的響應靈敏度對比結果作為評估標準。由5人操作體驗進行主觀評測。
同一個按鈕用觸摸屏和用鼠標點擊主觀靈敏度響應對比分4個等級:觸摸按鈕沒有按下響應計0分,觸摸屏響應靈敏小于鼠標,計1分,觸摸屏響應和鼠標相近,計2分,觸摸屏響應靈敏大于鼠標,計3分。每個按鈕點擊操作完成后,將5人的累計得分作為最終評測得分,若每個按鈕響應對比累計分數大于等于10分以上,說明觸摸屏靈敏度幾乎與鼠標接近,甚至優于鼠標,若分數小于5分,說明觸摸屏響應錯誤,若分數在5分與10分間,說明觸摸屏靈敏度弱于鼠標。
驗證過程中,觸摸屏按鈕響應正常,無響應失效發生,計算機運行良好。評估結果見表2,可以明顯看出觸摸屏響應靈敏度和鼠標響應靈敏度接近。進一步驗證了本文設計的觸摸屏驅動的正確以及校準算法的有效性。
本文討論了VxWorks操作系統下WindML實現輸入設備驅動的原理,詳細設計了觸摸屏驅動以及校準算法。在Wind River Workbench 3.2開發環境下設計了按鈕響應程序,通過實驗評估,驗證了本文實現的觸摸屏驅動及校準功能在用戶體驗上的及時性和準確性方面與鼠標響應幾乎無差別。本文實現的觸摸屏驅動擴展了用戶對輸入設備的使用,封裝成的觸摸屏驅動組件也豐富了WindML庫的種類,為用戶直接使用提供了便利。