android 电容屏(四):驱动调试之驱动程序分析篇 -- FocalTech

本人用的触摸屏IC是FocalTech公司的ft5306,是一款i2c的电容屏多点触控芯片。对于它的整体驱动官方已经给了,我们就触摸屏和按键部分的代码做相关说明。说明其中应该注意的地方。

对于所有的input设备,报告input事件时候都分这么几部分,首先在probe文件中设置设备发送的事件类型、按键类型、设置设备一些属性信息。然后在发送事件时候要根据probe的设置来发送事件,否则就会被判为无效忽略掉。

一、触摸屏部分

1.设备配置

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

__set_bit(EV_SYN, input_dev->evbit);  //设备同步,每次触摸完成以后都要发送一个同步事件,来表明这次触摸已经完成
__set_bit(EV_ABS, input_dev->evbit); //绝对坐标事件,触摸屏每次发送的坐标都是绝对坐标,不同于鼠标的相对坐标
__set_bit(EV_KEY, input_dev->evbit); //按键事件,每次触摸都有一个BTN_TOUCH的按键事件

触摸屏必须支持的按键类型

__set_bit(BTN_TOUCH, input_dev->keybit);//touch类型按键

触摸屏属性设置

input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS);//报告最大支持的点数
input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);//将触摸点看成一个椭圆,它的长轴长度。这个是可选项,并不影响正常使用。
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ft5x0x_ts->x_max, 0, 0);//x坐标取值范围
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ft5x0x_ts->y_max, 0, 0);//y坐标取值范围

2.事件发送

我们知道每次触摸完成后都必须发送一个同步事件(EV_SYN)来表明这次触摸的完成。 那么对于多点触控的屏幕事件发送分为两种方法,一是每次事件同步前包括多个点,一是每次事件同步前仅包含一个点。

先来看包含多个点的

static void ft5x0x_report_value(struct ft5x0x_ts_data *data)
{
    struct ts_event *event = &data->event;
    int i;
    int uppoint = 0;    //已经抬起的点数  

    for (i = 0; i < event->touch_point; i++)  //循环处理 缓存中的所有点
    {
        input_mt_slot(data->input_dev, event->au8_finger_id[i]);  //发送点的ID  

        if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  //如果点按下
        {
            input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,  true);  //手指按下
            input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->au16_x[i]); //x坐标
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y,event->au16_y[i]);    //y坐标
            input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure); //触摸点长轴长度
        }
        else
        {
            uppoint++;                              //没有按下,则表明这个手指已经抬起
            input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,false);   //报告手指抬起
        }  

    }  

        if(event->touch_point == uppoint)
        {
            input_report_key(data->input_dev, BTN_TOUCH, 0); //所有手指都抬起了 发送BTN_TOUCH 抬起事件  

        }
        else
        {
            input_report_key(data->input_dev, BTN_TOUCH, event->touch_point > 0);//还有手指没抬起,发送BTN_TOUCH 按下的事件   

        }
        input_sync(data->input_dev); //sync 设备同步  

}  

然后是每次同步仅发送一个点

static ft5x0x_report_value(struct ft5x0x_ts_data *data)  

{
    struct ts_event *event = &data->event;
    int i;  

    for (i = 0; i < event->touch_point; i++)  //循环处理 缓存中的所有点
    {
        input_mt_slot(data->input_dev, event->au8_finger_id[i]);  //发送点的ID
        if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  //如果点按下
        {
            input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,  true);  //手指按下
            input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->au16_x[i]); //x坐标
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y,event->au16_y[i]);    //y坐标
            input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure); //触摸点长轴长度
        }
        else
        {
            input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,false);   //手指抬起
        }  

        input_mt_report_pointer_emulation(input_dev, true);//用模拟点的方法,来告知此次触摸已经完成。
        input_sync(data->input_dev); //sync 设备同步  

    }
} 

这两种方法都可以,但是建议选择上面那种,效率比较高。

二、触摸按键部分

对于触摸按键的发送可以分为两种方法,一是android提供的 virtualkey‘s 架构方法,一种是直接报告key event的方法。我们一一来看

1.报告key event方法

在probe中添加所支持的按键类型,本人用的触摸屏上有三个按键因此

报告支持事件类型

__set_bit(EV_SYN, input_dev->evbit); 

__set_bit(EV_KEY, input_dev->evbit);

报告支持的按键

