***************************************************************************************************************************
作者: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()動態建立。
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核心中,需要修改代碼的]
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;#endifprintk("[FTS] ft5x0x_ts_probe, driver version is %s.\n", 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\n");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\n");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; }#endifinput_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\n",dev_name(&client->dev));goto exit_input_register_device_failed;}#ifdef CONFIG_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);#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\n", uc_reg_value); ft5x0x_read_reg(FT5X0X_REG_PERIODACTIVE, &uc_reg_value); printk("[FTS] report rate is %dHz.\n", uc_reg_value * 10); ft5x0x_read_reg(FT5X0X_REG_THGROUP, &uc_reg_value); printk("[FTS] touch threshold is %d.\n", 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\n", __FUNCTION__, err);sysfs_remove_group(&client->dev.kobj, &ft5x0x_attribute_group); } else { printk("ft5x0x:%s() - sysfs_create_group() succeeded.\n", __FUNCTION__); }printk("[FTS] ==probe over =\n"); 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 =\n");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=\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);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==\n");ret = i2c_add_driver(&ft5x0x_ts_driver);printk("ret=%d\n",ret);return ret;}static void __exit ft5x0x_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-systems.com>");MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");MODULE_LICENSE("GPL");
移植到通用的Linux的核心中,就不寫出來了,自己去研究吧。