4、TSP初始化流程:上电时序、固件下载、初始化配置、中断注册

触摸屏的初始化,说白了就是让芯片从「冷启动」到「准备好干活」的全过程。我刚开始接触TSP驱动时,总觉得不就是给个电、写几个寄存器嘛,结果被各种时序问题折腾得够呛。今天咱们就把这四步拆开揉碎了讲清楚。

核心要点:TSP初始化四步走——上电时序、固件下载、初始化配置、中断注册。一步都不能乱,顺序错了触摸屏就罢工。

TSP初始化流程总览 ① 上电时序 VDD → VDDIO → RST ② 固件下载 校验 → 烧录 → 确认 ③ 初始化配置 寄存器 → 参数 → 校准 ④ 中断注册 request_irq → 回调 关键:每一步必须等待前一步完成,不可并行 典型耗时:上电 10ms + 固件下载 200ms + 配置 50ms + 中断注册 1ms 上电 固件 配置 中断

4.1 上电时序——别急着给电

上电时序,说白了就是给触摸屏芯片「喂电」的顺序和节奏。我见过不少新手,上来就把所有电源引脚一起拉高,结果芯片要么不工作,要么工作不稳定。

一个典型的电容式触摸屏芯片,通常需要三路电源:

  • VDD(核心电压):1.8V 或 2.8V,给芯片内部逻辑供电
  • VDDIO(IO电压):1.8V 或 3.3V,给I2C/SPI接口供电
  • AVDD(模拟电压):2.8V 或 3.3V,给触摸检测模拟电路供电

上电顺序一般是:VDD → VDDIO → AVDD → 复位释放。为什么?因为内部逻辑先起来,IO接口才能工作,最后模拟部分才能正常。

⚠️ 注意:不同芯片的上电时序要求不同。有的要求VDD和VDDIO同时上电,有的要求VDD先于VDDIO至少1ms。一定要看datasheet里的时序图,别想当然。

我曾经在一个项目里,因为上电时序没处理好,导致芯片每次冷启动都检测不到触摸。查了两天才发现,是VDDIO比VDD早了0.5ms,芯片内部IO buffer处于不确定状态。后来加了个延时,问题就解决了。

4.2 固件下载——给芯片「灌」程序

上电完成后,芯片处于「裸机」状态。这时候需要把固件烧进去,芯片才能知道怎么干活。

固件下载的流程大致如下:

  1. 进入下载模式:通过拉高/拉低某个GPIO引脚,或者发送特定I2C命令,让芯片进入固件下载模式
  2. 校验固件:读取固件文件的CRC或MD5,确保固件没损坏
  3. 烧录固件:通过I2C/SPI接口,把固件数据逐块写入芯片的Flash或SRAM
  4. 校验烧录结果:读取芯片内部固件版本号,确认烧录成功
  5. 软复位:让芯片用新固件重新启动

经验之谈:固件下载过程中,千万不要断电或打断I2C通信。我有个同事就是在下载固件时不小心碰掉了排线,结果芯片变砖了,只能重新焊一片新的。

固件文件一般放在手机的文件系统中,路径通常是 /vendor/firmware//etc/firmware/。驱动在初始化时,会通过 request_firmware() 接口从文件系统加载固件。

// 固件下载示例代码(伪代码)
int tsp_firmware_download(struct tsp_device *tsp) {
    const struct firmware *fw;
    int ret;

    // 1. 从文件系统加载固件
    ret = request_firmware(&fw, "goodix_firmware.bin", &tsp->client->dev);
    if (ret < 0) {
        dev_err(&tsp->client->dev, "固件加载失败\n");
        return ret;
    }

    // 2. 进入下载模式
    gpio_set_value(tsp->rst_gpio, 0);
    gpio_set_value(tsp->int_gpio, 1);
    msleep(20);

    // 3. 逐块烧录固件
    for (int i = 0; i < fw->size; i += 256) {
        tsp_i2c_write_block(tsp, fw->data + i, 256);
        msleep(5);
    }

    // 4. 校验固件版本
    tsp_read_version(tsp);
    if (tsp->fw_version != EXPECTED_VERSION) {
        dev_err(&tsp->client->dev, "固件校验失败\n");
        release_firmware(fw);
        return -EINVAL;
    }

    // 5. 软复位
    gpio_set_value(tsp->rst_gpio, 1);
    msleep(50);

    release_firmware(fw);
    dev_info(&tsp->client->dev, "固件下载成功,版本: 0x%x\n", tsp->fw_version);
    return 0;
}

💡 小技巧:如果芯片支持「固件热升级」,可以在系统运行时更新固件,不需要重启手机。但要注意,升级过程中触摸功能会暂时失效,最好在屏幕关闭时进行。

4.3 初始化配置——给芯片「定规矩」

