1. 初识GT911触摸屏驱动移植最近在玩正点原子的IMX6ULL开发板发现官方教程主要讲的是FT5426触摸芯片而新开发板用的是GT911。作为一个嵌入式Linux开发者遇到这种教程没覆盖的情况太常见了。好在Linux内核自带goodix.c驱动支持GT911这让我少走了不少弯路。GT911是汇顶科技的一款电容式触摸屏控制器支持多点触控最高5点。在Linux内核中它的驱动代码位于/drivers/input/touchscreen/goodix.c。这个驱动不仅支持GT911还兼容GT9110、GT912等多个型号。我查了下兼容列表确认GT911在支持范围内这让我对移植工作有了信心。不过内核自带的驱动往往比较基础需要根据具体硬件进行调整。我的开发板上GT911通过I2C接口连接地址是0x14。在开始之前我建议你先准备好以下材料正点原子IMX6ULL开发板带GT911触摸屏的LCD模块编译好的Linux内核源码串口调试工具万用表用于检查硬件连接2. 设备树配置与驱动使能2.1 设备树节点编写设备树是Linux内核识别硬件的关键。我找到了内核文档中关于goodix驱动的说明Documentation/devicetree/bindings/input/touchscreen/goodix.txt参考它来配置设备树。在imx6ull.dtsi文件中添加以下节点i2c1 { clock-frequency 100000; pinctrl-names default; pinctrl-0 pinctrl_i2c1; status okay; gt91114 { compatible goodix,gt911; reg 0x14; pinctrl-names default; pinctrl-0 pinctrl_tsc; interrupt-parent gpio1; interrupt-gpios gpio1 9 GPIO_ACTIVE_LOW; reset-gpios gpio5 9 GPIO_ACTIVE_HIGH; irq-gpios gpio1 9 GPIO_ACTIVE_LOW; }; };这里有几个关键点需要注意中断引脚必须在iomuxc节点下配置复位引脚建议放在SNVS域我用的GPIO5_9I2C地址要正确0x142.2 内核配置与编译配置内核启用GT911驱动make menuconfig依次进入Device Drivers --- Input device support --- Touchscreens --- * Goodix I2C touchscreen保存配置后编译内核和设备树make zImage dtbs -j8编译完成后将zImage和.dtb文件拷贝到开发板启动。这时候驱动应该能加载但触摸屏很可能无法正常工作。3. 硬件复位时序问题排查3.1 现象分析驱动加载后我用i2c-tools测试I2C通信i2cdetect -y 1发现能检测到0x14地址的设备说明I2C通信正常。但读取配置寄存器时发现全是0i2cdump -f -y 1 0x14这意味着驱动无法正确读取GT911的配置信息。经过分析我发现内核自带的goodix.c驱动缺少关键的硬件复位时序。3.2 复位时序实现查阅GT911数据手册复位需要严格按照以下时序复位引脚拉低至少5μs等待10ms复位引脚拉高中断引脚拉低至少50μs将中断引脚配置为输入我参考正点原子的驱动代码在goodix.c中添加了复位函数static int goodix_ts_reset(struct goodix_ts_data *ts) { int ret 0; /* 申请复位GPIO */ if (gpio_is_valid(ts-reset_pin)) { ret devm_gpio_request_one(ts-client-dev, ts-reset_pin, GPIOF_OUT_INIT_HIGH, goodix_ts_reset); if (ret) return ret; } /* 申请中断GPIO先作为输出 */ if (gpio_is_valid(ts-irq_pin)) { ret devm_gpio_request_one(ts-client-dev, ts-irq_pin, GPIOF_OUT_INIT_HIGH, goodix_ts_irq); if (ret) return ret; } /* 严格按照时序操作 */ gpio_set_value(ts-reset_pin, 0); // 复位GT911 msleep(10); gpio_set_value(ts-reset_pin, 1); // 停止复位 msleep(10); gpio_set_value(ts-irq_pin, 0); // 拉低INT引脚 msleep(50); gpio_direction_input(ts-irq_pin); // INT引脚设为输入 return 0; }然后在probe函数中调用这个复位函数/* 获取设备树中的GPIO配置 */ ts-reset_pin of_get_named_gpio(client-dev.of_node, reset-gpios, 0); ts-irq_pin of_get_named_gpio(client-dev.of_node, interrupt-gpios, 0); /* 执行复位操作 */ error goodix_ts_reset(ts); if (error) { printk(reset failed\n); }4. 实现稳定五点触控4.1 原始驱动的问题即使添加了复位时序触摸屏能工作了但多点触控效果很差。使用tslib测试时发现手指移动时光标不跟随而是跳到最终位置只能画两点间的直线无法实现连续轨迹多点触控不稳定经常丢失触点这是因为内核自带的goodix.c驱动的中断处理函数比较简单没有优化多点触控的上报机制。4.2 优化中断处理函数我参考了社区优化的多点触控处理代码主要改进点包括更高效的数据读取方式完善的触点状态跟踪准确的上报机制新的中断处理函数如下static irqreturn_t goodix_ts_multi_touch_irq_handler(int irq, void *dev_id) { int ret; u8 status, large_detect, touch_num 0; int input_x, input_y, id 0; u8 data, buf[GOODIX_CONTACT_SIZE * 5]; struct goodix_ts_data *ts dev_id; int i; u16 touch_index 0; static u16 last_index 0; int pos 0, report_num 0; /* 读取触摸状态寄存器 */ ret goodix_i2c_read(ts-client, GOODIX_READ_COOR_ADDR, data, 1); if (data 0x00) return IRQ_HANDLED; /* 解析触摸信息 */ status data 0x80; large_detect (data 0x40) 6; touch_num data 0x0f; if (touch_num) { /* 读取所有触点数据 */ ret goodix_i2c_read(ts-client, GOODIX_READ_COOR_ADDR1, buf, GOODIX_CONTACT_SIZE*5); if (ret) return IRQ_HANDLED; id buf[0]; touch_index | (0x01 id); /* 上报每个触点 */ for (i 0; i 5; i) { if (touch_index (0x01 i)) { input_x (buf[pos1] | (buf[pos2]8)) 0xfff; input_y (buf[pos3] | (buf[pos4]8)) 0xfff; input_mt_slot(ts-input_dev, id); input_mt_report_slot_state(ts-input_dev, MT_TOOL_FINGER, true); input_report_abs(ts-input_dev, ABS_MT_POSITION_X, input_x); input_report_abs(ts-input_dev, ABS_MT_POSITION_Y, input_y); report_num; if (report_num touch_num) { pos 8; id buf[pos]; touch_index | (0x01 id); } } else { input_mt_slot(ts-input_dev, i); input_mt_report_slot_state(ts-input_dev, MT_TOOL_FINGER, false); } } } else if (last_index) { /* 无触摸时清除上次触点 */ for (i 0; i 5; i) { if (last_index (0x01 i)) { input_mt_slot(ts-input_dev, i); input_mt_report_slot_state(ts-input_dev, MT_TOOL_FINGER, false); } } } last_index touch_index; input_mt_report_pointer_emulation(ts-input_dev, true); input_sync(ts-input_dev); /* 清除中断标志 */ data 0x00; goodix_i2c_write(ts-client, GOODIX_READ_COOR_ADDR, data, 1); return IRQ_HANDLED; }4.3 测试与验证修改完成后重新编译内核并烧写到开发板。使用tslib测试多点触控export TSLIB_TSDEVICE/dev/input/event1 ts_test_mt现在应该能看到五点触控同时工作手指移动时光标平滑跟随可以画出连续的曲线触点识别稳定不跳动5. 常见问题与解决方案在实际移植过程中我遇到了不少坑这里总结几个典型问题5.1 I2C通信失败现象i2cdetect检测不到设备可能原因I2C地址不正确GT911默认0x14或0x5D硬件连接问题SDA/SCL接反、上拉电阻缺失设备树中I2C控制器未启用解决方案用万用表检查I2C线路确认设备树中i2c1节点status okay尝试调整I2C地址5.2 触摸坐标不准现象触摸点位置与实际位置偏差大可能原因屏幕分辨率配置错误坐标轴方向反了触摸屏与LCD对齐问题解决方案检查goodix_read_config函数中的分辨率设置在input_dev中调整坐标范围考虑增加坐标校准功能5.3 中断不触发现象触摸屏无反应但I2C通信正常可能原因中断GPIO配置错误中断触发方式不匹配中断线路上有干扰解决方案检查设备树中interrupt-gpios配置用示波器观察中断信号尝试调整中断触发方式边沿/电平移植GT911驱动虽然遇到不少挑战但最终实现稳定五点触控的效果还是很值得的。这个过程让我对Linux输入子系统、设备树和中断处理有了更深的理解。如果你也遇到类似问题希望这篇文章能帮你少走弯路。