重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
本文档为移植LCD8000-97C屏幕电容触控驱动到iMX6平台过程的总结。提供一些SylixOS触控相关的框架理解和移植心得。
创新互联公司专业为企业提供上街网站建设、上街做网站、上街网站设计、上街网站制作等企业网站建设、网页设计与制作、上街企业网站模板建站服务,十年上街做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
如图21所示:触控屏通过外部中断提醒主机从I2C总线上读取触控坐标和按压数据,主机读取到触控数据后将数据解析并转换成鼠标事件发送给系统上层,完成一次触控。
图 2-1 触控流程
/driver/touch/touch.c文件主要实现了SylixOS的touch框架。主体框架仅需要关注如图31所示的四个函数:
图 3-1 Touch框架重要函数
该函数主要完成如下的功能:
1)创建触摸屏使用的I2C的总线设备;
2)注册设备触控中断;
3)申请设备复位管脚,复位设备;
4)调用设备Init函数。
这是函数__touchHwInit注册的中断响应服务,主要完成接收触控中断、清除中断,并发出一个touch相关的信号的功能。
该函数是touch设备的一个线程,其在执行过程中等待touch信号,收到信号后调用函数__touchHandleEvents.
该函数通过触控芯片驱动提供的getevent回调函数来获取数据生成的鼠标事件,并向上层发送相关鼠标事件。
整个框架的流程如图32所示:
图 3-2 Touch主体框架流程
移植的触控屏为英蓓特LCD8000-97c,由于原厂不提供相关触控芯片的资料支持,只能从支持该屏幕的Linux或者Android的调试信息和源码来获得相关IC信息,具体方式不再赘述。这里通过调试信息和源码确认屏幕触控芯片型号为CT365。
在RIotboard官方提供的源码中关于CT365的驱动是以二进制形式提供的,仅有头文件/driver/input/touchscreen/generic_ts_rel/ct365.h能作为参考,如程序清单 31所示,根据该头文件可以获取CT365的触控信息报文结构:
程序清单 3-1 ct365头文件中关于数据结构的描述
struct struct_ct365_pts_data { unsigned char xhi; // X coordinate Hi unsigned char yhi; // Y coordinate Hi unsigned char ylo : 4; // Y coordinate Lo unsigned char xlo : 4; // X coordinate Lo unsigned char status : 3; // Action information, 1: Down; 2: Move; 3: Up unsigned char id : 5; // ID information, from 1 to CFG_MAX_POINT_NUM unsigned char area; // Touch area unsigned char pressure; // Touch Pressure};
程序清单 31大致可以判断出CT365的有效报文为6个字节,其中前三个字节的触控坐标信息和第四个字节前三位的触控状态是最为重要的数据,这些数据最终会解析成鼠标事件。
将触屏的I2C的两根线接入逻辑分析仪,触摸屏幕的时候抓取I2C通信数据(由于无法再次进行实验,不提供抓取的数据截图)。
根据逻辑分析仪抓取的数据,可以分析得出CT365的I2C设备地址为0x01,数据传输方式如图3-4所示:
图 3-4 CT365的数据通信流程
根据以上搜集的信息,结合硬件的原理图基本可以确定CT365的工作方式以及数据传输流程。
CT365和主板连接部分只有一个I2C接口和一个中断管脚,因此不需要初始化和复位,上电之后每次触控都会产生一个中断,通过中断通知系统向CT365数据发送一帧数据为0的写命令,随后发送读命令读取CT365反馈的触控信息,解析生产鼠标事件。
参考BSP包中其他的触控芯片驱动的实现,针对CT365仅需实现读函数,触摸数据的解析函数。分解成以下几个子函数:
ct365GetEvent
ct365GetTouchPoint
ct365GetRxData
ct36xRegRead
函数ct365GetEvent主要是由touch框架回调的,用来获取触控数据和生成鼠标事件的,所以先调用ct365GetRxData来获取触控数据,在调用ct365GetTouchPoint来解析生成鼠标事件。具体实现如程序清单 32所示:
程序清单 3-2 ct365GetEvent函数实现
INT ct365GetEvent (PTOUCH_DEV pTouchDev, mouse_event_notify events[]){ INT iError; UCHAR ucBuffer[32]; iError = ct365GetRxData(pTouchDev, ucBuffer, sizeof(ucBuffer)); if (iError == PX_ERROR) { printk(KERN_WARNING "touch: get touch point error!\n"); return (PX_ERROR); } iError = ct365GetTouchPoint(pTouchDev, events, ucBuffer); return (iError); }
函数ct365GetTouchPoint通过传入的触控数据,根据CT365头文件的数据报文结构分别解析出触控的坐标和触控的状态,封装出鼠标事件。具体实现如程序清单 3-3所示:
程序清单 3-3 ct365GetTouchPoint函数实现
static INT ct365GetTouchPoint (PTOUCH_DEV pTouchDev, mouse_event_notify events[], UINT8 *pucData) { INT iTouchPoint; 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; } else if (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; } else if (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); }
函数ct365GetRxData调用函数ct36xRegRead去读取I2C数据。具体实现如程序清单 3-4所示:
程序清单 3-4 ct365GetRxData函数实现
static INT ct365GetRxData (PTOUCH_DEV pTouchDev, UINT8 *pucBuffer, UINT16 usLen) { INT iError; iError = ct36xRegRead(pTouchDev->TOUCH_pI2cDevice, 0, pucBuffer, usLen); return (iError); }
函数ct36xRegRead根据CT365的通讯流程读取CT365反馈的触控数据。具体实现如程序清单 3-5所示:
程序清单 3-5 ct36xRegRead函数实现
static INT ct36xRegRead (PLW_I2C_DEVICE pI2cDev, UINT8 ucReg, UINT8 *pucBuffer, UINT16 usLen) { INT iError; LW_I2C_MESSAGE i2cMsgs[] = { { .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(): failed to i2c transfer!\n"); return (PX_ERROR); } return (ERROR_NONE); }
当触控驱动完成后,如程序清单 3-6所示,根据硬件原理图在BSP文件中配置好CT365的中断管脚和I2C信息:
程序清单 3-6 配置CT365驱动信息信息
static TOUCH_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, /* 最大触摸点数 */};static TOUCH_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);
在系统正常启动和驱动正常加载的情况下,运行任意一个带触摸或者鼠标组件的QT程序,进行触控操作,如果正确响应触控事件,说明驱动移植基本完成。