触摸屏驱动学习并移植

一、触摸屏驱动概览

1、常用的2种触摸屏

(1)电阻触摸屏。驱动一般分2种:一种是SoC内置触摸屏控制器,一种是外置的专门触摸屏控制芯片,通过I2C接口和SoC通信。

(2)电容触摸屏。驱动只有一种,外接专用的电容式触摸屏控制芯片,I2C接口和SoC通信。

2、本专栏文章开发板:朱老师X210使用的触摸屏

(1)X210V3使用的触摸屏:ft5x06
(2)X210V3S使用的触摸屏:gslX6803

3、学习触摸屏驱动的关键点

(1)input子系统相关知识
(2)中断上下半部
(3)I2C子系统
(4)触摸屏芯片本身知识

二、gslX680驱动的移植实践

1、初步移植实验

(1)源码获取
gsl_point_id 厂商提供的预先编译好部分代码
gslX680.c
gslX680.h

  一般开发:源码是不编译进内核中的,而是编译成模块方便进行测试,随时修改代码。但这里是无法这样操作的,因为触摸屏厂商提供的代码是不完整的,有部分是提前编译好的,在编译链接的过程中会出现问题.

(2)源码加入内核中:kernel/drivers/input/touchscreen

(3)mach文件中添加board_info,从而生成一个struct i2c_client类型的变量可进行匹配(kernel/arch/arm/mach-s5pv210/mach-x210.c)

参考gslX680.c中的信息:
在这里插入图片描述
在这里插入图片描述
iic通信中,从地址取决于从设备,可通过从设备的芯片手册得到。查阅手册得到:

/********************************************************************************************/
GSL1680 支持单个 I2C 兼容的设备地址, 0x40 的。 该地址左移一位,形成的 SLA + W
或 SLA + R 的与 I2C 兼容的地址。 
/********************************************************************************************/

  上述的这个移位操作在主机代码中操作了所以我们在从设备驱动代码使用 0x40 即可。

修改后的文件:

/* I2C1 */
static struct i2c_board_info i2c_devs1[] __initdata = {
 #ifdef CONFIG_VIDEO_TV20
         {
                 I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),
        },
#endif
        {
               I2C_BOARD_INFO("gslx680", (0x40)),
        },
 
};

2、在内核配置中添加CONFIG项

(1)定义一个宏名,譬如CONFIG_TOUCHSCREEN_GSLX680

(2)在代码中使用宏来条件编译

/* I2C1 */
static struct i2c_board_info i2c_devs1[] __initdata = {
#ifdef CONFIG_VIDEO_TV20
        {
                I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),
        },
#endif
#ifdef CONFIG_TOUCHSCREEN_GSLX680
        {
                I2C_BOARD_INFO("gslx680", (0x40)),
        },
#endif
};
/* I2C2 */

(3)在Makefile中使用宏来条件配置
文件末尾添加:

obj-$(CONFIG_TOUCHSCREEN_GSLX680)       += gslX680.o gsl_point_id

(4)在Kconfig项目中添加宏的配置项
文件末尾添加:

config TOUCHSCREEN_GSLX680
         tristate "9tripod GSL1680 Touch Panel Controller"
         depends on I2C
         help
           This enables support for FocalTech over I2C based touchscreens.

(5)make menuconfig并选择Y或者N

(6)make -j4进行编译,生成镜像后去烧录启动。

3、内核编译的一般配置

/**************************内核重新配置编译步骤**************************/
make distclean        #清除之前的配置信息
ls arch/arm/configs   #查看自己需要执行的配置
make x210ii_qt_defconfig #进行大部分配置,完成主要设置
make menuconfig #对部分细节进行配置

三、gslX680驱动源码分析

1、kernel/drivers/input/touchscreen/gslX680.c

部分解析请查阅上篇文章:linux驱动之I2C子系统

static const struct i2c_device_id gsl_ts_id[] = {
	{GSLX680_I2C_NAME, 0},//该字符串用于device和driver进行匹配
	{}
};

static struct i2c_driver gsl_ts_driver = {
	.driver = {
		.name = GSLX680_I2C_NAME,
		.owner = THIS_MODULE,
	},
#ifndef CONFIG_HAS_EARLYSUSPEND
	.suspend	= gsl_ts_suspend,
	.resume	= gsl_ts_resume,
#endif
	.probe		= gsl_ts_probe,//初始化操作
	.remove		= __devexit_p(gsl_ts_remove),
	.id_table	= gsl_ts_id,
};