固件下载完成后,芯片已经能跑了,但还需要告诉它一些「规矩」:比如屏幕分辨率是多少、灵敏度要设多高、报点率要多少等等。

初始化配置通常包括以下几类:

配置项 说明 典型值
屏幕分辨率 触摸屏的物理尺寸,用于坐标映射 1080 × 2400
灵敏度阈值 触摸检测的灵敏度,值越小越灵敏 30 ~ 50
报点率 每秒上报触摸数据的次数 120Hz / 240Hz
手势开关 是否开启双击唤醒、手势识别等功能 开启/关闭
工作模式 主动模式/被动模式/低功耗模式 主动模式

这些配置参数,有的写在芯片的寄存器里,有的写在固件的配置表中。我个人习惯把配置参数放在一个结构体里,方便管理和调试。

// 初始化配置结构体
struct tsp_config {
    u16 screen_width;
    u16 screen_height;
    u8 sensitivity;
    u8 report_rate;
    bool gesture_enable;
    u8 work_mode;
};

// 配置写入函数
int tsp_apply_config(struct tsp_device *tsp, struct tsp_config *cfg) {
    // 写入分辨率
    tsp_i2c_write_reg(tsp, REG_SCREEN_WIDTH, cfg->screen_width);
    tsp_i2c_write_reg(tsp, REG_SCREEN_HEIGHT, cfg->screen_height);

    // 设置灵敏度
    tsp_i2c_write_reg(tsp, REG_SENSITIVITY, cfg->sensitivity);

    // 设置报点率
    tsp_i2c_write_reg(tsp, REG_REPORT_RATE, cfg->report_rate);

    // 手势开关
    tsp_i2c_write_reg(tsp, REG_GESTURE_ENABLE, cfg->gesture_enable ? 1 : 0);

    return 0;
}

⚠️ 注意:配置参数不是随便写的。灵敏度设得太高,容易误触;设得太低,触摸不灵敏。我建议在量产前,用自动化测试工具扫一遍参数空间,找到最优值。

4.4 中断注册——让芯片「喊」你

配置完成后,芯片已经准备好检测触摸了。但问题是,芯片怎么告诉CPU「有人摸我」?答案就是——中断。

触摸屏芯片通常有一个INT引脚,平时是高电平。当检测到触摸时,INT引脚会拉低(或拉高,取决于配置),触发CPU的中断处理函数。

中断注册的流程:

  1. 配置中断引脚:设置GPIO为输入模式,并使能内部上拉/下拉
  2. 申请中断:调用 request_irq()devm_request_threaded_irq() 注册中断处理函数
  3. 设置中断触发方式:一般是下降沿触发或低电平触发
  4. 使能中断:调用 enable_irq() 让中断生效
// 中断注册示例
static irqreturn_t tsp_irq_handler(int irq, void *dev_id) {
    struct tsp_device *tsp = (struct tsp_device *)dev_id;

    // 读取触摸数据
    tsp_read_touch_data(tsp);

    // 上报触摸事件给输入子系统
    input_report_key(tsp->input_dev, BTN_TOUCH, 1);
    input_report_abs(tsp->input_dev, ABS_X, tsp->x);
    input_report_abs(tsp->input_dev, ABS_Y, tsp->y);
    input_sync(tsp->input_dev);

    return IRQ_HANDLED;
}

int tsp_register_interrupt(struct tsp_device *tsp) {
    struct device *dev = &tsp->client->dev;
    int ret;

    // 配置中断引脚
    ret = devm_gpio_request_one(dev, tsp->int_gpio, GPIOF_IN, "tsp_int");
    if (ret < 0) {
        dev_err(dev, "中断引脚申请失败\n");
        return ret;
    }

    // 注册中断处理函数
    ret = devm_request_threaded_irq(dev, gpio_to_irq(tsp->int_gpio),
                                    NULL, tsp_irq_handler,
                                    IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                    "tsp", tsp);
    if (ret < 0) {
        dev_err(dev, "中断注册失败\n");
        return ret;
    }

    dev_info(dev, "中断注册成功,IRQ: %d\n", gpio_to_irq(tsp->int_gpio));
    return 0;
}

💡 小技巧:中断处理函数里不要做耗时操作,比如I2C读写。如果必须做,用 threaded_irq 模式,把耗时操作放到内核线程里执行,避免阻塞中断上下文。

中断注册完成后,整个TSP初始化流程就结束了。这时候触摸屏已经可以正常工作了——你用手指点一下屏幕,芯片会通过中断告诉CPU,CPU再通过输入子系统把触摸事件传给上层应用。

总结一下:上电时序是基础,固件下载是灵魂,初始化配置是规矩,中断注册是桥梁。四步走完,触摸屏才能「活」过来。我在实际项目中,经常把这四步封装成一个 tsp_init() 函数,按顺序调用,简单明了。


公众号:蓝海资料掘金营,微信deep3321