1.适用范围

本文档为移植LCD8000-97C屏幕电容触控驱动到iMX6平台过程的总结。提供一些SylixOS触控相关的框架理解和移植心得。

2.原理概述

如图21所示:触控屏通过外部中断提醒主机从I2C总线上读取触控坐标和按压数据,主机读取到触控数据后将数据解析并转换成鼠标事件发送给系统上层,完成一次触控。

图 2-1 触控流程

3.技术实现

3.1Touch框架分析

/driver/touch/touch.c文件主要实现了SylixOS的touch框架。主体框架仅需要关注如图31所示的四个函数:

图 3-1 Touch框架重要函数

3.1.1函数__touchHwInit

该函数主要完成如下的功能:

1)创建触摸屏使用的I2C的总线设备;

2)注册设备触控中断;

3)申请设备复位管脚,复位设备;

4)调用设备Init函数。

3.1.2函数__touchIsr

这是函数__touchHwInit注册的中断响应服务,主要完成接收触控中断、清除中断,并发出一个touch相关的信号的功能。

3.1.3函数__touchThread

该函数是touch设备的一个线程,其在执行过程中等待touch信号,收到信号后调用函数__touchHandleEvents.

3.1.4函数__touchHandleEvents

该函数通过触控芯片驱动提供的getevent回调函数来获取数据生成的鼠标事件,并向上层发送相关鼠标事件。

3.1.5框架的流程图

整个框架的流程如图32所示:

图 3-2 Touch主体框架流程

3.2数据交互流程确认

移植的触控屏为英蓓特LCD8000-97c,由于原厂不提供相关触控芯片的资料支持,只能从支持该屏幕的Linux或者Android的调试信息和源码来获得相关IC信息,具体方式不再赘述。这里通过调试信息和源码确认屏幕触控芯片型号为CT365。

3.2.1Linux和Android下驱动源码分析

在RIotboard官方提供的源码中关于CT365的驱动是以二进制形式提供的,仅有头文件/driver/input/touchscreen/generic_ts_rel/ct365.h能作为参考,如程序清单 31所示,根据该头文件可以获取CT365的触控信息报文结构:

程序清单 3-1 ct365头文件中关于数据结构的描述

