基于FT5x06嵌入式Linux电容触摸屏驱动

2019-07-13 00:17发布

***************************************************************************************************************************
作者:EasyWave                                                                                 时间:2013.02.06
类别:Linux 内核驱动源码分析                                                      声明:转载,请保留链接 注意:如有错误,欢迎指正。这些是我学习的日志文章...... *************************************************************************************************************************** 一:FT5X06电容触摸IC简介      FT5x06系列ICs是单芯片电容式触摸屏控制器IC,带有一个内置的8位微控制器单元(MCU)。采用互电容的方法,在配合的相互的电容式触摸面板,它支持真正的多点触摸功能。FT5x06具有用户友好的输入的功能,这可以应用在许多便携式设备,例如蜂窝式电话,移动互联网设备,上网本和笔记本个人电脑。FT5x06系列IC包括FT5206/FT5306/FT5406。具体的功能如下图所示:   二:硬件接口设计 从FT5X06的datasheet中,我们可以看到,FT5X06既可以工作的SPI的接口方式,也可以工作在I2C的接口方式,不管工作在SPI,还是工作在I2C,从硬件的接口设计上来说,这下面的几个控制口,都是需要要接的。如下图所示: 1):INT引脚,这个脚是一个中端信号,它用来通知HOST端FT5X06已经准备好,可以进行读操作了。 2):WAKE引脚:这个功能主要的作用是将FT5X06从睡眠状态转换到工作状态。 3):/RST引脚:FT5X06的芯片复位信号。 如何来设计硬件接口呢,这个我们可以从FT5X06的datasheet看出来,首先我们来看下FT5X06的上电时序,如下图所示: FT5X06的上电时序 FT5X06的RESET时序 FT5X06的wakeup时序 各自的最小的时间限制如下所所示: 因此,从上面的图片和表格中,我们可以看出,在poweron中,必须确保在上电后,wakeup的电平为高电平,至于INT信号,只要确保无INT信号时,这个INT为高即可,这个可以从poweron的时序可以看出,它是一个低电平的动作,这个是驱动中来做的事情了。 接下来就是确定I2C的从地址,如下图所示:[以下引用自网络] 从地址高位必须为:3,低位必须根据i2ccon设定的值来确定。 根据FT5406数据手册上的指令,我们先了解下驱动如何实现电容屏的多点触摸,其实很简单,主要需要触摸屏IC FT5406 能够捕获多点数据,这点电容屏基本多能支持到捕获2点以上,而FT5406 可以捕获5个触摸点,编写驱动时,只要去获取这几个点的数据,然后上报就可以了。如下图所示:[以下引用自网络] 02H :捕获的触摸点个数              03H- 1EH :对应每个点的x,y坐标数值。   三:Linux驱动源码         I2C的驱动需要根据具体的ARM芯片,一般来说,IC原厂,一般会将在linux的bsp中都会有I2C的驱动,这个部分不需要我们去写的,我们只需要将FT5X06和BSP包中的I2C驱动匹配起来就好了。而整个FT5X06也是这样做的。在linu内核中关于i2c的一般会有这个函数:i2c_board_info()i2c_board_info用于构建信息表来列出存在的I2C设备。这一信息用于增长新型I2C驱动的驱动模型树。对于主板,它使用i2c_register_board_info()来静态创建。对于子板,利用已知的适配器使用i2c_new_device()动态创建。 [cpp] view plaincopyprint?
  1. struct i2c_board_info {  
  2.     char type[I2C_NAME_SIZE];    
  3.     unsigned short flags;    
  4.     unsigned short addr;    
  5.     void *platform_data;    
  6.     struct dev_archdata *archdata;    
  7.     int irq;    
  8. };  
  9. static struct i2c_board_info i2c_devs0[] __initdata = {  
  10. #ifdef CONFIG_TOUCHSCREEN_CDTLCD  
  11. {  
  12. I2C_BOARD_INFO("ft5x0x_ts", 0x3x),  
  13. .irq = IRQ_EINT1,  
  14. },  
  15. #endif   
  16. };  
