当设备树中触摸 IC的设备节点和驱动匹配以后 。进入probe入口函数。在此函数中初始化触摸 IC,中断和 input 子系统等
在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用 Type A 还是 Type B 时序
触摸屏坐标信息时,需要按照MT协议来上报
MT 协议被分为两种类型, TypeA 和 TypeB,这两种类型的区别如下:
Type A:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(此类型在实际使用中非常少! 。
Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息, FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力
触摸点的信息通过一系列的 ABS_MT 事件(有的资料也叫消息)上报给 linux 内核,只有ABS_MT 事件是用于多点触摸的, 常用的是一下几类
触摸点数据上报时序
当设备树中触摸 IC的设备节点和驱动匹配以后, probe 函数就会执行,可以在此函数中初始化触摸 IC,中断和 input 子系统等。
-
-
- static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
-
- }
-
- /*
- * @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
- * @param - client : i2c设备
- * @return : 0,成功;其他负值,失败
- */
- static int ft5x06_ts_remove(struct i2c_client *client)
- {
- /* 释放input_dev */
- input_unregister_device(ft5x06.input);
- return 0;
- }
-
- /*
- * 传统驱动匹配表
- */
- static const struct i2c_device_id ft5x06_ts_id[] = {
- { "edt-ft5206", 0, },
- { "edt-ft5426", 0, },
- { /* sentinel */ }
- };
-
- /*
- * 设备树匹配表
- */
- static const struct of_device_id ft5x06_of_match[] = {
- { .compatible = "edt,edt-ft5206", },
- { .compatible = "edt,edt-ft5426", },
- { /* sentinel */ }
- };
-
- /* i2c驱动结构体 */
- static struct i2c_driver ft5x06_ts_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "edt_ft5x06",
- .of_match_table = of_match_ptr(ft5x06_of_match),
- },
- .id_table = ft5x06_ts_id,
- .probe = ft5x06_ts_probe,
- .remove = ft5x06_ts_remove,
- };
-
- /*
- * @description : 驱动入口函数
- * @param : 无
- * @return : 无
- */
- static int __init ft5x06_init(void)
- {
- int ret = 0;
-
- ret = i2c_add_driver(&ft5x06_ts_driver);
-
- return ret;
- }
-
- /*
- * @description : 驱动出口函数
- * @param : 无
- * @return : 无
- */
- static void __exit ft5x06_exit(void)
- {
- i2c_del_driver(&ft5x06_ts_driver);
- }
-
- module_init(ft5x06_init);
- module_exit(ft5x06_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("zuozhongkai");
-
- struct ft5x06_dev {
- struct device_node *nd; /* 设备节点 */
- int irq_pin,reset_pin; /* 中断和复位IO */
- int irqnum; /* 中断号 */
- void *private_data; /* 私有数据 */
- struct input_dev *input; /* input结构体 */
- struct i2c_client *client; /* I2C客户端 */
- };
-
- static struct ft5x06_dev ft5x06;
-
- /*
- * @description : 根据i2c协议从FT5X06读取多个寄存器数据
- * @param - dev: ft5x06设备
- * @param - reg: 要读取的寄存器首地址
- * @param - val: 读取到的数据
- * @param - len: 要读取的数据长度
- * @return : 操作结果
- */
- static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len)
- {
- int ret;
- struct i2c_msg msg[2];
- struct i2c_client *client = (struct i2c_client *)dev->client;
-
- /* msg[0],第一条写消息,发送要读取的寄存器首地址 */
- msg[0].addr = client->addr; /* ft5x06地址 */
- msg[0].flags = 0; /* 标记为发送数据 */
- msg[0].buf = ® /* 读取的首地址 */
- msg[0].len = 1; /* reg长度*/
-
- /* msg[1],第二条读消息,读取寄存器数据 */
- msg[1].addr = client->addr; /* ft5x06地址 */
- msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
- msg[1].buf = val; /* 读取数据缓冲区 */
- msg[1].len = len; /* 要读取的数据长度*/
-
- ret = i2c_transfer(client->adapter, msg, 2);
- if(ret == 2) {
- ret = 0;
- } else {
- ret = -EREMOTEIO;
- }
- return ret;
- }
-
-
- /*
- * @description : i2c驱动的probe函数,当驱动与
- * 设备匹配以后此函数就会执行
- * @param - client : i2c设备
- * @param - id : i2c设备ID
- * @return : 0,成功;其他负值,失败
- */
- static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- int ret = 0;
-
- ft5x06.client = client;
-
- /* 1,获取设备树中的中断和复位引脚 */
- ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
- ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
-
- /* 2,复位FT5x06 */
- ret = ft5x06_ts_reset(client, &ft5x06);
- if(ret < 0) {
- goto fail;
- }
-
- /* 3,初始化中断 */
- ret = ft5x06_ts_irq(client, &ft5x06);
- if(ret < 0) {
- goto fail;
- }
-
- /* 4,初始化FT5X06 */
- ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0); /* 进入正常模式 */
- ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1); /* FT5426中断模式 */
-
- /* 5,input设备注册
- 使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理
- */
- ft5x06.input = devm_input_allocate_device(&client->dev);
- if (!ft5x06.input) {
- ret = -ENOMEM;
- goto fail;
- }
- /* 6,初始化input设备 */
- ft5x06.input->name = client->name;
- ft5x06.input->id.bustype = BUS_I2C;
- ft5x06.input->dev.parent = &client->dev;
- /* 7,设置事件类型 */
- __set_bit(EV_KEY, ft5x06.input->evbit);
- __set_bit(EV_ABS, ft5x06.input->evbit);
- __set_bit(BTN_TOUCH, ft5x06.input->keybit);
- /* 7,设置EV_ABS事件上报信息 */
- input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);
- input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);
- input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0);
- input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0);
-
- /*8, 初始化多点电容触摸的 slots (触摸点)*/
- ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
- if (ret) {
- goto fail;
- }
- /*8, 注册input设备*/
- ret = input_register_device(ft5x06.input);
- if (ret)
- goto fail;
-
- return 0;
-
- fail:
- return ret;
- }
在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用 Type A 还是 Type B 时序。
- /*
- * @description : FT5X06中断服务函数
- * @param - irq : 中断号
- * @param - dev_id : 设备结构。
- * @return : 中断执行结果
- */
- static irqreturn_t ft5x06_handler(int irq, void *dev_id)
- {
- struct ft5x06_dev *multidata = dev_id;
-
- u8 rdbuf[29];
- int i, type, x, y, id;
- int offset, tplen;
- int ret;
- bool down;
-
- offset = 1; /* 偏移1,也就是0X02+1=0x03,从0X03开始是触摸值 */
- tplen = 6; /* 一个触摸点有6个寄存器来保存触摸值 */
-
- memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */
-
- /* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */
- ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
- if (ret) {
- goto fail;
- }
-
- /* 上报每一个触摸点坐标 */
- for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
- u8 *buf = &rdbuf[i * tplen + offset];
-
- /* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
- * bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件
- * bit5:4 保留
- * bit3:0 X轴触摸点的11~8位。
- */
- type = buf[0] >> 6; /* 获取触摸类型 */
- if (type == TOUCH_EVENT_RESERVED)
- continue;
-
- /* 我们所使用的触摸屏和FT5X06是反过来的 */
- x = ((buf[2] << 8) | buf[3]) & 0x0fff;
- y = ((buf[0] << 8) | buf[1]) & 0x0fff;
-
- /* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
- * bit7:4 Touch ID 触摸ID,表示是哪个触摸点
- * bit3:0 Y轴触摸点的11~8位。
- */
- id = (buf[2] >> 4) & 0x0f;
- down = type != TOUCH_EVENT_UP;
- //产生 ABS_MT_SLOT 事件,告诉内核当前上报的是哪个触摸点的坐标数据
- input_mt_slot(multidata->input, id);
- /* 给 slot 关 联 一 个 ABS_MT_TRACKING_ID ,
- ABS_MT_TOOL_TYPE 事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )
- */
- input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);
-
- if (!down)
- continue;
- //上报触摸点坐标信息
- input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
- input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
- }
-
- input_mt_report_pointer_emulation(multidata->input, true);
- //上报一个同步事件,告诉内核上报结束
- input_sync(multidata->input);
-
- fail:
- return IRQ_HANDLED;
-
- }