structstruct_ct365_pts_data{unsignedcharxhi;//XcoordinateHiunsignedcharyhi;//YcoordinateHiunsignedcharylo:4;//YcoordinateLounsignedcharxlo:4;//XcoordinateLounsignedcharstatus:3;//Actioninformation,1:Down;2:Move;3:Upunsignedcharid:5;//IDinformation,from1toCFG_MAX_POINT_NUMunsignedchararea;//Touchareaunsignedcharpressure;//TouchPressure};

程序清单 31大致可以判断出CT365的有效报文为6个字节,其中前三个字节的触控坐标信息和第四个字节前三位的触控状态是最为重要的数据,这些数据最终会解析成鼠标事件。

3.2.2逻辑分析仪采样

将触屏的I2C的两根线接入逻辑分析仪,触摸屏幕的时候抓取I2C通信数据(由于无法再次进行实验,不提供抓取的数据截图)。
根据逻辑分析仪抓取的数据,可以分析得出CT365的I2C设备地址为0x01,数据传输方式如图3-4所示:

图 3-4 CT365的数据通信流程

根据以上搜集的信息,结合硬件的原理图基本可以确定CT365的工作方式以及数据传输流程。
CT365和主板连接部分只有一个I2C接口和一个中断管脚,因此不需要初始化和复位,上电之后每次触控都会产生一个中断,通过中断通知系统向CT365数据发送一帧数据为0的写命令,随后发送读命令读取CT365反馈的触控信息,解析生产鼠标事件。

3.3代码实现

参考BSP包中其他的触控芯片驱动的实现,针对CT365仅需实现读函数,触摸数据的解析函数。分解成以下几个子函数:

ct365GetEvent

ct365GetTouchPoint

ct365GetRxData

ct36xRegRead

3.3.1函数ct365GetEvent

函数ct365GetEvent主要是由touch框架回调的,用来获取触控数据和生成鼠标事件的,所以先调用ct365GetRxData来获取触控数据,在调用ct365GetTouchPoint来解析生成鼠标事件。具体实现如程序清单 32所示:

程序清单 3-2 ct365GetEvent函数实现

INTct365GetEvent(PTOUCH_DEVpTouchDev,mouse_event_notifyevents[]){INTiError;UCHARucBuffer[32];iError=ct365GetRxData(pTouchDev,ucBuffer,sizeof(ucBuffer));if(iError==PX_ERROR){printk(KERN_WARNING"touch:gettouchpointerror!\n");return(PX_ERROR);}iError=ct365GetTouchPoint(pTouchDev,events,ucBuffer);return(iError);}

3.3.2函数ct365GetTouchPoint

函数ct365GetTouchPoint通过传入的触控数据,根据CT365头文件的数据报文结构分别解析出触控的坐标和触控的状态,封装出鼠标事件。具体实现如程序清单 3-3所示:

程序清单 3-3 ct365GetTouchPoint函数实现

staticINTct365GetTouchPoint(PTOUCH_DEVpTouchDev,mouse_event_notifyevents[],UINT8*pucData){INTiTouchPoint;iTouchPoint=pucData[3]&0x3;if(iTouchPoint>TOUCH_MAX_INPUT_POINTS){iTouchPoint=TOUCH_MAX_INPUT_POINTS;}if(iTouchPoint==1){events[0].xmovement=(INT16)((pucData[0]<<4)|(pucData[0]>>4&0xf));events[0].ymovement=(INT16)((pucData[1]<<4)|(pucData[2]&0xf));events[0].ctype=MOUSE_CTYPE_ABS;events[0].kstat=MOUSE_LEFT;pTouchDev->TOUCH_iLastX=events[0].xmovement;pTouchDev->TOUCH_iLastY=events[0].ymovement;}elseif(iTouchPoint==2){events[0].xmovement=(INT16)((pucData[0]<<4)|(pucData[0]>>4&0xf));events[0].ymovement=(INT16)((pucData[1]<<4)|(pucData[2]&0xf));events[0].ctype=MOUSE_CTYPE_ABS;events[0].kstat=MOUSE_LEFT;pTouchDev->TOUCH_iLastX=events[0].xmovement;pTouchDev->TOUCH_iLastY=events[0].ymovement;iTouchPoint=1;}elseif(iTouchPoint==3){events[0].xmovement=pTouchDev->TOUCH_iLastX;events[0].ymovement=pTouchDev->TOUCH_iLastY;events[0].ctype=MOUSE_CTYPE_ABS;events[0].kstat=0;iTouchPoint=TOUCH_RELEASE_NUM;}pTouchDev->TOUCH_iLastX=events[0].xmovement;pTouchDev->TOUCH_iLastY=events[0].ymovement;API_InterVectorEnable(pTouchDev->TOUCH_ulVector);return(iTouchPoint);}

3.3.3函数ct365GetRxData

函数ct365GetRxData调用函数ct36xRegRead去读取I2C数据。具体实现如程序清单 3-4所示:

程序清单 3-4 ct365GetRxData函数实现

staticINTct365GetRxData(PTOUCH_DEVpTouchDev,UINT8*pucBuffer,UINT16usLen){INTiError;iError=ct36xRegRead(pTouchDev->TOUCH_pI2cDevice,0,pucBuffer,usLen);return(iError);}

3.3.4函数ct36xRegRead

函数ct36xRegRead根据CT365的通讯流程读取CT365反馈的触控数据。具体实现如程序清单 3-5所示:

程序清单 3-5 ct36xRegRead函数实现

staticINTct36xRegRead(PLW_I2C_DEVICEpI2cDev,UINT8ucReg,UINT8*pucBuffer,UINT16usLen){INTiError;LW_I2C_MESSAGEi2cMsgs[]={{.I2CMSG_usAddr=pI2cDev->I2CDEV_usAddr,.I2CMSG_usFlag=0,.I2CMSG_usLen=1,.I2CMSG_pucBuffer=(PUCHAR)&ucReg,},{.I2CMSG_usAddr=pI2cDev->I2CDEV_usAddr,.I2CMSG_usFlag=LW_I2C_M_RD,.I2CMSG_usLen=usLen,.I2CMSG_pucBuffer=pucBuffer,},};iError=API_I2cDeviceTransfer(pI2cDev,i2cMsgs,2);if(iError!=2){printk(KERN_ERR"__ft5x06RxData():failedtoi2ctransfer!\n");return(PX_ERROR);}return(ERROR_NONE);}

3.4BSP中驱动配置

当触控驱动完成后,如程序清单 3-6所示,根据硬件原理图在BSP文件中配置好CT365的中断管脚和I2C信息:

程序清单 3-6 配置CT365驱动信息信息

staticTOUCH_DATA_G_Ct365Data={.T_pcBusName="/bus/i2c/2",/*I2C总线名称*/.T_uiIrq=IMX6Q_GPIO_NUMR(6,14),/*IRQ管脚号*/.T_uiReset=NULL,/*Reset管脚号*/.T_uiIrqCfg=GPIO_FLAG_IN|GPIO_FLAG_TRIG_FALL,/*IRQ中断模式*/.T_uiRstVal=LW_GPIOF_INIT_LOW,/*Reset复位电平*/.T_usAddr=0x01,/*I2C从机地址*/.T_iWidth=1024,/*分辨率宽*/.T_iHeight=768,/*分辨率高*/.T_iTouchNum=1,/*最大触摸点数*/};staticTOUCH_DRV_FUNC_G_CT365DrvFunc={.getevent=ct365GetEvent,.init=NULL,.deinit=NULL,.reset=NULL,};

如程序清单 3 7所示,配置完成后需要在函数boadDevInit里创建触控设备:

程序清单 3-7 创建CT365触控设备

touchDevCreate("/dev/input/touch0",_G_touchDrvNum,&_G_Ct365Data,&_G_CT365DrvFunc);

3.5测试

在系统正常启动和驱动正常加载的情况下,运行任意一个带触摸或者鼠标组件的QT程序,进行触控操作,如果正确响应触控事件,说明驱动移植基本完成。