__set_bit(KEY_HOME, input_dev->keybit);
__set_bit(KEY_BACK, input_dev->keybit);
__set_bit(KEY_MENU, input_dev->keybit);

触摸屏上的三个按键对应的坐标

(KEY_BACK)  120:1400   (KEY_HOME) 360:1400(KEY_MENU)  500:1400

key event的报告方法很简单只要报告相应的key 和设备同步sync就可以了

static void ft5x0x_report_value(struct ft5x0x_ts_data *data)
{
    struct ts_event *event = &data->event;
    int i;
    for (i = 0; i < event->touch_point; i++)
    {
        if (event->au16_y[i]==1400)
        {
            if(event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)
            {  

                switch(event->au16_x[i])
                {
                case 120:
                    input_report_key(data->input_dev, KEY_BACK, 1);
                    break;
                case 360:
                    input_report_key(data->input_dev, KEY_HOME, 1);
                    break;
                case 500:
                    input_report_key(data->input_dev, KEY_MENU, 1);
                    break;
                default: break;
                }  

            }
            else
            {  

                switch(event->au16_x[i])
                {
                case 120:
                    input_report_key(data->input_dev, KEY_BACK, 0);
                    break;
                case 360:
                    input_report_key(data->input_dev, KEY_HOME, 0);
                    break;
                case 500:
                    input_report_key(data->input_dev, KEY_MENU, 0);
                    break;
                default: break;
                }  

            }
            input_sync(data->input_dev);
            return;
        }
}  

对于这种方法有一个bug,就是事件发送上去,系统并不认为是触摸屏发送的按键,系统的 触屏震动反馈 并不起作用。这并不符合标准的android触摸设备标准。具体怎么破本人比较菜没有找到方法,大神们谁知道 求破。

2.virtualkeys方法

virtualkeys是android提供的架构使用起来简单方便,推荐大家使用。直接上代码

static ssize_t ft5x06_virtual_keys_show(struct kobject *kobj,       //按键的配置
                    struct kobj_attribute *attr, char *buf)
{
    return sprintf(buf,
        __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":120:1400:8:8"   //键类型:键值:按键区域中心x坐标:按键区域中心y坐标:按键区域宽:按键区域高
        ":" __stringify(EV_KEY) ":"
                    __stringify(KEY_HOME) ":360:1400:8:8"
        ":" __stringify(EV_KEY) ":"
                    __stringify(KEY_MENU) ":500:1400:8:8"
        "\n");
}  

static struct kobj_attribute ft5x06_virtual_keys_attr = {
    .attr = {
        .name = "virtualkeys.Ft5x0x_Touch_Screen",  //这里的名字必须为virtualkeys.设备名字  否则系统不会识别
        .mode = S_IRUGO,
    },
    .show = &ft5x06_virtual_keys_show,
};  

static struct attribute *ft5x06_properties_attrs[] = {
    &ft5x06_virtual_keys_attr.attr,
    NULL,
};  

static struct attribute_group ft5x06_properties_attr_group = {
    .attrs = ft5x06_properties_attrs,
};  

static void ft5x06_virtual_keys_init(void)
{
    struct kobject *properties_kobj;
    int ret;  

    properties_kobj = kobject_create_and_add("board_properties", NULL);//添加目录board_properties  

    if (properties_kobj)
        ret = sysfs_create_group(properties_kobj,//生成/sys/board_properties/virtualkeys.Ft5x0x_Touch_Screen虚拟按键配置文件
            &ft5x06_properties_attr_group); //可以使用 cat /sys/board_properties/virtualkeys.Ft5x0x_Touch_Screen命令来查看配置是否正确
    if (!properties_kobj || ret)
        pr_err("failed to create board_properties\n");
}  

然后将ft5x06_virtual_keys_init()加入到 触摸屏的init 或者probe 函数中,这样触摸键就可以使用了。

三、触摸屏驱动流程

i2c中加入平台初始化代码

static struct ft5x0x_platform_data  ft5x0x_platform_i2c_data = {
    .x_max=540,
    .y_max=960,
    .irq= SABRESD_CHARGE_FLT_1_B, //中断引脚
    .reset=SABRESD_DISP0_RST_B,  //复位引脚
};  

触摸屏驱动初始化

static int __init ft5x0x_ts_init(void)
{
    int ret;
    ret = i2c_add_driver(&ft5x0x_ts_driver);
    if (ret) {
        printk(KERN_WARNING "Adding ft5x0x driver failed "
               "(errno = %d)\n", ret);
    } else {
        pr_info("Successfully added driver %s\n",
            ft5x0x_ts_driver.driver.name);
    }
    return ret;
}

probe函数

#define VIRTUAL_LI      0
#define EVENT_LI        1
#define TOUCH_KEY       VIRTUAL_LI
static int ft5x0x_ts_probe(struct i2c_client *client,
               const struct i2c_device_id *id)
{
    。。。。。。。。。。
    ft5x0x_ts = kzalloc(sizeof(struct ft5x0x_ts_data), GFP_KERNEL);//分配参数内存

    ..........
    i2c_set_clientdata(client, ft5x0x_ts);参数地址传给i2c 内核

    初始化一些参数
    ft5x0x_ts->irq = client->irq;
    ft5x0x_ts->client = client;
    ft5x0x_ts->pdata = pdata;
    ft5x0x_ts->x_max = pdata->x_max - 1;
    ft5x0x_ts->y_max = pdata->y_max - 1;
    ft5x0x_ts->pdata->reset = FT5X0X_RESET_PIN;
    ft5x0x_ts->pdata->irq = ft5x0x_ts->irq;
.....................
    err = request_threaded_irq(client->irq, NULL, ft5x0x_ts_interrupt,  //注册读取数据中断
                   IRQF_TRIGGER_FALLING, client->dev.driver->name,
                   ft5x0x_ts);
    。、、、、、、、、、、、、
    input_dev = input_allocate_device();//分配设备
...........................................
    __set_bit(EV_SYN, input_dev->evbit);  //注册设备支持event类型
    __set_bit(EV_ABS, input_dev->evbit);
    __set_bit(EV_KEY, input_dev->evbit);
    __set_bit(BTN_TOUCH, input_dev->keybit);
#if TOUCH_KEY == EVENT_LI               //如果使用event key的方法
    __set_bit(KEY_HOME, input_dev->keybit);
    __set_bit(KEY_BACK, input_dev->keybit);
    __set_bit(KEY_MENU, input_dev->keybit);
#endif
    input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS);   //设备属性
    input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR,
                 0, PRESS_MAX, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_POSITION_X,
                 0, ft5x0x_ts->x_max, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
                 0, ft5x0x_ts->y_max, 0, 0);

    input_dev->name ="Ft5x0x_Touch_Screen";//lijianzhang
    err = input_register_device(input_dev);         //注册这个input设备
    。。。。。。。。。。。
