IMX6q ft5x0x_ts触摸芯片分析

Imx6使用的触摸屏控制芯片是ft5x06系列的,对应的文件为:ft5x06_ts.c。

Ft5x06_ts驱动涉及的内容如下:

1.  I2C驱动框架。

2.  中断子系统,中断分层思想。

3.  Workqueue机制。

4.  Input输入子系统。

IMX6q ft5x0x_ts触摸芯片分析_第1张图片

如上图所示,是触摸板与cpu的连接图,触摸板没有使用cpu的AD转换装置,ft5x06内部自带AD转换,将x y坐标通过I2C传送出来。当用户手指接触触摸屏时,CAP_TCH_INT0产生中断,在中断处理函数中,将数据读取出来,并通过处理传输到上层。

 

引脚功能配置

1.  I2C2功能初始化:

查看原理图可知道,触摸板与cpu的I2C-2相连。

在board-mx6q_sarbresd.h中找到mx6q_sabresd_pads[]数组,在数组中添加:

/* I2C2, Camera, MIPI */

MX6Q_PAD_KEY_COL3__I2C2_SCL,

MX6Q_PAD_KEY_ROW3__I2C2_SDA,

2.将A16(NANDF_ALE)引脚设置为中断模式

仍然在mx6q_sabresd_pads[]数组中添加:

/* CAP_TCH_INT0*/

MX6Q_PAD_NANDF_ALE__GPIO_6_8,

 

板级文件添加

board-mx6q_sarbresd.c中找到mxc_i2c1_board_info2[]数组,这里很奇怪,为什么是mxc_i2c1?这是飞思卡尔的定义,在原理图中,I2C以1开头,而在驱动中,I2C又是以I2C0开头。在xc_i2c1_board_info2[]添加:

{

I2C_BOARD_INFO("ft5x0x_ts", 0x38),

 .irq = gpio_to_irq(SABRESD_CAP_TCH_INT0),

},

通过查看原理图,和芯片手册,可以知道Ft5x06_ts的地址是0x38。将触摸板的设备名字取为“ft5x06_ts”,I2C通信的地址为0x38。

.irq = gpio_to_irq(SABRESD_CAP_TCH_INT0),设置A16引脚的中断号,gpio_to_irq是飞思卡尔提供的一个接口函数,宏SABRESD_CAP_TCH_INT0的定义为:

#define SABRESD_CAP_TCH_INT0    IMX_GPIO_NR(6, 8)

关于如何使用引脚,参考:

http://blog.csdn.net/sddsighhz/article/details/36653517

 

板级文件添加好了之后,接下来分析驱动,驱动对应的文件是:ft5x06_ts.c。

老方法,驱动的入口函数是module_init:

module_init(ft5x0x_ts_init);

 

static int __init ft5x0x_ts_init(void)

{

    int ret;

   //printk("ft5x0x_ts_init\n");

    ret =i2c_add_driver(&ft5x0x_ts_driver);

    //printk("ret=%d\n",ret);

    return ret;

 

}

ft5x0x_ts_init函数中注册了一个i2c驱动,找到ft5x0x_ts_driver结构。

static struct i2c_driver ft5x0x_ts_driver = {

.probe        = ft5x0x_ts_probe,

.remove       = __devexit_p(ft5x0x_ts_remove),

.id_table = ft5x0x_ts_id,

.driver  = {

     .name    = FT5X0X_NAME,

     .owner   = THIS_MODULE,

},

};

  I2C设备和驱动是通过.name匹配的,FT5X0X_NAME宏就是ft5x0x_ts。

#defineFT5X0X_NAME   "ft5x0x_ts"

前面板级文件中,有:I2C_BOARD_INFO("ft5x0x_ts",0x38)正好和驱动中的FT5X0X_NAME匹配,配备成功之后,就会运行.probe。

接下来分析ft5x0x_ts_probe函数

代码就不全部贴出来了,分析关键部分。

