android4.0触摸屏(touchscreen)以及屏幕按键驱动解析 | 少将全栈
  • 欢迎访问少将全栈,学会感恩,乐于付出,珍惜缘份,成就彼此、推荐使用最新版火狐浏览器和Chrome浏览器访问本网站。
  • 吐槽,投稿,删稿,交个朋友
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏少将全栈吧

android4.0触摸屏(touchscreen)以及屏幕按键驱动解析

点滴 admin 11年前 (2014-04-15) 1865次浏览 已收录 扫描二维码

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

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

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

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

二、触摸按键部分

对于触摸按键的发送可以分为两种方法,一是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. staticvoidft5x0x_report_value(structft5x0x_ts_data*data)
  2. {
  3. structts_event*event=data-event;
  4. inti;
  5. for(i=0;ievent-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. switch(event-au16_x[i])
  12. {
  13. case120:
  14. input_report_key(data-input_dev,KEY_BACK,1);
  15. break;
  16. case360:
  17. input_report_key(data-input_dev,KEY_HOME,1);
  18. break;
  19. case500:
  20. input_report_key(data-input_dev,KEY_MENU,1);
  21. break;
  22. default:break;
  23. }
  24. }
  25. else
  26. {
  27. switch(event-au16_x[i])
  28. {
  29. case120:
  30. input_report_key(data-input_dev,KEY_BACK,0);
  31. break;
  32. case360:
  33. input_report_key(data-input_dev,KEY_HOME,0);
  34. break;
  35. case500:
  36. input_report_key(data-input_dev,KEY_MENU,0);
  37. break;
  38. default:break;
  39. }
  40. }
  41. input_sync(data-input_dev);
  42. return;
  43. }
  44. }

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

2.virtualkeys方法

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

  1. staticssize_tft5x06_virtual_keys_show(structkobject*kobj,//按键的配置
  2. structkobj_attribute*attr,char*buf)
  3. {
  4. returnsprintf(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. staticstructkobj_attributeft5x06_virtual_keys_attr={
  13. .attr={
  14. .name=“virtualkeys.Ft5x0x_Touch_Screen”,//这里的名字必须为virtualkeys.设备名字否则系统不会识别
  15. .mode=S_IRUGO,
  16. },
  17. .show=ft5x06_virtual_keys_show,
  18. };
  19. staticstructattribute*ft5x06_properties_attrs[]={
  20. ft5x06_virtual_keys_attr.attr,
  21. NULL,
  22. };
  23. staticstructattribute_groupft5x06_properties_attr_group={
  24. .attrs=ft5x06_properties_attrs,
  25. };
  26. staticvoidft5x06_virtual_keys_init(void)
  27. {
  28. structkobject*properties_kobj;
  29. intret;
  30. properties_kobj=kobject_create_and_add(“board_properties”,NULL);//添加目录board_properties
  31. if(properties_kobj)
  32. ret=sysfs_create_group(properties_kobj,//生成/sys/board_properties/virtualkeys.Ft5x0x_Touch_Screen虚拟按键配置文件
  33. ft5x06_properties_attr_group);//可以使用cat/sys/board_properties/virtualkeys.Ft5x0x_Touch_Screen命令来查看配置是否正确
  34. if(!properties_kobj||ret)
  35. pr_err(“failedtocreateboard_properties
    );
  36. }

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

三、触摸屏驱动流程

i2c中加入平台初始化代码

  1. staticstructft5x0x_platform_dataft5x0x_platform_i2c_data={
  2. .x_max=540,
  3. .y_max=960,
  4. .irq=SABRESD_CHARGE_FLT_1_B,spanstyle=“white-space:pre”/span//中断引脚
  5. .reset=SABRESD_DISP0_RST_B,spanstyle=“white-space:pre”/span//复位引脚
  6. };

触摸屏驱动初始化

  1. staticint__initft5x0x_ts_init(void)
  2. {
  3. intret;
  4. ret=i2c_add_driver(ft5x0x_ts_driver);
  5. if(ret){
  6. printk(KERN_WARNING“Addingft5x0xdriverfailed”
  7. “(errno=%d)
    ,ret);
  8. }else{
  9. pr_info(“Successfullyaddeddriver%s
    ,
  10. ft5x0x_ts_driver.driver.name);
  11. }
  12. returnret;
  13. }

probe函数

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

中断处理

  1. staticirqreturn_tft5x0x_ts_interrupt(intirq,void*dev_id)
  2. {
  3. structft5x0x_ts_data*ft5x0x_ts=dev_id;
  4. intret=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. enable_irq(ft5x0x_ts-irq);
  10. returnIRQ_HANDLED;
  11. }

报告事件

  1. staticvoidft5x0x_report_value(structft5x0x_ts_data*data)
  2. {
  3. structts_event*event=data-event;
  4. inti;
  5. intuppoint=0;
  6. /*protocolB*/
  7. for(i=0;ievent-touch_point;i++)
  8. {
  9. #ifTOUCH_KEY==EVENT_LI//如果使用keyevent方法
  10. if(event-au16_y[i]==1400)
  11. {
  12. if(event-au8_touch_event[i]==0||event-au8_touch_event[i]==2)
  13. {
  14. switch(event-au16_x[i])
  15. {
  16. case120:
  17. input_report_key(data-input_dev,KEY_BACK,1);
  18. break;
  19. case360:
  20. input_report_key(data-input_dev,KEY_HOME,1);
  21. break;
  22. case500:
  23. input_report_key(data-input_dev,KEY_MENU,1);
  24. break;
  25. default:break;
  26. }
  27. }
  28. else
  29. {
  30. switch(event-au16_x[i])
  31. {
  32. case120:
  33. input_report_key(data-input_dev,KEY_BACK,0);
  34. break;
  35. case360:
  36. input_report_key(data-input_dev,KEY_HOME,0);
  37. break;
  38. case500:
  39. input_report_key(data-input_dev,KEY_MENU,0);
  40. break;
  41. default:break;
  42. }
  43. uppoint++;
  44. }
  45. input_sync(data-input_dev);
  46. return;
  47. }
  48. #endif
  49. input_mt_slot(data-input_dev,event-au8_finger_id[i]);
  50. if(event-au8_touch_event[i]==0||event-au8_touch_event[i]==2)
  51. {
  52. input_mt_report_slot_state(data-input_dev,MT_TOOL_FINGER,true);
  53. input_report_abs(data-input_dev,ABS_MT_POSITION_X,event-au16_x[i]);//lijianzhang
  54. input_report_abs(data-input_dev,ABS_MT_POSITION_Y,event-au16_y[i]);
  55. input_report_abs(data-input_dev,ABS_MT_TOUCH_MAJOR,event-pressure);
  56. }
  57. else
  58. {
  59. uppoint++;
  60. input_mt_report_slot_state(data-input_dev,MT_TOOL_FINGER,false);
  61. }
  62. }
  63. if(event-touch_point==uppoint)
  64. {
  65. input_report_key(data-input_dev,BTN_TOUCH,0);
  66. }
  67. else
  68. {
  69. input_report_key(data-input_dev,BTN_TOUCH,event-touch_point0);
  70. }
  71. input_sync(data-input_dev);
  72. }

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

从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. touch.size.calibration=none
  4. 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事件,当我们写完驱动以后,可以用这个命令将发送的事件打印出来,看驱动写的是否正确。

喜欢 (0)
[🍬谢谢你请我吃糖果🍬🍬~]
分享 (0)
关于作者:
少将,关注Web全栈开发、项目管理,持续不断的学习、努力成为一个更棒的开发,做最好的自己,让世界因你不同。