您的位置:首页 > 运维架构 > Linux

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

2015-07-02 15:53 639 查看
***************************************************************************************************************************

作者: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?

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
};

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?

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.\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;
}
#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\n",
dev_name(&client->dev));
goto exit_input_register_device_failed;
}

#ifdef CONFIG_HAS_EARLYSUSPEND
printk("==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");

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.\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;
    }
#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\n",
		dev_name(&client->dev));
		goto exit_input_register_device_failed;
	}

#ifdef CONFIG_HAS_EARLYSUSPEND
	printk("==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的内核中,就不写出来了,自己去研究吧。



版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: