第4章 GT911/FT5x06驱动实战:主流触控IC驱动分析、中断处理与坐标上报、调试技巧与常见问题

好,我们直接进入正题。这一章聊的是触控IC驱动实战,我选了市面上最常用的两颗芯片——GT911和FT5x06。为什么选它们?说白了,你在国产平板、车机、工控板上,十有八九会遇到这两颗。我自己做过的项目里,至少七八个案子用的就是它们。

4.1 GT911与FT5x06的核心差异

先看一张对比表,心里有个底:

对比项 GT911 FT5x06
厂商 汇顶(Goodix) 敦泰(FocalTech)
I2C地址 0x5D / 0x14(可配置) 0x38(固定)
最大触点数 5点 5点(FT5206为2点)
寄存器读取方式 先写地址,再读数据 直接读偏移地址
中断触发方式 下降沿触发 下降沿触发
固件升级 需专用工具 可通过I2C升级

嗯,这里要注意:GT911的I2C地址不是固定的。它的地址由INT引脚的上拉电阻决定。我踩过这个坑——板子画好了,结果I2C扫描不到设备,查了半天发现是地址配错了。所以拿到新板子,第一件事就是确认INT引脚的硬件连接。

4.2 驱动初始化流程

驱动初始化,说白了就是让触控IC从「休眠」状态进入「工作」状态。流程大致如下:

  1. 硬件复位:拉低RST引脚,保持5ms以上,再拉高。
  2. I2C通信检测:读取芯片ID寄存器,确认通信正常。
  3. 配置参数写入:设置分辨率、中断触发方式等。
  4. 中断使能:注册中断处理函数,准备接收触摸事件。

我贴一段GT911的初始化代码,你感受一下:

static int gt911_init(struct i2c_client *client)
{
    int ret;
    uint8_t chip_id[2];

    // 1. 硬件复位
    gpio_set_value(gt911_rst_gpio, 0);
    msleep(10);
    gpio_set_value(gt911_rst_gpio, 1);
    msleep(50);

    // 2. 读取芯片ID
    ret = gt911_i2c_read(client, GT911_REG_CHIP_ID, chip_id, 2);
    if (ret < 0) {
        dev_err(&client->dev, "Failed to read chip ID\n");
        return -ENODEV;
    }

    // 3. 配置分辨率
    gt911_i2c_write(client, GT911_REG_CONFIG_VERSION, 0x01);
    gt911_i2c_write(client, GT911_REG_MAX_X, (screen_width >> 8) & 0xFF);
    gt911_i2c_write(client, GT911_REG_MAX_X + 1, screen_width & 0xFF);
    // ... 类似配置Y轴

    // 4. 注册中断
    ret = request_threaded_irq(client->irq, NULL, gt911_irq_handler,
                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                               "gt911", client);
    if (ret) {
        dev_err(&client->dev, "Failed to request irq\n");
        return ret;
    }

    return 0;
}

FT5x06的初始化大同小异,只是寄存器地址不同。我个人习惯把复位时序单独封装成一个函数,方便调试时手动调用。

4.3 中断处理与坐标上报

中断处理是驱动的核心。触控IC检测到触摸后,会拉低INT引脚,触发中断。驱动在中断处理函数里读取触摸数据,然后上报给上层。

GT911的中断处理流程:

static irqreturn_t gt911_irq_handler(int irq, void *dev_id)
{
    struct i2c_client *client = dev_id;
    uint8_t buf[GT911_POINT_INFO_LEN];
    int touch_count, i;

    // 1. 读取触摸点信息
    gt911_i2c_read(client, GT911_REG_POINT_INFO, buf, GT911_POINT_INFO_LEN);

    touch_count = buf[0] & 0x0F;  // 低4位表示触摸点数

    // 2. 逐个读取触摸点坐标
    for (i = 0; i < touch_count; i++) {
        uint8_t point_buf[GT911_POINT_DATA_LEN];
        int x, y, id;

        gt911_i2c_read(client, GT911_REG_POINT_DATA + i * 8, point_buf, GT911_POINT_DATA_LEN);

        x = (point_buf[0] << 8) | point_buf[1];
        y = (point_buf[2] << 8) | point_buf[3];
        id = point_buf[5] & 0x0F;

        // 3. 上报坐标
        input_report_key(gt911_input_dev, BTN_TOUCH, 1);
        input_report_abs(gt911_input_dev, ABS_MT_POSITION_X, x);
        input_report_abs(gt911_input_dev, ABS_MT_POSITION_Y, y);
        input_mt_slot(gt911_input_dev, id);
        input_mt_report_slot_state(gt911_input_dev, MT_TOOL_FINGER, true);
    }

    // 4. 同步上报
    input_sync(gt911_input_dev);

    // 5. 清除中断标志(写0到状态寄存器)
    gt911_i2c_write(client, GT911_REG_POINT_INFO, 0x00);

    return IRQ_HANDLED;
}

这里有个细节:一定要在读取完所有坐标后,清除中断标志。否则IC会一直拉低INT引脚,导致中断风暴。我曾经在一个项目里忘了这步,结果系统CPU占用率直接飙到100%,触控还卡得要命。

FT5x06的处理逻辑类似,但它的寄存器布局更紧凑。读取一个触摸点只需要6个字节:

// FT5x06 触摸点数据结构
// byte[0]: 触摸状态 + 触摸ID
// byte[1]: X坐标高8位
// byte[2]: X坐标低8位
// byte[3]: Y坐标高8位
// byte[4]: Y坐标低8位
// byte[5]: 保留

4.4 调试技巧与常见问题

做触控驱动调试,说白了就是跟I2C通信和中断时序较劲。我总结几个实用技巧:

调试三板斧:

  1. I2C探测:先用i2cdetect确认设备地址是否正确。
  2. 寄存器dump:读取关键寄存器,确认IC是否正常工作。
  3. 中断计数:在中断处理函数里加一个计数器,看中断是否触发。

常见问题及解决方案:

问题现象 可能原因 解决方案
I2C通信失败 地址错误、上拉电阻缺失 检查硬件连接,确认I2C地址
中断不触发 中断配置错误、INT引脚未拉低 检查中断触发方式,用示波器看INT波形
坐标上报异常 字节序错误、寄存器偏移算错 对照datasheet逐字节验证
多点触摸乱跳 ID分配错误、未清除中断标志 检查ID字段解析,确保清除标志

注意:GT911有一个「配置版本」寄存器(0x8047)。如果这个值跟你写入的不一致,说明配置没生效。我曾经遇到过IC内部flash损坏,导致配置无法保存,每次复位后都恢复默认值。排查了整整两天才找到原因。

4.5 性能优化建议

触控驱动的性能,直接影响用户体验。我给出几个优化方向:

  • 减少I2C读取次数:一次读取多个寄存器,而不是逐字节读取。
  • 使用线程化中断:避免在中断上下文做耗时操作。
  • 合理设置采样率:不是越高越好,60Hz通常就够用。
  • 开启硬件去抖:如果IC支持,优先用硬件去抖,减少软件负担。

嗯,说到采样率,我记得有个项目为了追求「流畅」,把采样率调到了120Hz。结果触控是流畅了,但CPU负载也上去了,导致其他任务卡顿。后来降到80Hz,体验反而更好了。所以,不要盲目追求参数,实际体验才是王道。

4.6 小结

这一章我们聊了GT911和FT5x06的驱动实战。从初始化流程到中断处理,再到坐标上报,每一步都有坑。我个人建议:拿到新IC时,先花半小时把datasheet的寄存器表看透,再动手写代码。磨刀不误砍柴工,真的。

下一章我们会聊触控屏的校准算法,包括线性校准和三点校准。到时候我会分享一个我踩过的「校准后反而更不准」的坑,敬请期待。