if (!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {

        err =-ENODEV;

        goto exit_check_functionality_failed;

    }

上面代码判断i2c适配器的所支持的通信方法,如果这个适配器不具有I2C功能,则失败。

 

ft5x0x_ts = kzalloc(sizeof(struct ft5x0x_ts_data),GFP_KERNEL);

    //ft5x0x_ts= kmalloc(sizeof(struct ft5x0x_ts_data), GFP_KERNEL);

    if(!ft5x0x_ts) {

        err =-ENOMEM;

        goto exit_alloc_data_failed;

    }

上述代码分配一个structft5x0x_ts_data结构,这个结构是自己定义的,我们看看这个结构,并注释好结构中的内容:

structft5x0x_ts_data {

struct input_dev    *input_dev;//input输入设备结构

struct ts_event     event;//飞凌定义的触摸结构,包括x坐标,y坐标等。

struct work_struct pen_event_work;//工作结构体,用于中断分层

struct workqueue_struct *ts_workqueue;//工作队列结构体,用于处理中断下部分工作

struct early_suspend    early_suspend;//安卓的休眠机制。

struct mutex device_mode_mutex;   /* Ensures that only one function canspecify the Device Mode at a time. */

};

 

reqirq_id = client->irq;

这行代码是获取板级文件中的中断号。 .irq =gpio_to_irq(SABRESD_CAP_TCH_INT0)

 

i2c_set_clientdata(client, ft5x0x_ts);

将自定义的设备结构ft5x0x_ts赋给设备驱动client的私有指针.

 

mutex_init(&ft5x0x_ts->device_mode_mutex);

初始化互斥锁。

 

INIT_WORK(&ft5x0x_ts->pen_event_work,ft5x0x_ts_pen_irq_work);

初始化工作队列。待会儿需要关注ft5x0x_ts_pen_irq_work。

 

ft5x0x_ts->ts_workqueue =create_singlethread_workqueue(dev_name(&client->dev));

创建workqueue,只创建一个内核线程。它和create_workqueue区别:create_workqueue用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。

 

err =request_irq(reqirq_id, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING,"ft5x0x_ts", ft5x0x_ts);

申请中断,采用的是下降沿的方式触发中断,当中断产生时,进入到ft5x0x_ts_interrupt函数中执行。

 

disable_irq(reqirq_id);

使中断失效。

 

input_dev = input_allocate_device();

分配输入型设备结构

 

ft5x0x_ts->input_dev = input_dev;

将输入型设备结构复制给struct ft5x0x_ts结构中的input_dev成员变量。

 

接下来的代码,将重点分析:

#ifdef CONFIG_ANDROID

    set_bit(ABS_MT_TOUCH_MAJOR,input_dev->absbit);

    set_bit(ABS_MT_POSITION_X,input_dev->absbit);

    set_bit(ABS_MT_POSITION_Y,input_dev->absbit);

    set_bit(ABS_MT_WIDTH_MAJOR,input_dev->absbit);

    input_set_abs_params(input_dev,

                 ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);

    input_set_abs_params(input_dev,

                 ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);

    input_set_abs_params(input_dev,

                 ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);

    input_set_abs_params(input_dev,

                 ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);

   input_set_abs_params(input_dev,

                 ABS_MT_TRACKING_ID, 0, 5, 0, 0);

   set_bit(EV_KEY, input_dev->evbit);

   set_bit(EV_ABS, input_dev->evbit);

#if CFG_SUPPORT_TOUCH_KEY

    //setup keycode area

   set_bit(EV_SYN, input_dev->evbit);

   set_bit(BTN_TOUCH, input_dev->keybit);

   input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //new

   input_dev->keycode = tsp_keycodes_forlinx;

    for(i = 0;i < CFG_NUMOFKEYS; i++)

    {

       input_set_capability(input_dev, EV_KEY,((int*)input_dev->keycode)[i]);

       tsp_keystatus_forlinx[i] = KEY_RELEASE;

    }

#endif

#else

   set_bit(EV_SYN, input_dev->evbit);

   set_bit(EV_KEY, input_dev->evbit);

    set_bit(EV_ABS,input_dev->evbit);

   set_bit(BTN_TOUCH, input_dev->keybit);

   input_set_abs_params(input_dev, ABS_X, TOUCHSCREEN_MINX,TOUCHSCREEN_MAXX, 0, 0);

   input_set_abs_params(input_dev, ABS_Y, TOUCHSCREEN_MINY,TOUCHSCREEN_MAXY, 0, 0);

    input_set_abs_params(input_dev,ABS_PRESSURE, 0, 255, 0, 0);

   input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);

#endif

首先,上述这段代码用宏控制用于安卓还是linux,安卓设备中,一般支持多点触控,在上报input event的时候,需要设置多点触控的相关属性,以便于在中断中实现多点触控的协议。Linux+QT设备,一般不需要使用多点触控的功能。

