前言
正点原子的源码提供的驱动 GT9147只支持单点,说是没有硬件检测触摸点按下和抬起,但其实芯片是有提供的,只不过不太明确。我改进了一点点代码,就可以支持多点触摸,供大家学习。
原理
中断来之后,先读点个数N,然后根据点数读取N个寄存器,寄存器信息包括:id+X+Y.
比如此时
touchnum=3
id=1, X=100, Y=100
id=2, X=200, Y=200
id=3, X=300, Y=300
下一次上报
touchnum=2
id=2, X=200, Y=200
id=3, X=300, Y=300
那就可以知道点1松开了。
代码
这份修订是已经可以通过tslib一个叫tstest_mt那个啥的工具测过了。准确无误。
struct point_info {
int id;
int x;
int y;
};
struct gt9147_dev {
int irq_pin;
int reset_pin;
void *priv;
struct input_dev *input;
struct i2c_client *client;
int touch_num;
struct point_info p[MAX_SUPPORT_POINTS];
struct work_struct work;
};
const short touch_id_arr[MAX_SUPPORT_POINTS] = {
GT_TP1_REG, GT_TP2_REG, GT_TP3_REG, GT_TP4_REG, GT_TP5_REG
};
static int gt9147_report(struct gt9147_dev *dev)
{
static int last_touchnum = 0;
static int last_point_id[MAX_SUPPORT_POINTS] = {0};
int i, j;
int idx;
/* report true state */
for (i = 0; i < dev->touch_num; i++) {
input_mt_slot(dev->input, dev->p[i].id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
input_report_abs(dev->input, ABS_MT_POSITION_X, dev->p[i].x);
input_report_abs(dev->input, ABS_MT_POSITION_Y, dev->p[i].y);
}
/* sync false state */
if (last_touchnum > dev->touch_num) {
#if 0
printk("last[%d]: ", last_touchnum);
for(idx = 0; idx < last_touchnum; idx++)
printk("%d ", last_point_id[idx]);
printk("now[%d]: ", dev->touch_num);
for(idx = 0; idx < dev->touch_num; idx++)
printk("%d ", dev->p[idx].id);
printk("\n");
#endif
for (i = 0; i < dev->touch_num; i++) {
for (j = 0; j < last_touchnum; j++) {
if (last_point_id[j] == dev->p[i].id)
last_point_id[j] = -1;
}
}
/* to report release point */
for (j = 0; j < last_touchnum; j++) {
if (last_point_id[j] != -1) {
#if 0
printk("release id: %d\n", last_point_id[j]);
#endif
input_mt_slot(dev->input, last_point_id[j]);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false);
}
}
}
/* last point */
if (last_touchnum == 1 && dev->touch_num == 0){
} else {
input_mt_report_pointer_emulation(dev->input, true);
input_sync(dev->input);
}
/* update last data */
last_touchnum = dev->touch_num;
memset(last_point_id, 0, sizeof(int) * MAX_SUPPORT_POINTS);
for (i = 0; i < dev->touch_num; i++)
last_point_id[i] = dev->p[i].id;
return 0;
}
static irqreturn_t gt9147_irq_handler(int irq, void *dev_id)
{
int ret;
uint8_t data;
struct gt9147_dev *dev = dev_id;
int i;
uint8_t touch_data[5];
ret = gt9147_read_regs(dev, GT_GSTID_REG, &data, 1);
if (!data) {
goto failed;
}
dev->touch_num = data & 0x0f;
// printk("Current touch num=%d\n", dev->touch_num);
dev->touch_num = (dev->touch_num > MAX_SUPPORT_POINTS ? \
MAX_SUPPORT_POINTS : dev->touch_num);
for (i = 0; i < dev->touch_num; i++) {
gt9147_read_regs(dev, touch_id_arr[i], touch_data, 5);
dev->p[i].id = touch_data[0] & 0x0F;
dev->p[i].x = touch_data[1] | (touch_data[2] << 8);
dev->p[i].y = touch_data[3] | (touch_data[4] << 8);
// printk("[%d]: id %d x: %d y: %d\n", i, id, x, y);
}
gt9147_report(dev);
/* Clear reg */
data = 0;
gt9147_write_regs(dev, GT_GSTID_REG, &data, 1);
failed:
return IRQ_HANDLED;
}
大概逻辑是 中断来了之后读取所有按下的点和个数,然后放到 gt9147_report函数处理,gt9147_report函数是重点,但也简单:
- 上报所有按下点的状态
- 如果上次记录的点个数大于当前点个数,就说明有按键释放了.
根据上次保存的点id数组,寻找当前释放的点,然后上报释放点。 - 注意的是,如果是最后一个点释放,不用上报SYN事件了。
- 保存此次点个数和ID,用于第2步。