struct i2c_board_info { char type[I2C_NAME_SIZE]; unsigned short flags; unsigned short addr; void *platform_data; struct dev_archdata *archdata; int irq; }; static struct i2c_board_info i2c_devs0[] __initdata = { #ifdef CONFIG_TOUCHSCREEN_CDTLCD { I2C_BOARD_INFO("ft5x0x_ts", 0x3x), .irq = IRQ_EINT1, }, #endif };
最后在内核初始化的部分调用int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len);函数即可。如下详细的解释:
@busnum: 指定这些设备属于哪个总线
@info: I2C设备描述符向量
@len: 向量中描述符的数量;为了预留特定的总线号,可以是0。
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
下面贴出部分代码:[下面的代码是Android下的Linux驱动,如果要修改到通用的Linux内核中,需要修改代码的] [cpp] view plaincopyprint?
  1. static int   
  2. ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)  
  3. {  
  4.     struct ft5x0x_ts_data *ft5x0x_ts;  
  5.     struct input_dev *input_dev;  
  6.     int err = 0;  
  7.     unsigned char uc_reg_value;   
  8. #if CFG_SUPPORT_TOUCH_KEY  
  9.     int i;  
  10. #endif  
  11.       
  12.     printk("[FTS] ft5x0x_ts_probe, driver version is %s. ", CFG_FTS_CTP_DRIVER_VERSION);  
  13.       
  14.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {  
  15.         err = -ENODEV;  
  16.         goto exit_check_functionality_failed;  
  17.     }  
  18.   
  19.     ft5x0x_ts = kzalloc(sizeof(struct ft5x0x_ts_data), GFP_KERNEL);  
  20.     //ft5x0x_ts = kmalloc(sizeof(struct ft5x0x_ts_data), GFP_KERNEL);  
  21.     if (!ft5x0x_ts) {  
  22.         err = -ENOMEM;  
  23.         goto exit_alloc_data_failed;  
  24.     }  
  25.     //memset(ft5x0x_ts, 0, sizeof(struct ft5x0x_ts_data));  
  26.   
  27.     this_client = client;  
  28.     i2c_set_clientdata(client, ft5x0x_ts);  
  29.   
  30.     mutex_init(&ft5x0x_ts->device_mode_mutex);  
  31.     INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);  
  32.   
  33.     ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));  
  34.     if (!ft5x0x_ts->ts_workqueue) {  
  35.         err = -ESRCH;  
  36.         goto exit_create_singlethread;  
  37.     }  
  38.   
  39.   
  40.     err = request_irq(IRQ_EINT(6), ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING, "ft5x0x_ts", ft5x0x_ts);  
  41.     if (err < 0) {  
  42.         dev_err(&client->dev, "ft5x0x_probe: request irq failed ");  
  43.         goto exit_irq_request_failed;  
  44.     }  
  45.   
  46.   
  47.     disable_irq(IRQ_EINT(6));  
  48.   
  49.     input_dev = input_allocate_device();  
  50.     if (!input_dev) {  
  51.         err = -ENOMEM;  
  52.         dev_err(&client->dev, "failed to allocate input device ");  
  53.         goto exit_input_dev_alloc_failed;  
  54.     }  
  55.       
  56.     ft5x0x_ts->input_dev = input_dev;  
  57.   
  58.     set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);  
  59.     set_bit(ABS_MT_POSITION_X, input_dev->absbit);  
  60.     set_bit(ABS_MT_POSITION_Y, input_dev->absbit);  
  61.     set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);  
  62.   
  63.     input_set_abs_params(input_dev,  
  64.                  ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);  
  65.     input_set_abs_params(input_dev,  
  66.                  ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);  
  67.     input_set_abs_params(input_dev,  
  68.                  ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);  
  69.     input_set_abs_params(input_dev,  
  70.                  ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);  
  71.     input_set_abs_params(input_dev,  
  72.                  ABS_MT_TRACKING_ID, 0, 5, 0, 0);  
  73.   
  74.   
  75.     set_bit(EV_KEY, input_dev->evbit);  
  76.     set_bit(EV_ABS, input_dev->evbit);  
  77.   
  78. #if CFG_SUPPORT_TOUCH_KEY  
  79.     //setup key code area  
  80.     set_bit(EV_SYN, input_dev->evbit);  
  81.     set_bit(BTN_TOUCH, input_dev->keybit);  
  82.     input_dev->keycode = tsp_keycodes;  
  83.     for(i = 0; i < CFG_NUMOFKEYS; i++)  
  84.     {  
  85.         input_set_capability(input_dev, EV_KEY, ((int*)input_dev->keycode)[i]);  
  86.         tsp_keystatus[i] = KEY_RELEASE;  
  87.     }  
  88. #endif  
  89.   
  90.     input_dev->name      = FT5X0X_NAME;      //dev_name(&client->dev)  
  91.     err = input_register_device(input_dev);  
  92.     if (err) {  
  93.         dev_err(&client->dev,  
  94.         "ft5x0x_ts_probe: failed to register input device: %s ",  
  95.         dev_name(&client->dev));  
  96.         goto exit_input_register_device_failed;  
  97.     }  
  98.   
  99. #ifdef CONFIG_HAS_EARLYSUSPEND  
  100.     printk("==register_early_suspend = ");  
  101.     ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;  
  102.     ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;  
  103.     ft5x0x_ts->early_suspend.resume  = ft5x0x_ts_resume;  
  104.     register_early_suspend(&ft5x0x_ts->early_suspend);  
  105. #endif  
  106.   
  107.     msleep(150);  //make sure CTP already finish startup process  
  108.       
  109.     //get some register information  
  110.     uc_reg_value = ft5x0x_read_fw_ver();  
  111.     printk("[FTS] Firmware version = 0x%x ", uc_reg_value);  
  112.     ft5x0x_read_reg(FT5X0X_REG_PERIODACTIVE, &uc_reg_value);  
  113.     printk("[FTS] report rate is %dHz. ", uc_reg_value * 10);  
  114.     ft5x0x_read_reg(FT5X0X_REG_THGROUP, &uc_reg_value);  
  115.     printk("[FTS] touch threshold is %d. ", uc_reg_value * 4);  
  116.   
  117. #if CFG_SUPPORT_AUTO_UPG  
  118.     fts_ctpm_auto_upg();  
  119. #endif      
  120.   
  121. #if CFG_SUPPORT_UPDATE_PROJECT_SETTING  
  122.     fts_ctpm_update_project_setting();  
  123. #endif  
  124.   
  125.     enable_irq(IRQ_EINT(6));  
  126.    //create sysfs  
  127.    err = sysfs_create_group(&client->dev.kobj, &ft5x0x_attribute_group);  
  128.    if (0 != err)  
  129.   {  
  130.     dev_err(&client->dev, "%s() - ERROR: sysfs_create_group() failed: %d ", __FUNCTION__, err);  
  131.     sysfs_remove_group(&client->dev.kobj, &ft5x0x_attribute_group);  
  132.   }  
  133.    else  
  134.     {  
  135.         printk("ft5x0x:%s() - sysfs_create_group() succeeded. ", __FUNCTION__);  
  136.     }  
  137.   
  138.     printk("[FTS] ==probe over = ");  
  139.     return 0;  
  140.   
  141. exit_input_register_device_failed:  
  142.     input_free_device(input_dev);  
  143. exit_input_dev_alloc_failed:  
  144. //  free_irq(client->irq, ft5x0x_ts);  
  145.     free_irq(IRQ_EINT(6), ft5x0x_ts);  
  146. exit_irq_request_failed:  
  147. //exit_platform_data_null:  
  148.     cancel_work_sync(&ft5x0x_ts->pen_event_work);  
  149.     destroy_workqueue(ft5x0x_ts->ts_workqueue);  
  150. exit_create_singlethread:  
  151.     printk("==singlethread error = ");  
  152.     i2c_set_clientdata(client, NULL);  
  153.     kfree(ft5x0x_ts);  
  154. exit_alloc_data_failed:  
  155. exit_check_functionality_failed:  
  156.     return err;  
  157. }  
  158. static int __devexit ft5x0x_ts_remove(struct i2c_client *client)  
  159. {  
  160.     struct ft5x0x_ts_data *ft5x0x_ts;  
  161.     printk("==ft5x0x_ts_remove= ");  
  162.     ft5x0x_ts = i2c_get_clientdata(client);  
  163.     unregister_early_suspend(&ft5x0x_ts->early_suspend);  
  164. //  free_irq(client->irq, ft5x0x_ts);  
  165.     mutex_destroy(&ft5x0x_ts->device_mode_mutex);  
  166.     free_irq(IRQ_EINT(6), ft5x0x_ts);  
  167.     input_unregister_device(ft5x0x_ts->input_dev);  
  168.     kfree(ft5x0x_ts);  
  169.     cancel_work_sync(&ft5x0x_ts->pen_event_work);  
  170.     destroy_workqueue(ft5x0x_ts->ts_workqueue);  
  171.     i2c_set_clientdata(client, NULL);   
  172.     del_timer(&test_timer);  
  173.     return 0;  
  174. }  
  175.   
  176. static const struct i2c_device_id ft5x0x_ts_id[] = {  
  177.     { FT5X0X_NAME, 0x3x },{ }  
  178. };  
  179.   
  180.   
  181. MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);  
  182.   
  183. static struct i2c_driver ft5x0x_ts_driver = {  
  184.     .probe      = ft5x0x_ts_probe,  
  185.     .remove     = __devexit_p(ft5x0x_ts_remove),  
  186.     .id_table   = ft5x0x_ts_id,  
  187.     .driver = {  
  188.         .name   = FT5X0X_NAME,  
  189.         .owner  = THIS_MODULE,  
  190.     },  
  191. };  
  192.   
  193. static int __init ft5x0x_ts_init(void)  
  194. {  
  195.     int ret;  
  196.     printk("==ft5x0x_ts_init== ");  
  197.     ret = i2c_add_driver(&ft5x0x_ts_driver);  
  198.     printk("ret=%d ",ret);  
  199.     return ret;  
  200. }  
  201.   
  202. static void __exit ft5x0x_ts_exit(void)  
  203. {  
  204.     printk("==ft5x0x_ts_exit== ");  
  205.     i2c_del_driver(&ft5x0x_ts_driver);  
  206. }  
  207.   
  208. module_init(ft5x0x_ts_init);  
  209. module_exit(ft5x0x_ts_exit);  
  210.   
  211. MODULE_AUTHOR("");  
  212. MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");  
  213. MODULE_LICENSE("GPL");  
