日期:2014-05-16 浏览次数:20993 次
这是几个月以前的东西了,在彻底遗忘之前拿出来好好写写。做个笔记,也算是造福后来人了。在做这个项目之前,没有做过电容屏的驱动,印象中的电容触摸屏是不需要校正的。IC支持多大的屏就要配多大的屏。但是拿到需求,发现要用FT5406做10寸屏,可是FT5406手册上明明写了,最大支持到8.9寸。由于经验不足,感到略懵。就去核实这个需求方案是不是搞错了?!得到的答案:蓝魔的平板也是这个搭配。这样, 那需求应该就没问题了。先看现象再说:
硬件搭起来看现象,如下图:

红色区域是FT5406上报有效数据的范围(1280*600),以左上角为原点 ,X轴方向上报数据的最大值1280,Y轴方向上报的最大数据是600.。但是我用的LG的10.1寸屏,分辨率为1366*768。若想把触摸IC上报的数据和像素点的值一一对应起来,只能通过校正了。开始做校正的时候有点犯抽。竟然自己写校正算法,代码冗长不说,校准误差也特别大。 还好,后来想起了tslib这个东西。tslib是专门为电阻屏设计的一个校正库,只能校正单点触摸数据。而FT5406是支持5点触摸的。 不过只需要校正一点就可以了,这个点与其他四个点的上报数据的偏差大小无区别,只需要在驱动中做相同的消除偏差处理即可。思路有了,下面就从驱动开始说起:
1. FT5406 在Linux 3.5 中的驱动要点----数据上报过程
FT5406是通过IIC总线同CPU进行数据交互的,内核中的驱动框架符合一个典型IIC设备驱动+输入子系统(默认大家是了解IIC设备驱动和输入子系统驱动的)。硬件I/O的初始化和寄存器配置就不在这里赘述了, 照着手册来就可以了。重点看一下,数据上报过程,先看一个FT5406 原理图(图中标的是5206 ,没关系接口是一样的)::

原理图上可以看到,用到了EINT14这根中断线。通过这条中断线,差不多就能猜到上报流程了吧:当用户触摸到触摸板以后,产生中断,在中断服务程序中读IIC。这样就完成了一次数据的上报。下面就看看内核源码的实现,先看一个流程图:

中断代码实现如下:
static void ft5x0x_ts_pen_irq_work(struct work_struct *work) { //底半部中断
struct ft5x0x_ts_data *ts = container_of(work, struct ft5x0x_ts_data, work);
if (!ft5x0x_read_data(ts)) {
ft5x0x_ts_report(ts);
}
enable_irq(this_client->irq);
}
static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id) {//顶半部中断
struct ft5x0x_ts_data *ts = dev_id;
disable_irq_nosync(this_client->irq);
if (!work_pending(&ts->work)) {
queue_work(ts->queue, &ts->work);
}
return IRQ_HANDLED;
}static int ft5x0x_read_data(struct ft5x0x_ts_data *ts) {
struct ft5x0x_event *event = &ts->event;
u8 buf[32] = { 0 };
int ret;
#ifdef CONFIG_FT5X0X_MULTITOUCH
ret = ft5x0x_i2c_rxdata(buf, 31);
#else
ret = ft5x0x_i2c_rxdata(buf, 7);
#endif
if (ret < 0) {
printk("%s: read touch data failed, %d\n", __func__, ret);
return ret;
}
memset(event, 0, sizeof(struct ft5x0x_event));
event->touch_point = buf[2] & 0x07;
if (!event->touch_point) {
ft5x0x_ts_release(ts);
return 1;
}
#ifdef CONFIG_FT5X0X_MULTITOUCH
switch (event->touch_point) {
case 5:
event->x[4] = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
event->y[4] = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
case 4:
event->x[3] = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
event->y[3] = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
case 3:
event->x[2] = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
event->y[2] = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
case 2:
event->x[1] = (s16)(buf[0x09] & 0x0F)<<8 | (s16)buf[0x0a];
event->y[1] = (s16)(buf[0x0b] & 0x0F)<<8 | (s16)buf[0x0c];
case 1:
event->x[0] = (s16)(buf[0x03] & 0x0F)<<8 | (s16)buf[0x04];
event->y[0] = (s16)(buf[0x05] & 0x0F)<<8 | (s16)buf[0x06];
break;
default:
printk("%s: invalid touch data, %d\n", __func__, event->touch_point);
return -1;
}
#else
if (event->touch_point == 1) {
event-&