static int __init gsl_ts_init(void)
{
    int ret;
	print_info("==gsl_ts_init==\n");
	ret = i2c_add_driver(&gsl_ts_driver);//添加驱动
	print_info("ret=%d\n",ret);
	return ret;
}
static void __exit gsl_ts_exit(void)
{
	print_info("==gsl_ts_exit==\n");
	i2c_del_driver(&gsl_ts_driver);//删除驱动
	return;
}
struct gsl_ts {
	struct i2c_client *client;
	struct input_dev *input;
	struct work_struct work;
	struct workqueue_struct *wq;
	struct gsl_ts_data *dd;
	u8 *touch_data;//记录了触摸时的信息,包括压力,坐标等等
	u8 device_id;
	int irq;
#if defined(CONFIG_HAS_EARLYSUSPEND)
	struct early_suspend early_suspend;
#endif
};			
static int __devinit gsl_ts_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct gsl_ts *ts;
	int rc;

	print_info("GSLX680 Enter %s\n", __func__);
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		dev_err(&client->dev, "I2C functionality not supported\n");
		return -ENODEV;
	}//检查是否支持i2c功能
 
	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
	if (!ts)
		return -ENOMEM;
	print_info("==kzalloc success=\n");

	ts->client = client;
	i2c_set_clientdata(client, ts);
	ts->device_id = id->driver_data;

	rc = gslX680_ts_init(client, ts);
	if (rc < 0) {
		dev_err(&client->dev, "GSLX680 init failed\n");
		goto error_mutex_destroy;
	}	

	gsl_client = client;
	
	gslX680_init();
	init_chip(ts->client);
	check_mem_data(ts->client);
	
	rc=  request_irq(client->irq, gsl_ts_irq, IRQF_TRIGGER_RISING, client->name, ts);//申请中断号
	if (rc < 0) {
		print_info( "gsl_probe: request irq failed\n");
		goto error_req_irq_fail;
	}

	/* create debug attribute */
	//rc = device_create_file(&ts->input->dev, &dev_attr_debug_enable);

#ifdef CONFIG_HAS_EARLYSUSPEND
	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	//ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;
	ts->early_suspend.suspend = gsl_ts_early_suspend;
	ts->early_suspend.resume = gsl_ts_late_resume;
	register_early_suspend(&ts->early_suspend);
#endif


#ifdef GSL_MONITOR
	print_info( "gsl_ts_probe () : queue gsl_monitor_workqueue\n");

	INIT_DELAYED_WORK(&gsl_monitor_work, gsl_monitor_worker);
	gsl_monitor_workqueue = create_singlethread_workqueue("gsl_monitor_workqueue");
	queue_delayed_work(gsl_monitor_workqueue, &gsl_monitor_work, 1000);
#endif

	print_info("[GSLX680] End %s\n", __func__);

	return 0;

//exit_set_irq_mode:	
error_req_irq_fail:
    free_irq(ts->irq, ts);	

error_mutex_destroy:
	input_free_device(ts->input);
	kfree(ts);
	return rc;
}

  驱动内基本不定义结构体变量,都是定义结构体指针,然后kzalloc()申请内存,避免栈溢出了

static int gslX680_ts_init(struct i2c_client *client, struct gsl_ts *ts)
{
	struct input_dev *input_device;
	int rc = 0;
	
	printk("[GSLX680] Enter %s\n", __func__);

	ts->dd = &devices[ts->device_id];

	if (ts->device_id == 0) {
		ts->dd->data_size = MAX_FINGERS * ts->dd->touch_bytes + ts->dd->touch_meta_data;
		ts->dd->touch_index = 0;
	}

	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
	if (!ts->touch_data) {
		pr_err("%s: Unable to allocate memory\n", __func__);
		return -ENOMEM;
	}

	input_device = input_allocate_device();
	if (!input_device) {
		rc = -ENOMEM;
		goto error_alloc_dev;
	}

	ts->input = input_device;
	input_device->name = GSLX680_I2C_NAME;
	input_device->id.bustype = BUS_I2C;
	input_device->dev.parent = &client->dev;
	input_set_drvdata(input_device, ts);

	set_bit(EV_ABS, input_device->evbit);

	set_bit(BTN_TOUCH, input_device->keybit);//触摸屏本身就是一个按键
	set_bit(EV_ABS, input_device->evbit);
	set_bit(EV_KEY, input_device->evbit);
	input_set_abs_params(input_device, ABS_X, 0, SCREEN_MAX_X, 0, 0);
	input_set_abs_params(input_device, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
	input_set_abs_params(input_device, ABS_PRESSURE, 0, 1, 0, 0);
#ifdef HAVE_TOUCH_KEY
	input_device->evbit[0] = BIT_MASK(EV_KEY);
	//input_device->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	for (i = 0; i < MAX_KEY_NUM; i++)
		set_bit(key_array[i], input_device->keybit);
#endif
	
	client->irq = IRQ_PORT;//外部中断对应的端口号
	ts->irq = client->irq;

	ts->wq = create_singlethread_workqueue("kworkqueue_ts");
	if (!ts->wq) {
		dev_err(&client->dev, "Could not create workqueue\n");
		goto error_wq_create;
	}
	flush_workqueue(ts->wq);	

	INIT_WORK(&ts->work, gslX680_ts_worker);

	rc = input_register_device(input_device);
	if (rc)
		goto error_unreg_device;

	return 0;

error_unreg_device:
	destroy_workqueue(ts->wq);
error_wq_create:
	input_free_device(input_device);
error_alloc_dev:
	kfree(ts->touch_data);
	return rc;
}

在这里插入图片描述
  mt表示支持多点触摸

  对于该部分代码具体操做设置请仔细阅读触摸屏读数据手册,但有些内容我们是无法探究清楚的,只有专业搞触摸屏驱动的那些原厂工程师清楚。

注:本资料大部分由朱老师物联网大讲堂课程笔记整理而来并且引用了部分他人博客的内容,如有侵权,联系删除!水平有限,如有错误,欢迎各位在评论区交流。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值