#if TOUCH_KEY == VIRTUAL_LI     //如果使用虚拟键盘设定

    ft5x06_virtual_keys_init();
#endif
。。。。。。。。。。。。。。。。。。。。。
}

中断处理

static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)
{
    struct ft5x0x_ts_data *ft5x0x_ts = dev_id;
    int ret = 0;
    disable_irq_nosync(ft5x0x_ts->irq);
    ret = ft5x0x_read_Touchdata(ft5x0x_ts); //读取数据
    if (ret == 0)
        ft5x0x_report_value(ft5x0x_ts);//报告数据

    enable_irq(ft5x0x_ts->irq);

    return IRQ_HANDLED;
}

报告事件

static void ft5x0x_report_value(struct ft5x0x_ts_data *data)
{
    struct ts_event *event = &data->event;
    int i;
    int uppoint = 0;

    /*protocol B*/
    for (i = 0; i < event->touch_point; i++)
    {
#if TOUCH_KEY == EVENT_LI       //如果使用 key event方法
        if (event->au16_y[i]==1400)
        {
            if(event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)
            {

                switch(event->au16_x[i])
                {
                case 120:
                    input_report_key(data->input_dev, KEY_BACK, 1);
                    break;
                case 360:
                    input_report_key(data->input_dev, KEY_HOME, 1);
                    break;
                case 500:
                    input_report_key(data->input_dev, KEY_MENU, 1);
                    break;
                default: break;
                }

            }
            else
            {

                switch(event->au16_x[i])
                {
                case 120:
                    input_report_key(data->input_dev, KEY_BACK, 0);
                    break;
                case 360:
                    input_report_key(data->input_dev, KEY_HOME, 0);
                    break;
                case 500:
                    input_report_key(data->input_dev, KEY_MENU, 0);
                    break;
                default: break;
                }
                uppoint++;

            }
            input_sync(data->input_dev);
            return;
        }

#endif

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

        if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)
        {
            input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
            input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->au16_x[i]);       //lijianzhang
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y,event->au16_y[i]);
            input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure);
        }
        else
        {
            uppoint++;
            input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
        }

    }

        if(event->touch_point == uppoint)
        {
            input_report_key(data->input_dev, BTN_TOUCH, 0);
        }
        else
        {
            input_report_key(data->input_dev, BTN_TOUCH, event->touch_point > 0);
        }
        input_sync(data->input_dev);

}

