WindML下PM6500触摸屏驱动设计及实现
2023-11-03刘丽霞刘志鹏
刘丽霞,刘志鹏,张 力
(1.中国航天科工集团第二研究院 七〇六所,北京 100854;2.32379部队第一科室,北京 100072)
0 引 言
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库中触摸屏校准算法的空白。通过设计应用程序测试验证驱动的正确性和有效性,也说明了该算法可以进行推广和应用。
1 触摸屏介绍
鼠标(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 串口通信协议
2 基于WindML触摸屏驱动设计原理
2.1 WindML输入设备驱动分层架构
基于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输入驱动设计开发。
2.2 应用层响应WindML驱动机制
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;
3 实现触摸屏high-level输入驱动
3.1 创建驱动头文件和源文件
设计触摸屏驱动源文件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中实现的驱动函数进行详细阐述。
3.2 实现打开函数驱动
实现原理是:通过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)函数实现了输入设备与输入服务绑定。
3.3 实现控制函数驱动
实现原理:根据上层用户发送的请求类型分别执行控制功能,主要包括读取及处理原始数据、校准触摸屏、获取指针设备类型等。
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()函数注册输入回调函数,实现消息响应,如鼠标指针移动、鼠标左击。
3.4 实现驱动关闭函数
实现原理:通过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驱动函数调用。
3.5 创建一个驱动配置数据库
驱动功能实现后,需要创建一个驱动配置数据库,供用户在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
4 实验与分析
4.1 触摸屏驱动使用方法
本文第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标准函数实现触摸可控及触摸校准等应用功能。
4.2 驱动软件测试
为了验证触摸屏驱动的正确性和触摸屏校准算法的有效性,本文设计了测试用例进行评估。用例中设计了10个不同大小的正方形按钮,大小从10像素*10像素,20像素*20像素,……100像素*100像素依次递增10个像素构成10个按钮,按钮经编号后随机分布在全屏幕,显示屏幕像素为1024*768。
根据人为对触摸屏与鼠标点击按钮的响应灵敏度对比结果作为评估标准。由5人操作体验进行主观评测。
同一个按钮用触摸屏和用鼠标点击主观灵敏度响应对比分4个等级:触摸按钮没有按下响应计0分,触摸屏响应灵敏小于鼠标,计1分,触摸屏响应和鼠标相近,计2分,触摸屏响应灵敏大于鼠标,计3分。每个按钮点击操作完成后,将5人的累计得分作为最终评测得分,若每个按钮响应对比累计分数大于等于10分以上,说明触摸屏灵敏度几乎与鼠标接近,甚至优于鼠标,若分数小于5分,说明触摸屏响应错误,若分数在5分与10分间,说明触摸屏灵敏度弱于鼠标。
验证过程中,触摸屏按钮响应正常,无响应失效发生,计算机运行良好。评估结果见表2,可以明显看出触摸屏响应灵敏度和鼠标响应灵敏度接近。进一步验证了本文设计的触摸屏驱动的正确以及校准算法的有效性。
5 结束语
本文讨论了VxWorks操作系统下WindML实现输入设备驱动的原理,详细设计了触摸屏驱动以及校准算法。在Wind River Workbench 3.2开发环境下设计了按钮响应程序,通过实验评估,验证了本文实现的触摸屏驱动及校准功能在用户体验上的及时性和准确性方面与鼠标响应几乎无差别。本文实现的触摸屏驱动扩展了用户对输入设备的使用,封装成的触摸屏驱动组件也丰富了WindML库的种类,为用户直接使用提供了便利。