set_bit(ABS_MT_TOUCH_MAJOR,input_dev->absbit);ABS_MT_TOUCH_MAJOR:接触区域的长轴的长度。该长度应该按接触表面的单位提供。如果表面的分辨率是X-Y,则ABS_MT_TOUCH_MAJOR可能的最大值是sqrt(X^2 + Y^2)。

set_bit(ABS_MT_POSITION_X,input_dev->absbit);ABS_MT_POSITION_X:接触中心的X坐标。

set_bit(ABS_MT_POSITION_Y, input_dev->absbit);ABS_MT_POSITION_Y:接触中心的Y坐标。

set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);ABS_MT_WIDTH_MAJOR:工具轮廓区域短轴的表面单位长度,圆形的话可以被忽略。

input_set_abs_params(input_dev,ABS_MT_POSITION_X, 0,SCREEN_MAX_X, 0, 0);设置x坐标取值范围。SCREEN_MAX_X为800

input_set_abs_params(input_dev,ABS_MT_POSITION_Y,0, SCREEN_MAX_Y, 0, 0);设置y坐标取值范围。SCREEN_MAX_X为480

input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR,0, PRESS_MAX, 0, 0);将触摸点看成一个椭圆,这里是设置它的长轴长度。这个是可选项,并不影响正常使用。PRESS_MAX是255.

input_set_abs_params(input_dev,ABS_MT_WIDTH_MAJOR,0, 200, 0, 0); 如果设备支持ABS_MT_WIDTH_MAJOR这个事件,那么此事件可以提供手指触摸接触面积大小。

 ABS_MT_TOUCH_MAJOR表示了手指接触TP的直径的近似。ABS_MT_WIDTH_MAJOR是手指的直径的近似。当手指和触摸屏接触的越紧密,则压力越大,手指和屏幕接触的面积(直径)也会越大。而手指的直径通常是一个常量。这样ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR就可以用来表示压力了。而且这个值在[0,1)间。对于某些提供了压力值的TP,可以使用ABS_MT_PRESSURE来替代ABS_MT_TOUCH_MAJOR.

input_set_abs_params(input_dev,ABS_MT_TRACKING_ID,0, 5, 0, 0);设置上报的id数目为5个。ABS_MT_TRACKING_ID,用来支持硬件跟踪多点信息,即该点属于哪一条线等。

对于触摸屏,必须支持的事件类型有以下这么三个

set_bit(EV_SYN, input_dev->evbit); //设备同步,每次触摸完成以后都要发送一个同步事件,来表明这次触摸已经完成

set_bit(EV_ABS, input_dev->evbit); //绝对坐标事件,触摸屏每次发送的坐标都是绝对坐标,不同于鼠标的相对坐标

set_bit(EV_KEY, input_dev->evbit); //按键事件,每次触摸都有一个     

 

#if CFG_SUPPORT_TOUCH_KEY

    //setup keycode area

   set_bit(EV_SYN, input_dev->evbit);

   set_bit(BTN_TOUCH, input_dev->keybit);

   input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //new

   input_dev->keycode = tsp_keycodes_forlinx;

    for(i = 0;i < CFG_NUMOFKEYS; i++)

    {

        input_set_capability(input_dev,EV_KEY, ((int*)input_dev->keycode)[i]);

       tsp_keystatus_forlinx[i] = KEY_RELEASE;

    }

#endif

这部分代码是支持屏幕之外的触摸按键的,就是我们安卓手机home键那一排的三个实体的按键(back home menu),这里要却别于安卓的虚拟按键,有很多厂商在显示屏内部也搞了三个虚拟按键(back home menu),虚拟按键是在framework层实现的,这里我们不管。

代码中的input_set_capability(input_dev, EV_KEY,((int*)input_dev->keycode)[i]); 用于记录本设备对于哪些事件感兴趣,这里把(back home menu)按键记录下来了。我们知道,实体按键是和触摸屏连在一起的,在LCD屏的显示区域外,当触摸屏发现x坐标>800,或者y坐标> 480的时候,我们就需要去处理本设备“感兴趣”事件。

Linux的部分先不分析,比较简单。

继续分析ft5x0x_ts_probe。

input_dev->name= FT5X0X_NAME;

设置input设备的名字为ft5x0x_ts。

 

err = input_register_device(input_dev);

注册input设备驱动,代码运行到这儿,linux内核中就可以在设备的/dev/input/目录下看到相应的eventX设备了,至于是event几,要看内核的分配。

 