这里驱动流程做了简略的说明,关键的代码都已经贴出来了。与设备相关代码都是厂商给的没有太实际参考价值.

从android input的流程分析我们知道,驱动编译完成以后,要使触摸屏工作,还需要三个文件:触摸屏配置文件 (idc文件,用来配置触摸屏的一些属性)、keylayout文件(kl文件,安卓层面的按键映射文件)、characterMap文件(kcm文件,安卓层面的字符映射文件)
我们一一来看这三个文件

1.触摸屏配置文件

文件所在目录访问顺序:

首先ANDROID_ROOT/usr/idc目录下去找相应名字的文件并返回完整的路径名,如果找不到就从ANDROID_DATA/system/devices/idc下面去找,这里ANDROID_ROOT一般指的是/system目录,ANDROID_DATA一般指/data目录.

文件名称的查找顺序首先是Vendor_XXXX_Product_XXXX_Version_XXXX.idc,然后是Vendor_XXXX_Product_XXXX.idc最后是DEVICE_NAME.idc

总结来看安卓为输入设备打开配置文件依次会访问

/system/usr/idc/Vendor_XXXX_Product_XXXX_Version_XXXX.idc
/system/usr/idc/Vendor_XXXX_Product_XXXX.idc
/system/usr/idc/DEVICE_NAME.idc
/data/system/devices/idc/Vendor_XXXX_Product_XXXX_Version_XXXX.idc
/data/system/devices/idc/Vendor_XXXX_Product_XXXX.idc
/data/system/devices/idc/DEVICE_NAME.idc

我们驱动里并没有写版本号等这些信息,因此我们设备访问的idc文件会是/system/usr/idc/DEVICE_NAME.idc。因此我们在这个目录下增加文件Ft5x0x_Touch_Screen.idc.对于idc文件的内容,下面是我使用的idc文件的具体内容,仅供参考

touch.deviceType = touchScreen
touch.orientationAware = 1

touch.size.calibration = none
touch.orientation.calibration = none                                    

2.key layout文件

key layout文件是android层面的按键映射文件,通过这个文件,用户可以对kernel发送上来的按键功能进行重新定义。也就是说,kernel发送上来一个home键,你可以在这里把它映射成一个back键或者其他的。一般情况下不会修改这个文件,因此我么完全可以使用默认的配置文件

这个文件访问顺序

/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl
/data/system/devices/keylayout/DEVICE_NAME.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl

这里不用修改因此不用做改变

3.characterMap文件

characterMap文件是android层面的字符映射文件,比如:你摁下了一个‘e‘键,平时代表‘e‘,shift+‘e‘代表‘E‘,casplk+‘e‘代表‘E‘,alt+‘e‘可能代表别的意思,这个配置文件就是,做这些映射的。一般情况下这个文件也不用修改。使用默认的就可以。这个文件的访问顺序:

到了这里 我们的触摸屏已经完成了,烧写以后应该可以正常使用了。

在这里分享一个小技巧,getevent 这个工具,在/dev/input/目录下使用这个命令,会首先得到系统中所有input设备的描述,然后会得到,kernel发送的所有input事件,当我们写完驱动以后,可以用这个命令将发送的事件打印出来,看驱动写的是否正确。

原文地址:https://www.cnblogs.com/linhaostudy/p/11345758.html

时间: 2024-11-19 01:30:06

android 电容屏(四):驱动调试之驱动程序分析篇 -- FocalTech的相关文章

android 电容屏(三):驱动调试之驱动程序分析篇

平台信息: 内核:linux3.4.39系统:android4.4 平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新浪博客:http://weibo.com/cpjphone   以goodix的gt8105为例 一.总体架构 硬件部分:先看一个总体的图吧,其实触摸屏原理也比较简单,触摸屏和主控芯片间的联系,如下主要有三部分: 1.IIC部分,初始化gt8105的数据和传回主控制的坐标位置信息就是通过IIC这条线传输

android 电容屏(二):驱动调试之基本概念篇

