***************************************************************************************************************************
作者:EasyWave 时间:.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?structi2c_board_info{chartype[I2C_NAME_SIZE];unsignedshortflags;unsignedshortaddr;void*platform_data;structdev_archdata*archdata;intirq;};staticstructi2c_board_infoi2c_devs0[]__initdata={#ifdefCONFIG_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?staticintft5x0x_ts_probe(structi2c_client*client,conststructi2c_device_id*id){structft5x0x_ts_data*ft5x0x_ts;structinput_dev*input_dev;interr=0;unsignedcharuc_reg_value;#ifCFG_SUPPORT_TOUCH_KEYinti;#endifprintk("[FTS]ft5x0x_ts_probe,driverversionis%s.\n",CFG_FTS_CTP_DRIVER_VERSION);if(!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)){err=-ENODEV;gotoexit_check_functionality_failed;}ft5x0x_ts=kzalloc(sizeof(structft5x0x_ts_data),GFP_KERNEL);//ft5x0x_ts=kmalloc(sizeof(structft5x0x_ts_data),GFP_KERNEL);if(!ft5x0x_ts){err=-ENOMEM;gotoexit_alloc_data_failed;}//memset(ft5x0x_ts,0,sizeof(structft5x0x_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;gotoexit_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:requestirqfailed\n");gotoexit_irq_request_failed;}disable_irq(IRQ_EINT(6));input_dev=input_allocate_device();if(!input_dev){err=-ENOMEM;dev_err(&client->dev,"failedtoallocateinputdevice\n");gotoexit_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);#ifCFG_SUPPORT_TOUCH_KEY//setupkeycodeareaset_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;}#endifinput_dev->name=FT5X0X_NAME;//dev_name(&client->dev)err=input_register_device(input_dev);if(err){dev_err(&client->dev,"ft5x0x_ts_probe:failedtoregisterinputdevice:%s\n",dev_name(&client->dev));gotoexit_input_register_device_failed;}#ifdefCONFIG_HAS_EARLYSUSPENDprintk("==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);#endifmsleep(150);//makesureCTPalreadyfinishstartupprocess//getsomeregisterinformationuc_reg_value=ft5x0x_read_fw_ver();printk("[FTS]Firmwareversion=0x%x\n",uc_reg_value);ft5x0x_read_reg(FT5X0X_REG_PERIODACTIVE,&uc_reg_value);printk("[FTS]reportrateis%dHz.\n",uc_reg_value*10);ft5x0x_read_reg(FT5X0X_REG_THGROUP,&uc_reg_value);printk("[FTS]touchthresholdis%d.\n",uc_reg_value*4);#ifCFG_SUPPORT_AUTO_UPGfts_ctpm_auto_upg();#endif#ifCFG_SUPPORT_UPDATE_PROJECT_SETTINGfts_ctpm_update_project_setting();#endifenable_irq(IRQ_EINT(6));//createsysfserr=sysfs_create_group(&client->dev.kobj,&ft5x0x_attribute_group);if(0!=err){dev_err(&client->dev,"%s()-ERROR:sysfs_create_group()failed:%d\n",__FUNCTION__,err);sysfs_remove_group(&client->dev.kobj,&ft5x0x_attribute_group);}else{printk("ft5x0x:%s()-sysfs_create_group()succeeded.\n",__FUNCTION__);}printk("[FTS]==probeover=\n");return0;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("==singlethreaderror=\n");i2c_set_clientdata(client,NULL);kfree(ft5x0x_ts);exit_alloc_data_failed:exit_check_functionality_failed:returnerr;}staticint__devexitft5x0x_ts_remove(structi2c_client*client){structft5x0x_ts_data*ft5x0x_ts;printk("==ft5x0x_ts_remove=\n");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);return0;}staticconststructi2c_device_idft5x0x_ts_id[]={{FT5X0X_NAME,0x3x},{}};MODULE_DEVICE_TABLE(i2c,ft5x0x_ts_id);staticstructi2c_driverft5x0x_ts_driver={.probe=ft5x0x_ts_probe,.remove=__devexit_p(ft5x0x_ts_remove),.id_table=ft5x0x_ts_id,.driver={.name=FT5X0X_NAME,.owner=THIS_MODULE,},};staticint__initft5x0x_ts_init(void){intret;printk("==ft5x0x_ts_init==\n");ret=i2c_add_driver(&ft5x0x_ts_driver);printk("ret=%d\n",ret);returnret;}staticvoid__exitft5x0x_ts_exit(void){printk("==ft5x0x_ts_exit==\n");i2c_del_driver(&ft5x0x_ts_driver);}module_init(ft5x0x_ts_init);module_exit(ft5x0x_ts_exit);MODULE_AUTHOR("<wenfs@Focaltech->");MODULE_DESCRIPTION("FocalTechft5x0xTouchScreendriver");MODULE_LICENSE("GPL");
移植到通用的Linux的内核中,就不写出来了,自己去研究吧。