static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ft5x0x_ts_data *ft5x0x_ts; struct input_dev *input_dev; int err = 0; unsigned char uc_reg_value; #if CFG_SUPPORT_TOUCH_KEY int i; #endif printk("[FTS] ft5x0x_ts_probe, driver version is %s. ", CFG_FTS_CTP_DRIVER_VERSION); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { err = -ENODEV; goto exit_check_functionality_failed; } 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; } //memset(ft5x0x_ts, 0, sizeof(struct ft5x0x_ts_data)); this_client = client; i2c_set_clientdata(client, ft5x0x_ts); mutex_init(&ft5x0x_ts->device_mode_mutex); INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work); ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev)); if (!ft5x0x_ts->ts_workqueue) { err = -ESRCH; goto exit_create_singlethread; } err = request_irq(IRQ_EINT(6), ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING, "ft5x0x_ts", ft5x0x_ts); if (err < 0) { dev_err(&client->dev, "ft5x0x_probe: request irq failed "); goto exit_irq_request_failed; } disable_irq(IRQ_EINT(6)); input_dev = input_allocate_device(); if (!input_dev) { err = -ENOMEM; dev_err(&client->dev, "failed to allocate input device "); goto exit_input_dev_alloc_failed; } ft5x0x_ts->input_dev = input_dev; 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 key code area set_bit(EV_SYN, input_dev->evbit); set_bit(BTN_TOUCH, input_dev->keybit); input_dev->keycode = tsp_keycodes; for(i = 0; i < CFG_NUMOFKEYS; i++) { input_set_capability(input_dev, EV_KEY, ((int*)input_dev->keycode)[i]); tsp_keystatus[i] = KEY_RELEASE; } #endif input_dev->name = FT5X0X_NAME; //dev_name(&client->dev) err = input_register_device(input_dev); if (err) { dev_err(&client->dev, "ft5x0x_ts_probe: failed to register input device: %s ", dev_name(&client->dev)); goto exit_input_register_device_failed; } #ifdef CONFIG_HAS_EARLYSUSPEND printk("==register_early_suspend = "); 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 msleep(150); //make sure CTP already finish startup process //get some register information uc_reg_value = ft5x0x_read_fw_ver(); printk("[FTS] Firmware version = 0x%x ", uc_reg_value); ft5x0x_read_reg(FT5X0X_REG_PERIODACTIVE, &uc_reg_value); printk("[FTS] report rate is %dHz. ", uc_reg_value * 10); ft5x0x_read_reg(FT5X0X_REG_THGROUP, &uc_reg_value); printk("[FTS] touch threshold is %d. ", uc_reg_value * 4); #if CFG_SUPPORT_AUTO_UPG fts_ctpm_auto_upg(); #endif #if CFG_SUPPORT_UPDATE_PROJECT_SETTING fts_ctpm_update_project_setting(); #endif enable_irq(IRQ_EINT(6)); //create sysfs err = sysfs_create_group(&client->dev.kobj, &ft5x0x_attribute_group); if (0 != err) { dev_err(&client->dev, "%s() - ERROR: sysfs_create_group() failed: %d ", __FUNCTION__, err); sysfs_remove_group(&client->dev.kobj, &ft5x0x_attribute_group); } else { printk("ft5x0x:%s() - sysfs_create_group() succeeded. ", __FUNCTION__); } printk("[FTS] ==probe over = "); return 0; exit_input_register_device_failed: input_free_device(input_dev); exit_input_dev_alloc_failed: // free_irq(client->irq, ft5x0x_ts); free_irq(IRQ_EINT(6), ft5x0x_ts); exit_irq_request_failed: //exit_platform_data_null: cancel_work_sync(&ft5x0x_ts->pen_event_work); destroy_workqueue(ft5x0x_ts->ts_workqueue); exit_create_singlethread: printk("==singlethread error = "); i2c_set_clientdata(client, NULL); kfree(ft5x0x_ts); exit_alloc_data_failed: exit_check_functionality_failed: return err; } static int __devexit ft5x0x_ts_remove(struct i2c_client *client) { struct ft5x0x_ts_data *ft5x0x_ts; printk("==ft5x0x_ts_remove= "); ft5x0x_ts = i2c_get_clientdata(client); unregister_early_suspend(&ft5x0x_ts->early_suspend); // free_irq(client->irq, ft5x0x_ts); mutex_destroy(&ft5x0x_ts->device_mode_mutex); free_irq(IRQ_EINT(6), ft5x0x_ts); input_unregister_device(ft5x0x_ts->input_dev); kfree(ft5x0x_ts); cancel_work_sync(&ft5x0x_ts->pen_event_work); destroy_workqueue(ft5x0x_ts->ts_workqueue); i2c_set_clientdata(client, NULL); del_timer(&test_timer); return 0; } static const struct i2c_device_id ft5x0x_ts_id[] = { { FT5X0X_NAME, 0x3x },{ } }; MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id); 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, }, }; static int __init ft5x0x_ts_init(void) { int ret; printk("==ft5x0x_ts_init== "); ret = i2c_add_driver(&ft5x0x_ts_driver); printk("ret=%d ",ret); return ret; } static void __exit ft5x0x_ts_exit(void) { printk("==ft5x0x_ts_exit== "); i2c_del_driver(&ft5x0x_ts_driver); } module_init(ft5x0x_ts_init); module_exit(ft5x0x_ts_exit); MODULE_AUTHOR(""); MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver"); MODULE_LICENSE("GPL");
移植到通用的Linux的内核中,就不写出来了,自己去研究吧。   版权声明:本文为博主原创文章,未经博主允许不得转载。