关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung exynos 4210)  作者:xubin341719(欢迎转载,请注明作者) 参考网站:http://edsionte.com/techblog/archives/1582这部分参考别人的多一点 android 电容屏(一):电容屏基本原理篇 android 电容屏(二):驱动调试之基本概念篇

android 电容屏(一):电容屏基本原理篇

关键词:android  电容屏 tp  ITO 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung exynos 4210)  作者:xubin341719(欢迎转载,请注明作者) android 电容屏(一):电容屏基本原理篇 android 电容屏(二):驱动调试之基本概念篇 android 电容屏(三):驱动调试之驱动程序分析篇 一.电容屏工作原理 触摸屏的工作原理概括来说就是上报坐标值,X轴.Y轴的值.前面

LCD屏背光驱动调试心得---血的教训

开发板:明远智睿MY-IMX6-EK140 内核源码:linux-3.14.52 背光驱动IC:MP3202 调光原理:通过开发板的核心板PWM4引脚控制MP3202的EN脚,输出不同的占空比从而达到输出平均电流的不同,这样就可以达到调节背光亮度的目的 调试过程: 1.从硬件图可以看出核心板的PWM4引脚连接到了背光驱动IC MP3202的EN脚,所以我们通过搜索PWM4来找设备树上的硬件节点 2.在明远智睿MY-IMX6-EK140设备树,arch/arm/boot/dts/myimx6ek1

【转】 Android BCM4330 蓝牙BT驱动调试记录

原文网址:http://blog.csdn.net/dwyane_zhang/article/details/7180779 网上关于BT的驱动很少,所以我在开发过程中把其中的步骤记录下来.供大家相互学习讨论. 一.关于BT driver的移植: 1. Enablebluetootch in BoadConfig.mk BOARD_HAVE_BLUETOOTH := true 2.实现BT电源管理rfkill驱动. Kernel/driver/bluetooth/bluetooth-power.

基于GPL329xx linux平台电容屏gsl1680的驱动调试分析

因客户有用到了gsl1680 7寸电容屏,所以拿了一块过来,便在329xx的平台上面开始调试了. 大概浏览了一下所提供的资料,只有介绍模组的资料跟一份中文版的datasheet,datasheet只是说了个大概,没有提到读取触摸坐标的寄存器.不过还好有给一份在其他处理器平台的驱动,所以读取坐标的部分代码移植过来就可以了. gsl1680接口跟其他的电容屏一样,也是i2c接口的,貌似市面上的电容屏都是i2c接口,电容屏自带了微控制器MCU,用与处理采样,坐标转换等,还有一些抖动算法处理,完后将坐标

电容屏驱动技术

目录 一  电容屏介绍 二  input输入子系统 三  mtk ctp 软件控制流程 四  mtk平台调试ctp需要修改的地方 一.电容屏介绍介绍 1.电容式触摸屏的类型主要有两种: (1)表面电容式: 表面电容式利用位于四个角落的传感器以及均匀分布整个表面的薄膜,有一个普通的ITO层和一个金属边框,当一根手 指触摸屏幕时,从板面上放出电荷,感应在触屏的四角完成,不需要复杂的ITO图案: (2)投射式电容: 采用一个或多个精心设计,被蚀烛的ITO,这些 ITO层通过蛀蚀形成多个水平和垂直电极,

i.mx6 lvds接口的DE模式液晶屏驱动调试

我这篇文章主要讲述i.mx6 平台下 采用DE模式的lvds液晶屏的驱动调试, 阅读该文章之前请先阅读如下两篇我转载的文章,这两篇文章是理解我这篇文章的基础知识. 1.        lcd fb参数如何计算: http://blog.csdn.net/liuhuahan/article/details/43447657 2.        camera_lcd之DE和HV模式区别 http://blog.csdn.net/liuhuahan/article/details/43489269 详

Android 平台电容式触摸屏的驱动基本原理

本文地址,转载请注明:http://blog.csdn.net/dearsq/article/details/51251009 硬件工作原理 触摸屏的工作原理概括来说就是上报坐标值,X轴.Y轴的值.所以在 Linux 中是采用 input 子系统来对其进行实现. 具体的硬件原理可以参考这一篇文章 电容式触摸屏硬件基本原理. 本文主要归纳其驱动基本原理 与 Android平台上的移植步骤,并分析总结移植过程中碰到的问题. 驱动基本原理 触摸屏的驱动部分大概涉及到三个点: 中断 Linux 内核的中