zoukankan      html  css  js  c++  java
  • 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)来表明这次触摸的完成。 那么对于多点触控的屏幕事件发送分为两种方法,一是每次事件同步前包括多个点,一是每次事件同步前仅包含一个点。

    先来看包含多个点的

    1. static void ft5x0x_report_value(struct ft5x0x_ts_data *data)  
    2. {  
    3.     struct ts_event *event = &data->event;  
    4.     int i;  
    5.     int uppoint = 0;    //已经抬起的点数  
    6.   
    7.     for (i = 0; i < event->touch_point; i++)  //循环处理 缓存中的所有点  
    8.     {  
    9.         input_mt_slot(data->input_dev, event->au8_finger_id[i]);  //发送点的ID  
    10.           
    11.         if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  //如果点按下  
    12.         {  
    13.             input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,  true);  //手指按下  
    14.             input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->au16_x[i]); //x坐标     
    15.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y,event->au16_y[i]);    //y坐标  
    16.             input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure); //触摸点长轴长度  
    17.         }  
    18.         else  
    19.         {  
    20.             uppoint++;                              //没有按下,则表明这个手指已经抬起  
    21.             input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,false);   //报告手指抬起  
    22.         }  
    23.           
    24.   
    25.     }  
    26.   
    27.         if(event->touch_point == uppoint)              
    28.         {  
    29.             input_report_key(data->input_dev, BTN_TOUCH, 0); //所有手指都抬起了 发送BTN_TOUCH 抬起事件  
    30.   
    31.         }  
    32.         else  
    33.         {  
    34.             input_report_key(data->input_dev, BTN_TOUCH, event->touch_point > 0);//还有手指没抬起,发送BTN_TOUCH 按下的事件   
    35.   
    36.         }  
    37.         input_sync(data->input_dev); //sync 设备同步  
    38.           
    39. }  


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

    1. static ft5x0x_report_value(struct ft5x0x_ts_data *data)  
    2.   
    3. {  
    4.     struct ts_event *event = &data->event;  
    5.     int i;  
    6.   
    7.     for (i = 0; i < event->touch_point; i++)  //循环处理 缓存中的所有点  
    8.     {  
    9.         input_mt_slot(data->input_dev, event->au8_finger_id[i]);  //发送点的ID  
    10.         if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  //如果点按下  
    11.         {  
    12.             input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,  true);  //手指按下  
    13.             input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->au16_x[i]); //x坐标     
    14.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y,event->au16_y[i]);    //y坐标  
    15.             input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure); //触摸点长轴长度  
    16.         }  
    17.         else  
    18.         {  
    19.             input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,false);   //手指抬起  
    20.         }  
    21.           
    22.   
    23.         input_mt_report_pointer_emulation(input_dev, true);//用模拟点的方法,来告知此次触摸已经完成。  
    24.         input_sync(data->input_dev); //sync 设备同步  
    25.           
    26.     }  
    27. }  

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

    二、触摸按键部分

    对于触摸按键的发送可以分为两种方法,一是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就可以了

    1. static void ft5x0x_report_value(struct ft5x0x_ts_data *data)  
    2. {  
    3.     struct ts_event *event = &data->event;  
    4.     int i;  
    5.     for (i = 0; i < event->touch_point; i++)  
    6.     {  
    7.         if (event->au16_y[i]==1400)  
    8.         {  
    9.             if(event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  
    10.             {  
    11.   
    12.                 switch(event->au16_x[i])  
    13.                 {  
    14.                 case 120:  
    15.                     input_report_key(data->input_dev, KEY_BACK, 1);  
    16.                     break;  
    17.                 case 360:   
    18.                     input_report_key(data->input_dev, KEY_HOME, 1);  
    19.                     break;  
    20.                 case 500:   
    21.                     input_report_key(data->input_dev, KEY_MENU, 1);  
    22.                     break;    
    23.                 defaultbreak;  
    24.                 }  
    25.                   
    26.             }  
    27.             else  
    28.             {  
    29.   
    30.                 switch(event->au16_x[i])  
    31.                 {  
    32.                 case 120:  
    33.                     input_report_key(data->input_dev, KEY_BACK, 0);  
    34.                     break;  
    35.                 case 360:   
    36.                     input_report_key(data->input_dev, KEY_HOME, 0);  
    37.                     break;  
    38.                 case 500:   
    39.                     input_report_key(data->input_dev, KEY_MENU, 0);  
    40.                     break;    
    41.                 defaultbreak;  
    42.                 }  
    43.                   
    44.             }  
    45.             input_sync(data->input_dev);  
    46.             return;  
    47.         }  
    48. }  
    49.       
    对于这种方法有一个bug,就是事件发送上去,系统并不认为是触摸屏发送的按键,系统的 触屏震动反馈 并不起作用。这并不符合标准的android触摸设备标准。具体怎么破本人比较菜没有找到方法,大神们谁知道 求破。

    2.virtualkeys方法

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

    1. static ssize_t ft5x06_virtual_keys_show(struct kobject *kobj,       //按键的配置  
    2.                     struct kobj_attribute *attr, char *buf)  
    3. {  
    4.     return sprintf(buf,  
    5.         __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":120:1400:8:8"   //键类型:键值:按键区域中心x坐标:按键区域中心y坐标:按键区域宽:按键区域高  
    6.         ":" __stringify(EV_KEY) ":"  
    7.                     __stringify(KEY_HOME) ":360:1400:8:8"  
    8.         ":" __stringify(EV_KEY) ":"  
    9.                     __stringify(KEY_MENU) ":500:1400:8:8"  
    10.         " ");  
    11. }  
    12.   
    13. static struct kobj_attribute ft5x06_virtual_keys_attr = {  
    14.     .attr = {  
    15.         .name = "virtualkeys.Ft5x0x_Touch_Screen",  //这里的名字必须为virtualkeys.设备名字  否则系统不会识别  
    16.         .mode = S_IRUGO,  
    17.     },  
    18.     .show = &ft5x06_virtual_keys_show,  
    19. };  
    20.   
    21. static struct attribute *ft5x06_properties_attrs[] = {  
    22.     &ft5x06_virtual_keys_attr.attr,  
    23.     NULL,  
    24. };  
    25.   
    26. static struct attribute_group ft5x06_properties_attr_group = {  
    27.     .attrs = ft5x06_properties_attrs,  
    28. };  
    29.   
    30. static void ft5x06_virtual_keys_init(void)  
    31. {  
    32.     struct kobject *properties_kobj;  
    33.     int ret;  
    34.   
    35.     properties_kobj = kobject_create_and_add("board_properties", NULL);//添加目录board_properties  
    36.   
    37.     if (properties_kobj)  
    38.         ret = sysfs_create_group(properties_kobj,//生成/sys/board_properties/virtualkeys.Ft5x0x_Touch_Screen虚拟按键配置文件  
    39.             &ft5x06_properties_attr_group); //可以使用 cat /sys/board_properties/virtualkeys.Ft5x0x_Touch_Screen命令来查看配置是否正确  
    40.     if (!properties_kobj || ret)  
    41.         pr_err("failed to create board_properties ");  
    42. }  


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


    三、触摸屏驱动流程

    i2c中加入平台初始化代码

    1. static struct ft5x0x_platform_data  ft5x0x_platform_i2c_data = {  
    2.     .x_max=540,  
    3.     .y_max=960,  
    4.     .irq= SABRESD_CHARGE_FLT_1_B,<span style="white-space:pre"> </span>//中断引脚  
    5.     .reset=SABRESD_DISP0_RST_B,<span style="white-space:pre">   </span>//复位引脚  
    6. };  
    触摸屏驱动初始化
    1. static int __init ft5x0x_ts_init(void)  
    2. {  
    3.     int ret;  
    4.     ret = i2c_add_driver(&ft5x0x_ts_driver);  
    5.     if (ret) {  
    6.         printk(KERN_WARNING "Adding ft5x0x driver failed "  
    7.                "(errno = %d) ", ret);  
    8.     } else {  
    9.         pr_info("Successfully added driver %s ",         
    10.             ft5x0x_ts_driver.driver.name);  
    11.     }  
    12.     return ret;  
    13. }  

    probe函数

    1. #define VIRTUAL_LI      0  
    2. #define EVENT_LI        1  
    3. #define TOUCH_KEY       VIRTUAL_LI    
    4. static int ft5x0x_ts_probe(struct i2c_client *client,  
    5.                const struct i2c_device_id *id)  
    6. {  
    7.     。。。。。。。。。。  
    8.     ft5x0x_ts = kzalloc(sizeof(struct ft5x0x_ts_data), GFP_KERNEL);//分配参数内存  
    9.   
    10.     ..........  
    11.     i2c_set_clientdata(client, ft5x0x_ts);参数地址传给i2c 内核  
    12.   
    13.     初始化一些参数  
    14.     ft5x0x_ts->irq = client->irq;   
    15.     ft5x0x_ts->client = client;  
    16.     ft5x0x_ts->pdata = pdata;  
    17.     ft5x0x_ts->x_max = pdata->x_max - 1;  
    18.     ft5x0x_ts->y_max = pdata->y_max - 1;  
    19.     ft5x0x_ts->pdata->reset = FT5X0X_RESET_PIN;  
    20.     ft5x0x_ts->pdata->irq = ft5x0x_ts->irq;  
    21. .....................  
    22.     err = request_threaded_irq(client->irq, NULL, ft5x0x_ts_interrupt,   //注册读取数据中断  
    23.                    IRQF_TRIGGER_FALLING, client->dev.driver->name,  
    24.                    ft5x0x_ts);  
    25.     。、、、、、、、、、、、、  
    26.     input_dev = input_allocate_device();//分配设备  
    27. ...........................................  
    28.     __set_bit(EV_SYN, input_dev->evbit);  //注册设备支持event类型  
    29.     __set_bit(EV_ABS, input_dev->evbit);  
    30.     __set_bit(EV_KEY, input_dev->evbit);  
    31.     __set_bit(BTN_TOUCH, input_dev->keybit);  
    32. #if TOUCH_KEY == EVENT_LI               //如果使用event key的方法  
    33.     __set_bit(KEY_HOME, input_dev->keybit);     
    34.     __set_bit(KEY_BACK, input_dev->keybit);    
    35.     __set_bit(KEY_MENU, input_dev->keybit);  
    36. #endif    
    37.     input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS);   //设备属性  
    38.     input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR,  
    39.                  0, PRESS_MAX, 0, 0);  
    40.     input_set_abs_params(input_dev, ABS_MT_POSITION_X,  
    41.                  0, ft5x0x_ts->x_max, 0, 0);  
    42.     input_set_abs_params(input_dev, ABS_MT_POSITION_Y,  
    43.                  0, ft5x0x_ts->y_max, 0, 0);  
    44.   
    45.       
    46.   
    47.     input_dev->name ="Ft5x0x_Touch_Screen";//lijianzhang  
    48.     err = input_register_device(input_dev);         //注册这个input设备  
    49.     。。。。。。。。。。。  
    50. #if TOUCH_KEY == VIRTUAL_LI     //如果使用虚拟键盘设定  
    51.   
    52.     ft5x06_virtual_keys_init();       
    53. #endif  
    54. 。。。。。。。。。。。。。。。。。。。。。  
    55. }  


    中断处理

    1. static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)  
    2. {  
    3.     struct ft5x0x_ts_data *ft5x0x_ts = dev_id;  
    4.     int ret = 0;  
    5.     disable_irq_nosync(ft5x0x_ts->irq);  
    6.     ret = ft5x0x_read_Touchdata(ft5x0x_ts); //读取数据  
    7.     if (ret == 0)  
    8.         ft5x0x_report_value(ft5x0x_ts);//报告数据  
    9.   
    10.     enable_irq(ft5x0x_ts->irq);  
    11.   
    12.     return IRQ_HANDLED;  
    13. }  


    报告事件

    1. static void ft5x0x_report_value(struct ft5x0x_ts_data *data)  
    2. {  
    3.     struct ts_event *event = &data->event;  
    4.     int i;  
    5.     int uppoint = 0;  
    6.   
    7.     /*protocol B*/    
    8.     for (i = 0; i < event->touch_point; i++)  
    9.     {  
    10. #if TOUCH_KEY == EVENT_LI       //如果使用 key event方法  
    11.         if (event->au16_y[i]==1400)  
    12.         {  
    13.             if(event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  
    14.             {  
    15.   
    16.                 switch(event->au16_x[i])  
    17.                 {  
    18.                 case 120:  
    19.                     input_report_key(data->input_dev, KEY_BACK, 1);  
    20.                     break;  
    21.                 case 360:   
    22.                     input_report_key(data->input_dev, KEY_HOME, 1);  
    23.                     break;  
    24.                 case 500:   
    25.                     input_report_key(data->input_dev, KEY_MENU, 1);  
    26.                     break;    
    27.                 defaultbreak;  
    28.                 }  
    29.                   
    30.             }  
    31.             else  
    32.             {  
    33.   
    34.                 switch(event->au16_x[i])  
    35.                 {  
    36.                 case 120:  
    37.                     input_report_key(data->input_dev, KEY_BACK, 0);  
    38.                     break;  
    39.                 case 360:   
    40.                     input_report_key(data->input_dev, KEY_HOME, 0);  
    41.                     break;  
    42.                 case 500:   
    43.                     input_report_key(data->input_dev, KEY_MENU, 0);  
    44.                     break;    
    45.                 defaultbreak;  
    46.                 }  
    47.                 uppoint++;  
    48.                   
    49.             }  
    50.             input_sync(data->input_dev);  
    51.             return;  
    52.         }  
    53.   
    54. #endif  
    55.       
    56.         input_mt_slot(data->input_dev, event->au8_finger_id[i]);  
    57.           
    58.           
    59.         if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)  
    60.         {  
    61.             input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,  true);  
    62.             input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->au16_x[i]);     //lijianzhang  
    63.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y,event->au16_y[i]);  
    64.             input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure);  
    65.         }  
    66.         else  
    67.         {  
    68.             uppoint++;  
    69.             input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,  false);  
    70.         }  
    71.           
    72.   
    73.     }  
    74.   
    75.         if(event->touch_point == uppoint)  
    76.         {  
    77.             input_report_key(data->input_dev, BTN_TOUCH, 0);  
    78.         }  
    79.         else  
    80.         {  
    81.             input_report_key(data->input_dev, BTN_TOUCH, event->touch_point > 0);  
    82.         }  
    83.         input_sync(data->input_dev);  
    84.           
    85. }  


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

    从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文件的具体内容,仅供参考

    1. touch.deviceType = touchScreen  
    2. touch.orientationAware = 1  
    3.   
    4. touch.size.calibration = none  
    5. 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'可能代表别的意思,这个配置文件就是,做这些映射的。一般情况下这个文件也不用修改。使用默认的就可以。这个文件的访问顺序:

    /system/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
    /system/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
    /system/usr/keychars/DEVICE_NAME.kcm
    /data/system/devices/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
    /data/system/devices/keychars/Vendor_XXXX_Product_XXXX.kcm
    /data/system/devices/keychars/DEVICE_NAME.kcm
    /system/usr/keychars/Generic.kcm
    /data/system/devices/keychars/Generic.kcm
    /system/usr/keychars/Virtual.kcm
    /data/system/devices/keychars/Virtual.kcm

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

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


  • 相关阅读:
    数组的处理
    bootstrap 列的排序
    bootstrap 偏移
    mysql数据库创建、删除数据库
    Spring 加载配置文件的方式
    MyBatis DAO层传递参数到mapping.xml 几种方式
    Python 正则表达式
    Python 之定时器
    Python学习
    获取鼠标经过位置的X、Y坐标
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744637.html
Copyright © 2011-2022 走看看