4、TSP初始化流程:上电时序、固件下载、初始化配置、中断注册
触摸屏的初始化,说白了就是让芯片从「冷启动」到「准备好干活」的全过程。我刚开始接触TSP驱动时,总觉得不就是给个电、写几个寄存器嘛,结果被各种时序问题折腾得够呛。今天咱们就把这四步拆开揉碎了讲清楚。
核心要点:TSP初始化四步走——上电时序、固件下载、初始化配置、中断注册。一步都不能乱,顺序错了触摸屏就罢工。
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 固件下载——给芯片「灌」程序
上电完成后,芯片处于「裸机」状态。这时候需要把固件烧进去,芯片才能知道怎么干活。
固件下载的流程大致如下:
- 进入下载模式:通过拉高/拉低某个GPIO引脚,或者发送特定I2C命令,让芯片进入固件下载模式
- 校验固件:读取固件文件的CRC或MD5,确保固件没损坏
- 烧录固件:通过I2C/SPI接口,把固件数据逐块写入芯片的Flash或SRAM
- 校验烧录结果:读取芯片内部固件版本号,确认烧录成功
- 软复位:让芯片用新固件重新启动
经验之谈:固件下载过程中,千万不要断电或打断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的中断处理函数。
中断注册的流程:
- 配置中断引脚:设置GPIO为输入模式,并使能内部上拉/下拉
- 申请中断:调用
request_irq()或devm_request_threaded_irq()注册中断处理函数 - 设置中断触发方式:一般是下降沿触发或低电平触发
- 使能中断:调用
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() 函数,按顺序调用,简单明了。