多点电容触摸简介
我手上使用的屏幕是 TFT LCD + 触摸屏组合起来的,底下是 LCD 面板,上面是触摸面板,将两个封装到一起就成了带有触摸屏的 LCD 屏幕。电容触摸屏也需要一个驱动 IC,驱动 IC 一般会提供一个 I2C 接口给主控制器,主控制器可以通过 I2C 接口来读取驱动 IC 的数据。我手上使用的触摸控制 IC 是 GT1151。
多点触摸(MT)协议
我们已经知道以下几点: ①、电容触摸屏是 I2C 接口,需要触摸 IC。 ②、触摸 IC 提供了中断信号引脚 (INT),可以通过中断来获取触摸信息。 ③、电容触摸屏得到的是触摸位置绝对信息以及触摸屏是否有按下。 结合以上几点我们可以得出电容触摸屏驱动起始就是以以下几种 Linux 驱动框架组合起来的: ①、I2C 设备驱动,因为电容触摸 IC 基本都是 I2C 接口,因此大框架就是 I2C 设备驱动。 ②、通过中断引脚向 Linux 内核上报触摸信息,因此需要用到 Linux 的中断驱动框架。坐标的上报在中断服务函数中完成。 ③、触摸屏的坐标信息、屏幕按下和抬起信息属于 Linux 的 input 子系统,因此向 Linux 内核上报触摸屏坐标信息就得用 input 子系统。
接下来我们来学习 input 子系统下的多点电容触摸协议。 触摸点的信息通过一系列的 ABS_MT 事件上报给 Linux 内核,只有 ABS_MT 事件是用于多点出没的,ABS_MT 时间定义在文件 include/dt-bindings/input/linux-event-codes.h 中,如下
#define ABS_MT_SLOT 0x2f /* MT slot being modified */#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */#define ABS_MT_POSITION_X 0x35 /* Center X touch position */#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */#define ABS_MT_TOOL_X 0x3c /* Center X tool position */#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
在这么多的 ABS_MT 事件中,我们最常用的就是 ABS_MT_SLOT、ABS_MT_POSITION_X、ABS_MT_POSITION_Y 和 ABS_MT_TRACKING_ID。其中 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y,用来上报触摸点的(X,Y)坐标信息,ABS_MT_SLOT 用来上报触摸点的 ID。对于能区分触摸点的设备,使用 ABS_MT_TRACKING_ID 来区分触摸点。
对于能区分触摸点的设备,我们上报触摸点信息的时候需要通过 input_mt_slot() 函数区分是哪一个触摸点,其原型如下
staticinlinevoidinput_mt_slot(struct input_dev *dev, int slot){ input_event(dev, EV_ABS, ABS_MT_SLOT, slot);}
此函数有两个参数,第一个参数是 input_dev 设备,第二个参数 slot 用于指定当前上报的是哪个触摸点信息。input_mt_slot() 函数会触发 ABS_MT_SLOT 事件,此事件会告诉接收者当前正在更新的是哪个触摸点的数据。
不管是什么类型的设备,最终都要调用 input_sync 函数来表示多点触摸信息传输完成,告诉接收者处理之前累积的所有信息,并准备好下一次接收。对于能区分触摸点的设备,其使用 slot 协议区分具体的触摸点,slot 需要用到 ABS_MT_TRACKING_ID 信息,这个 ID 需要硬件提供,这个设备需要给每个识别出的触摸点分配一个 slot,后面使用这个 slot 上报信息。设备驱动给每个识别出来的触摸点分配一个 slot,后面使用这个 slot 来上报触摸点信息,可以通过 slot 的 ABS_MT_TRACKING_ID 来新增、替换或删除触摸点。一个非负数的 ID 表示一个有效的触摸点,-1 这个 ID 表示未使用 slot。一个以前不存在的 ID 表示这是一个新加的触摸点,一个 ID 如果在也不存在就表示删除了。
对于能区分触摸点的设备驱动,发送触摸点信息的时序如下,
ABS_MT_SLOT 0ABS_MT_TRACKING_ID 45ABS_MT_POSITION_X x[0]ABS_MT_POSITION_Y y[0]ABS_MY_SLOT 1ABS_MT_TRACKING_ID 46ABS_MT_POSITION_X x[1]ABS_MT_POSITION_Y y[1]SYN_REPORT
首先上报 ABS_MT_SLOT 事件,也就是触摸点对应的 SLOT。每次上报一个触摸点坐标是前要先用 input_mt_slot 函数上报当前触摸点 SLOT,触摸点的 SLOT 就是触摸点 ID,需要由触摸 IC 提供。接下来根据要求,每个 SLOT 必须关联一个 ABS_MT_TRACKING_ID,通过修改 SLOT 关联的 ABS_MT_TRACKING_ID 来完成对触摸点的添加、替换和删除。具体用到的函数就是 input_mt_report_slot_state,如果是添加一个新的触摸点,那么此函数第三个参数要设置为 true,Linux 内核会自动分配一个 ABS_MT_TRACKING_ID 的值。然后就是上报触摸点的 X、Y 坐标。最后需要上报 SYN_REPORT 事件以保证同步。
当一个触摸点移除后,同样需要通过 SLOT 关联的 ABS_MT_TRACKING_ID 来处理,时序如下所示
ABS_MT_TRACKING_ID -1SYN_REPORT
ili210x.c 文件上报触摸坐标信息函数如下:
staticboolili210x_report_events(struct ili210x *priv, u8 *touchdata){structinput_dev *input = priv->input;int i;bool contact = false, touch;unsignedint x = 0, y = 0, z = 0;for (i = 0; i < priv->chip->max_touches; i++) { touch = priv->chip->parse_touch_data(touchdata, i, &x, &y, &z); input_mt_slot(input, i);if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) { touchscreen_report_pos(input, &priv->prop, x, y, true);if (priv->chip->has_pressure_reg) input_report_abs(input, ABS_MT_PRESSURE, z); contact = true; } } input_mt_report_pointer_emulation(input, false); input_sync(input);return contact;}
我们后续编写代码也是按照这个框架来编写。
多点触摸所使用的 API 函数
1、input_mt_init_slots 函数 input_mt_init_slots 函数用于初始化 MT 的输入 slots,编写 MT 驱动的时候必须先调用此函数初始化 slots,此函数原型如下所示
intinput_mt_init_slots(struct input_dev *dev, unsignedint num_slots,unsignedint flags)
dev 表示 MT 设备对应的 input_dev,因为 MT 设备属于 input_dev;num_slots 表示设备要使用的 SLOT 数量,也就是触摸点的数量;flags 表示其他一些flags 信息。返回值为 0 成功。
2、input_mt_slot 此函数用于产生 ABS_MT_SLOT 事件,告诉内核当前上报的是哪个触摸点的坐标数据,此函数原型如下:
staticinlinevoidinput_mt_slot(struct input_dev *dev, int slot){ input_event(dev, EV_ABS, ABS_MT_SLOT, slot);}
其中,dev 就是 MT 设备对应的 input_dev,slot 就是当前发送的触摸点信息。
3、input_mt_report_slot_state 此函数用于产生 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE 事件,ABS_MT_TRACKING_ID 事件给 slot 关联一个 ABS_MT_TRACKING_ID,ABS_MT_TOOL_TYPE 事件指定触摸类型。
boolinput_mt_report_slot_state(struct input_dev *dev, unsignedint tool_type, bool active)
其中,dev 表示 MT 设备对应的 input_dev。tool_type 表示触摸类型,可以选择手指、笔或手掌等等。active 为 true 时就表示连续触摸,input 子系统内核会自动分配一个 ABS_MT_TRACKING_ID 给 slot。false 表示触摸点抬起。
4、input_report_abs 所有触摸设备都使用此函数来上报触摸点坐标信息,通过 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 实现坐标信息上报。其函数原型如下:
staticinlinevoidinput_report_abs(struct input_dev *dev, unsignedint code, int value){ input_event(dev, EV_ABS, code, value);}
5、input_mt_report_pointer_emulation 函数 若追踪到的触摸点数量大于当前上报的数量,驱动程序用 BTN_TOOL_TAP 事件来通知用户空间当前追踪到的触摸点总数,然后调用 input_mt_report_pointer_emulation 函数将 use_count 参数设置为 false,否则将 use_count 参数设置为 true,表示当前的触摸点数量。函数原型如下:
voidinput_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
其中,dev 就是 MT 设备对应的 input_dev,use_count true 表示有效的触摸点数量,false 表示追踪到的触摸点数量大于当前上报的数量。
多点电容触摸驱动框架
综上所述,多点电容触摸框架需要以下几个内容: 1、主框架为 I2C。 2、Linux 中多用中断来上报触摸点坐标信息,使用到中断框架。 3、input 子系统框架 4、在中断处理中使用 Linux 的 MT 协议上报坐标信息。
接下来我们逐个回顾框架如何编写
1、I2C 驱动框架 驱动总体采用 I2C 框架,其参考代码如下所示
staticconststructi2c_device_idxxx_ts_id[] = {};staticconststructof_device_idxxx_of_match[] = {};staticstructi2c_driverxxx_tx_driver = { .owner = THIS_MODULE, ...};staticint __init xxx_init(void){int ret = 0; ret = i2c_add_driver(&xxx_ts_driver);return ret;}staticint __exit xxx_exit(void){ i2c_del_driver(&xxx_ts_driver);}module_init(xxx_init);module_exit(xxx_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("hcw");
当设备树中触摸 IC 的设备节点和驱动匹配以后,probe 函数就会执行,可以在此函数中初始化触摸 IC、中断、input 子系统等等。
2、初始化触摸 IC、中断和 input 子系统 初始化操作都是在 xxx_ts_probe 函数中完成,参考框架如下所示
staticintxxx_tx_probe(struct i2c_client &client, const struct i2c_device_id *id){structinput_dev *dev;/* 1、初始化 I2C */ ······/* 2、申请中断 */ devm_request_threaded_irq(&client->dev, client->irq, NULL, xxx_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name , &xxx); ······/* 3、input 设备申请与初始化 */ input = devm_input_allocate_device(&client->dev); input->name = client->name; input->id.bustype = BUS_I2C; input->dev.parent = &client -> dev; ······/* 4、初始化 input 和 MT */ __set_bit(EV_ABS, input->evbit); __set_bit(BTN_TOUCH, input->keybit); input_set_abs_params(input, ABS_X, 0, width, 0, 0); input_set_abs_params(input, ABS_Y, 0, height, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_X, 0, width, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, height, 0, 0); ······/* 5、注册 input_dev */ input_register_device(input);}
首先初始化触摸芯片,包括芯片的相关 IO,比如复位、中断等 IO 引脚,然后就是芯片本身的初始化,配置触摸芯片相关的寄存器。接下来就是申请中断、input dev,接着设置 input_dev 的上报事件。
值得注意的是,这里申请中断使用的函数是 devm_request_threaded_irq,这里的意思是将此中断线程化,可以保证高优先级的任务能被优先处理。devm_ 前缀的意思是让 Linux 自动释放资源。
3、上报坐标信息 最后就是在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择发送时序,对于能区分多个触摸点的设备参考框架如下:
staticirqreturn_txxx_handler(int irq, void *dev_id){int num;int x[n],y[n];/* 读取触摸点 */ ······/* 上报每一个触摸点坐标 */for(i = 0; i< num; i++) { input_mt_slot(input, id); input_mt_report_slot_state(input, MT_TOOL_FINGER, true); input_report_abs(input, ABS_MT_POSITION_X, x[i]); input_report_abs(input, ABS_MT_POSITION_Y, y[i]); } ······ input_sync(input); ······return IRQ_HANDLED;}
硬件分析
我手上的触摸芯片为 GT1151,这是一颗支持多点触控(最高5点)的电容触摸屏控制器,它通过最常用的 I2C总线 与主控芯片进行通信。除此之外,还有两根非常重要的GPIO线: 复位引脚(RST): 用于对触摸芯片进行硬件复位。通常由主控控制,在驱动初始化时,先拉低再拉高,完成一次复位操作。 中断引脚(INT): 这是触摸芯片主动“通知”主控的关键。当有触摸事件(按下、移动、抬起)发生时,GT1151会通过这个引脚向主控发送一个中断信号。采用中断方式,而不是让主控不停地去查询(轮询),可以极大地节省CPU资源,降低系统功耗。
查询底板原理图,可以得知 39、38、36、35 pin 对应的就是四根线,对应关系如下
图 1 原理图
程序编写
修改设备树
打开 imx6ull-hcw-emmc.dtsi,找到 iomuxc 节点,添加以下 pinctrl 组
pinctrl_gt1151_int: gt1151intgrp{ fsl,pins = < MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0xF080 >;};pinctrl_gt1151_reset: gt1151resetgrp{ fsl,pins = < MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0 >;};
接下来,在 i2c2 节点下添加设备,如下
gt1151@14{ compatible = "hcw,gt1151"; reg = <0x14>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gt1151_int &pinctrl_gt1151_reset>; interrupt-parent = <&gpio1>; interrupts = <9 0>; reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;};
使用新编译好的设备树启动 Linux 内核。
编写代码
创建 .c 文件,写入以下代码
/* * @Author: 胡城玮 * @FilePath: gt1151.c * @Date: 2026-03-24 * @Description: * @Version: 0.1 */#include<linux/module.h>#include<linux/i2c.h>#include<linux/regmap.h>#include<linux/gpio/consumer.h>#include<linux/of_irq.h>#include<linux/interrupt.h>#include<linux/input.h>#include<linux/input/mt.h>#include<linux/debugfs.h>#include<linux/delay.h>#include<linux/slab.h>#include<linux/gpio.h>#include<linux/of_gpio.h>#include<linux/input/mt.h>#include<linux/input/touchscreen.h>#include<linux/i2c.h>#include<asm/unaligned.h>#define GT_CTRL_REG 0X8040 /* GT1151控制寄存器 */#define GT_MODSW_REG 0X804D /* GT1151模式切换寄存器 */#define GT_1xx_CFGS_REG 0X8050 /* GT1151配置起始地址寄存器 */#define GT_CHECK_REG 0X80FF /* GT1151校验和寄存器 */#define GT_PID_REG 0X8140 /* GT1151产品ID寄存器 */#define GT_GSTID_REG 0X814E /* GT1151当前检测到的触摸情况 */#define GT_TP1_REG 0X8150 /* 第一个触摸点数据地址 */#define GT_TP2_REG 0X8158 /* 第二个触摸点数据地址 */#define GT_TP3_REG 0X8160 /* 第三个触摸点数据地址 */#define GT_TP4_REG 0X8168 /* 第四个触摸点数据地址 */#define GT_TP5_REG 0X8170 /* 第五个触摸点数据地址 */#define MAX_SUPPORT_POINTS 5 /* 最多5点电容触摸 */const u8 irq_table[] = {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}; /* 触发方式 */structgt1151_dev{int irq_pin, reset_pin;int irqnum;int irqtype;int max_x;int max_y;void *private_data;structinput_dev *input;structi2c_client *client;};staticstructgt1151_devgt1151dev;/** * @description: 硬件复位 * @param {i2c_client} *client * @param {gt1151_dev} *dev * @return {*} */staticintgt1151_dev_init(struct i2c_client *client, struct gt1151_dev *dev){int ret = 0;if (gpio_is_valid(dev->reset_pin)) { ret = devm_gpio_request_one(&client->dev, dev->reset_pin, GPIOF_OUT_INIT_HIGH, "gt1151 reset");if(ret) {return ret; } }if (gpio_is_valid(dev->irq_pin)) { ret = devm_gpio_request_one(&client->dev, dev->irq_pin, GPIOF_OUT_INIT_HIGH, "gt1151 int");if(ret) {return ret; } } gpio_set_value(dev->reset_pin, 0); /* 复位GT1151 */ msleep(10); gpio_set_value(dev->reset_pin, 1); /* 停止复位GT1151 */ msleep(10); gpio_set_value(dev->irq_pin, 0); /* 拉低INT引脚 */ msleep(50); gpio_direction_input(dev->irq_pin); /* INT引脚设置为输入 */return0;}/** * @description: 读寄存器 * @param {gt1151_dev} *dev * @param {u16} reg * @param {u8} *buf * @param {int} len * @return {*} */staticintgt1151_read_regs(struct gt1151_dev *dev, u16 reg, u8 *buf, int len){int ret; u8 regdata[2];structi2c_msgmsg[2];structi2c_client *client = (structi2c_client *)dev->client; regdata[0] = reg >> 8; regdata[1] = reg & 0xff; msg[0].addr = client->addr; msg[0].buf = ®data[0]; msg[0].flags = !I2C_M_RD; msg[0].len = 2; msg[1].addr = client->addr; msg[1].buf = buf; msg[1].flags = I2C_M_RD; msg[1].len = len; ret = i2c_transfer(client->adapter, msg, 2);if(ret == 2) { ret = 0; }else { ret = -EREMOTEIO; }return ret;}/** * @description: 写寄存器 * @param {gt1151_dev} *dev * @param {u16} reg * @param {u8} *buf * @param {int} len * @return {*} */staticintgt1151_write_regs(struct gt1151_dev *dev, u16 reg, u8 *buf, int len){ u8 b[256];structi2c_msgmsg;structi2c_client *client = (structi2c_client *)dev->client; b[0] = reg >> 8; b[1] = reg & 0xff;memcpy(&b[2], buf, len); msg.addr = client->addr; msg.flags = 0; msg.buf = b; msg.len = len + 2;return i2c_transfer(client->adapter, &msg, 1);}staticirqreturn_tgt1151_irq_handler(int irq, void *dev_id){int touch_num = 0;int input_x, input_y;int id = 0;int ret = 0; u8 data; u8 touch_data[5];structgt1151_dev *dev = dev_id; ret = gt1151_read_regs(dev, GT_GSTID_REG, &data, 1);if (data == 0x00) { /* 没有触摸数据,直接返回 */goto failed; } else { /* 统计触摸点数据 */ touch_num = data & 0x0f; }if(touch_num) { /* 单点触摸按下 */ gt1151_read_regs(dev, GT_TP1_REG, touch_data, 5); id = touch_data[0] & 0x0F;if(id == 0) { input_x = touch_data[1] | (touch_data[2] << 8); input_y = touch_data[3] | (touch_data[4] << 8); input_mt_slot(dev->input, id); input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true); input_report_abs(dev->input, ABS_MT_POSITION_X, input_x); input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y); } } elseif(touch_num == 0){ /* 单点触摸释放 */ input_mt_slot(dev->input, id); input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); } input_mt_report_pointer_emulation(dev->input, false); input_sync(dev->input); data = 0x00; gt1151_write_regs(dev, GT_GSTID_REG, &data, 1);failed:return IRQ_HANDLED;}staticintgt1151_read_firmware(struct i2c_client *client, struct gt1151_dev *dev){int ret = 0,version = 0; u8 data[7] = {0}; gt1151_read_regs(dev, GT_1xx_CFGS_REG, data, 7);if (ret) { dev_err(&client->dev, "Unable to read Firmware.\n");return ret; } dev->max_x = (data[2] << 8) + data[1]; dev->max_y = (data[4] << 8) + data[3]; dev->irqtype = data[6] & 0x3; printk("X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", dev->max_x, dev->max_y, dev->irqtype);return0;}staticconststructof_device_idgt1151_of_match[] = { {.compatible = "hcw,gt1151"}, {}};MODULE_DEVICE_TABLE(of, gt1151_of_match);staticintgt1151_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id){ printk("matched!!\r\n");int ret = 0; u8 data = 0; gt1151dev.client = client;/* 初始化设备 */// 获取设备信息 gt1151dev.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0); gt1151dev.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);// 复位 gt1151 ret = gt1151_dev_init(client, >1151dev);if(ret < 0)goto failed; data = 0x02; gt1151_write_regs(>1151dev, GT_CTRL_REG, &data, 1); /* 软复位 */ mdelay(100); data = 0x0; gt1151_write_regs(>1151dev, GT_CTRL_REG, &data, 1); /* 停止软复位 */ mdelay(100);// 读取硬件信息 ret = gt1151_read_firmware(client, >1151dev);if(ret < 0)goto failed;// input 设备注册 gt1151dev.input = devm_input_allocate_device(&client->dev);if (!gt1151dev.input) { ret = -ENOMEM;goto failed; } gt1151dev.input->name = client->name; // 获取设备名 gt1151dev.input->id.bustype = BUS_I2C; // 输入设备来自 IIC 总线 gt1151dev.input->dev.parent = &client->dev; // 设置父设备为 I2C 设备 __set_bit(EV_KEY, gt1151dev.input->evbit); __set_bit(EV_ABS, gt1151dev.input->evbit); // 设置支持的事件类型,按下和绝对坐标 __set_bit(BTN_TOUCH, gt1151dev.input->keybit); // 设置支持的按键/* 配置 input 设备的坐标能力和多点触摸能力 */ input_set_abs_params(gt1151dev.input, ABS_X, 0, gt1151dev.max_x, 0, 0); input_set_abs_params(gt1151dev.input, ABS_Y, 0, gt1151dev.max_y, 0, 0); input_set_abs_params(gt1151dev.input, ABS_MT_POSITION_X,0, gt1151dev.max_x, 0, 0); input_set_abs_params(gt1151dev.input, ABS_MT_POSITION_Y,0, gt1151dev.max_y, 0, 0); ret = input_mt_init_slots(gt1151dev.input, MAX_SUPPORT_POINTS, 0);if (ret)goto failed; ret = input_register_device(gt1151dev.input);if (ret) goto failed; ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, gt1151_irq_handler, irq_table[gt1151dev.irqtype] | IRQF_ONESHOT, client->name, >1151dev);if (ret) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");return ret; }failed:return ret;}staticvoidgt1151_i2c_remove(struct i2c_client *client){/* 进行资源释放 */}staticstructi2c_drivergt1151_i2c_driver = { .probe = gt1151_i2c_probe, .remove = gt1151_i2c_remove, .driver = { .name = "gt1151_drv", .owner = THIS_MODULE, .of_match_table = gt1151_of_match }};staticint __init gt1151_init(void){int ret = 0; ret = i2c_add_driver(>1151_i2c_driver);return ret;}staticvoid __exit gt1151_exit(void){ i2c_del_driver(>1151_i2c_driver);}module_init(gt1151_init);module_exit(gt1151_exit);MODULE_AUTHOR("hcw");MODULE_LICENSE("GPL");
由于内核中已经存在了 gtxxx 的驱动,所以我们暂时先将设备树和驱动程序中的设备名改为 "hcw,gt1"。装载模块,现在我们可以读取到触摸屏上传的数据了
图 2 input数据