#ifdef CONFIG_HAS_EARLYSUSPEND

    //printk("==register_early_suspend=\n");

    ft5x0x_ts->early_suspend.level= EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;

    ft5x0x_ts->early_suspend.suspend= ft5x0x_ts_suspend;

    ft5x0x_ts->early_suspend.resume = ft5x0x_ts_resume;

    register_early_suspend(&ft5x0x_ts->early_suspend);

#endif

上面的这段代码是与安卓休眠机制有关的,early_suspend是Android休眠流程的第一阶段即浅度休眠,不会受到wake_lock的阻止,一般用于关闭lcd、tp等设备为运行的应用节约电能。Android的PowerManagerService会根据用户的操作情况调整电源状态,如果需要休眠则会调用到HAL层的set_screen_state()接口,在set_screen_state()中会向/sys/power/state节点写入"mem"值让驱动层开始进入休眠流程。

.level代表的是这个休眠的等级。这里设置的是51级

.suspend是休眠挂起的函数,我们进入到ft5x0x_ts_suspend函数中,其实只有一行代码:

disable_irq(this_client->irq);其实就是使中断失能能。当安卓设备休眠之后,我们再按屏幕是没有反应的,因为中断已经被我们关闭了。

. resume是唤醒函数,进入到ft5x0x_ts_resume函数中,其实就是使中断使能而已。enable_irq(this_client->irq);

register_early_suspend(&ft5x0x_ts->early_suspend);把early_suspend注册到内核。这样系统就可以统一调度休眠唤醒了。

关于安卓浅度休眠的详细介绍,参考博客http://blog.csdn.net/g_salamander/article/details/7982170

 

   msleep(150);  //make sure CTPalready finish startup process

   

    //get someregister information

   uc_reg_value = ft5x0x_read_fw_ver();

   //printk("[FTS] Firmware version = 0x%x\n", uc_reg_value);

   ft5x0x_read_reg(FT5X0X_REG_PERIODACTIVE, &uc_reg_value);

   //printk("[FTS] report rate is %dHz.\n", uc_reg_value * 10);

   ft5x0x_read_reg(FT5X0X_REG_THGROUP, &uc_reg_value);

   //printk("[FTS] touch threshold is %d.\n", uc_reg_value * 4);

 

//#ifdef CONFIG_ANDROID

//#else

   printk("uc_reg_value=%d \n",uc_reg_value);

    //uc_reg_value=128 while read iic error

   if(uc_reg_value<128)

    {

        ts_proc_entry =create_proc_entry("driver/ft5x06_ts", 0, NULL);

        //if (ts_proc_entry) {

        //  ts_proc_entry->write_proc= 0;

        //}

    }

上面的这些代码就是去读取ft5x0x_ts相关寄存器的内容,其结果就起到调试的作用。

ts_proc_entry = create_proc_entry("driver/ft5x06_ts",0, NULL);会在开发板/proc/driver/目录创建一个ft5x0x_ts文件,这个文件不可用来操作ft5x0x_ts。

 

#if CFG_SUPPORT_AUTO_UPG

   fts_ctpm_auto_upg();

#endif   

 

#if CFG_SUPPORT_UPDATE_PROJECT_SETTING

   fts_ctpm_update_project_setting();

#endif

上面的这些代码是不编译的。

 

enable_irq(reqirq_id);

probe函数退出之前,需要使能中断。

 

err = sysfs_create_group(&client->dev.kobj,&ft5x0x_attribute_group);

在kobj目录下创建一个属性集合,并显示集合中的属性文件。也就是这个input设备的属性集合。

 

分析probe函数中提交的工作

在ft5x0x_ts_probe函数中,我们分析到注册了一个中断函数。

err = request_irq(reqirq_id, ft5x0x_ts_interrupt,IRQF_TRIGGER_FALLING, "ft5x0x_ts", ft5x0x_ts);

中断来临时,会执行注册的ft5x0x_ts_interrupt函数,打开该函数:

static irqreturn_t ft5x0x_ts_interrupt(int irq, void*dev_id)

{

    structft5x0x_ts_data *ft5x0x_ts = dev_id;

    //disable_irq(IRQ_EINT(6));

   disable_irq_nosync(reqirq_id);

//   printk("-------------------------INT______-\n");

 //  enable_irq(reqirq_id);

    if(!work_pending(&ft5x0x_ts->pen_event_work))

    {

        queue_work(ft5x0x_ts->ts_workqueue,&ft5x0x_ts->pen_event_work);

    }

    return IRQ_HANDLED;

}

进入中断后,先disable_irq_nosync(reqirq_id);关闭中断,防止触摸屏再次发送中断过来。为什么使用disable_irq_nosync呢?

因为disable_irq关闭中断并等待中断处理完后返回, 而disable_irq_nosync立即返回.在中断中是不允许使用disable_irq的,否则会导致cpu被synchronize_irq独占而发生系统崩溃.

接着work_pending(&ft5x0x_ts->pen_event_work)检查之前初始化的work队列是否被挂起,如果没有挂起,则提交工作(queue_work(ft5x0x_ts->ts_workqueue,&ft5x0x_ts->pen_event_work);)。

在ft5x0x_ts_probe函数中,我们也分析到初始化了一个工作:

INIT_WORK(&ft5x0x_ts->pen_event_work,ft5x0x_ts_pen_irq_work);

中断中提交了工作之后,中断就结束了,然后下半部分的中断就留给ft5x0x_ts_pen_irq_work函数处理。

static void ft5x0x_ts_pen_irq_work(struct work_struct*work)

{

    int ret =-1;

    ret =ft5x0x_read_data();  

    if (ret ==0)

    {

        ft5x0x_report_value();

    }

   

    enable_irq(reqirq_id);

}

下半部分中断中,先读取触摸芯片的数据,然后通过一系列的处理传送给上层,最后重新使能中断。

 

读取触摸芯片分析

static int ft5x0x_read_data(void)

{

    structft5x0x_ts_data *data = i2c_get_clientdata(this_client);

    structts_event *event = &data->event;

    u8buf[CFG_POINT_READ_BUF] = {0};

    int ret =-1;

    int i;

   //printk("start read_data i2c_rxdata\n");

    ret = ft5x0x_i2c_rxdata(buf,CFG_POINT_READ_BUF);

    if (ret< 0) {

        //printk("%sread_data i2c_rxdata failed: %d\n", __func__, ret);

        returnret;

    }

    memset(event,0, sizeof(struct ts_event));

    event->touch_point= buf[2] & 0x07;

    if(event->touch_point > CFG_MAX_TOUCH_POINTS)

    {

       event->touch_point = CFG_MAX_TOUCH_POINTS;

    }

    for (i = 0;i < event->touch_point; i++)

    {

       event->au16_x[i] = (s16)(buf[3 + 6*i] & 0x0F)<<8 |(s16)buf[4 + 6*i];

       event->au16_y[i] = (s16)(buf[5 + 6*i] & 0x0F)<<8 |(s16)buf[6 + 6*i];

       event->au8_touch_event[i] = buf[0x3 + 6*i] >> 6;

       event->au8_finger_id[i] = (buf[5 + 6*i])>>4;

       printk("\ntouch_point = %d event->au16_x[%d] = %u event->au16_y[%d] = %u \nau8_touch_event[%d] = %u au8_finger_id[%d] =%u\n",event->touch_point,i,event->au16_x[i],i,event->au16_y[i],i,event->au8_touch_event[i],event->au8_finger_id[i]);

    }

   if(event->touch_point==0)

    {

      printk("Touch up,it is end !\n");

    }

   event->pressure = 200;

   //printk("start read_data i2c_rxdata OK\n");

    return 0;

}

函数首先定义个buf数组,大小为3+6*5。这个数组格式正好是ft5x0x_ts芯片上报的数据格式。buf[0-2],包含的是一些基本信息,比如有多少个手指按下;每个6byte空间存放每个触控点的信息,这些信息包含接触点的x坐标(2byte)、y坐标(2byte)、按下还是弹起(1byte)、touch ID(1byte),加起来正好是6byte。这款触摸屏最多支持5点触控,所以buf设置也只能容下5个接触点的信息。

ret = ft5x0x_i2c_rxdata(buf, CFG_POINT_READ_BUF);

读取3+6*5长度的数据,读取不到这么多时,返回实际的读取长度,这个函数的实现,实际上是使用I2C core层提供的i2c_transfer函数。关于I2C驱动,这里不详细介绍了。

event->pressure = 200;这句说明这款新品不支持触摸压力检测,所以设置了一个常量。

读取之后,就是填充event结构了。下面是打印出来的event填充信息:

IMX6q ft5x0x_ts触摸芯片分析_第2张图片

填充好event结构之后,就要上报event事件了,进入到ft5x0x_report_value函数中。我们简化一下代码,把宏去掉,把linux部分去掉:

static void ft5x0x_report_value(void)

{

    structft5x0x_ts_data *data = i2c_get_clientdata(this_client);

    structts_event *event = &data->event;

    int i;

    for (i  = 0; i < event->touch_point; i++)

    {

        if(event->au16_x[i] < SCREEN_MAX_X && event->au16_y[i]

        {

            input_report_abs(data->input_dev,ABS_MT_POSITION_X, event->au16_x[i]);

            input_report_abs(data->input_dev,ABS_MT_POSITION_Y, event->au16_y[i]);

            input_report_abs(data->input_dev,ABS_MT_WIDTH_MAJOR, 1);

            input_report_abs(data->input_dev,ABS_MT_TRACKING_ID, event->au8_finger_id[i]);

            if (event->au8_touch_event[i]== 0 ||event->au8_touch_event[i] == 2)

            {

                input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR, event->pressure);

            }

            else

            {

                input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR, 0);

            }

        }

        else //maybe the touch key area

        {

            if(event->au16_x[i] >= SCREEN_MAX_X)

            {

               ft5x0x_touch_key_process_forlinx(data->input_dev,event->au16_x[i], event->au16_y[i], event->au8_touch_event[i]);

            }

        }

        input_report_key(data->input_dev,BTN_TOUCH, 1); //new

        input_mt_sync(data->input_dev);

    }

   if(event->touch_point==0)//touch up

    {

        intk=0;

       for(k=0;k

        {

           if(tsp_keystatus_forlinx[k] == KEY_PRESS)

            {

               input_report_key(data->input_dev, tsp_keycodes_forlinx[k], 0);

               tsp_keystatus_forlinx[k] = KEY_RELEASE;

               printk( "[FTS] %s key is up. Keycode : %d\n",tsp_keyname_forlinx[k], tsp_keycodes_forlinx[k]);

            }

        }

       input_report_key(data->input_dev, BTN_TOUCH, 0); //new

       input_mt_sync(data->input_dev);//new

    }

    input_sync(data->input_dev);

    if(event->touch_point == 0) {

       ft5x0x_ts_release();

        return;

    }

}   /*endft5x0x_report_value*/

先简单介绍下多点触控的协议。

多点触摸协议有两种,A协议和B协议。

根具代码看,是使用A协议,协议上说了报点格式是这样的,以两点为例:

       ABS_MT_POSITION_X x[0]

        ABS_MT_POSITION_Yy[0]

        SYN_MT_REPORT

       ABS_MT_POSITION_X x[1]

       ABS_MT_POSITION_Y y[1]

        SYN_MT_REPORT

        SYN_REPORT

如果第一个触点离开(抬起),这里的意思是说还有一个触点,需要继续上报这个触点。

       ABS_MT_POSITION_X x[1]

       ABS_MT_POSITION_Y y[1]

        SYN_MT_REPORT

        SYN_REPORT

如果两个触点都离开了,那么只需要报告一个同步事件就可以了。

        SYN_MT_REPORT

        SYN_REPORT

 

if(event->au16_x[i] >= SCREEN_MAX_X)

{

  ft5x0x_touch_key_process_forlinx(data->input_dev,event->au16_x[i], event->au16_y[i], event->au8_touch_event[i]);

 }

上述代码判断是否按下的是物理back、home、menu按键,SCREEN_MAX_X值是800,当x坐标值大于800时,ft5x0x_touch_key_process_forlinx函数将执行,然后将本设备记录的感兴趣事件上报给上层。

int ft5x0x_touch_key_process_forlinx(struct input_dev*dev, int x, int y, int touch_event)

{

    int i;

    int key_id;

    if( y <120&&y > 90)

    {

        key_id= 0;

    }

    else if (y< 67&&y > 47)

    {

        key_id= 1;

    }

    elseif(y<20&&y>=0)

    {

       key_id =2;

    }

    else

    {

        key_id= 0xf;

    }

    for(i = 0;i

    {

        if(key_id == i )

        {

            if(touch_event == 0)                                  // detect

            {

               input_report_key(dev, tsp_keycodes_forlinx[i], 1);

               printk( "[FTS] %s key is pressed. Keycode : %d\n",tsp_keyname_forlinx[i], tsp_keycodes_forlinx[i]);

               tsp_keystatus_forlinx[i] = KEY_PRESS;

            }

        }

    }

    return 0;

}

你可能感兴趣的:(安卓/linux驱动)