3、硬件链路分析:从触摸IC到主控的I2C/SPI总线延迟排查
触摸屏的响应延迟,很多时候不是算法的问题,而是数据在总线上堵车了。我见过不少团队,软件优化做了好几轮,延迟就是降不下来,最后拿示波器一抓——I2C时钟才100kHz,传输一帧数据要花掉好几毫秒。你说这能快得起来吗?
这一章,我们就从触摸IC到主控的物理链路入手,把I2C和SPI总线上的延迟一处处揪出来。
3.1 触摸数据是怎么从IC跑到主控的?
先看一张整体流程,心里有个谱。
从图上能看出来,触摸IC采集到原始数据后,通过I2C或SPI总线发给主控。主控收到中断信号,用DMA把数据搬到内存,然后应用层取走处理。每个环节都有延迟,但总线传输往往是最大的瓶颈。
3.2 I2C vs SPI:谁更适合触摸屏?
很多开发板默认用I2C,因为引脚少、接线简单。但I2C的速率上限摆在那里——标准模式100kHz,快速模式400kHz,高速模式3.4MHz。而SPI轻轻松松跑到几十MHz。
我做过一个对比测试,同样传输一帧触摸数据(约40字节),结果如下:
| 总线类型 | 时钟频率 | 传输时间(40字节) | 适用场景 |
|---|---|---|---|
| I2C 标准模式 | 100 kHz | ~3.6 ms | 低功耗、低速设备 |
| I2C 快速模式 | 400 kHz | ~0.9 ms | 大多数消费电子 |
| I2C 高速模式 | 3.4 MHz | ~0.11 ms | 高性能触摸屏 |
| SPI 模式0/3 | 10 MHz | ~0.032 ms | 工业/车载触摸 |
| SPI 模式0/3 | 40 MHz | ~0.008 ms | 超低延迟场景 |
注意:I2C的传输时间包含了地址位、控制位、应答位和起始/停止条件。实际有效数据吞吐量只有理论值的60%~70%。SPI没有这些开销,效率高得多。
3.3 实战排查:用示波器抓总线波形
理论说再多,不如动手测一测。我个人习惯的做法是这样的:
- 找到测试点——触摸IC的SCL/SDA(I2C)或SCLK/MOSI/MISO(SPI)引脚,最好在靠近主控端测量。
- 设置触发条件——用触摸中断信号作为触发源,抓取中断后的第一笔总线传输。
- 测量传输时间——从起始条件到停止条件的时间差,就是一帧数据的传输延迟。
- 检查信号质量——看上升沿/下降沿是否陡峭,有没有毛刺或振铃。
我曾经在一个项目里发现,I2C的SCL线上挂了两个上拉电阻,导致上升沿变得很缓,400kHz的时钟根本跑不上去。去掉一个电阻后,波形立马干净了,传输时间从1.2ms降到了0.4ms。
3.4 常见延迟陷阱与对策
在实际排查中,有几个坑特别容易踩到:
- 时钟频率设置错误——主控的I2C外设时钟分频配置不对,实际速率只有目标值的一半。用示波器量一下SCL频率就知道了。
- ACK等待超时——触摸IC处理内部数据时,可能会拉低SCL延长时钟(时钟拉伸)。如果主控的ACK超时设置太短,会反复重试,白白浪费几百微秒。
- 中断响应延迟——主控收到触摸中断后,如果正在处理高优先级中断,响应时间可能被拖到几百微秒甚至几毫秒。建议把触摸中断设为最高优先级。
- DMA配置不当——用CPU逐字节读取I2C数据,每读一个字节就要进一次中断,效率极低。用DMA+环形缓冲区,一次传输完成再通知CPU。
小技巧:如果硬件支持,把I2C换成SPI是最直接的优化手段。我有个项目把I2C 400kHz换成SPI 20MHz后,总线传输时间从0.9ms降到了0.02ms,几乎可以忽略不计。
3.5 代码级排查:检查驱动中的传输参数
硬件没问题了,还得看看驱动代码里有没有埋雷。下面是一段常见的I2C读取代码,注意看注释里的坑:
// 触摸IC读取函数(I2C示例)
int touch_read_reg(uint8_t reg_addr, uint8_t *buf, uint16_t len)
{
struct i2c_msg msg[2];
int ret;
// 写寄存器地址
msg[0].addr = TOUCH_I2C_ADDR;
msg[0].flags = 0; // 写
msg[0].len = 1;
msg[0].buf = ®_addr;
// 读数据
msg[1].addr = TOUCH_I2C_ADDR;
msg[1].flags = I2C_M_RD; // 读
msg[1].len = len;
msg[1].buf = buf;
// 注意:这里用了重复起始条件(restart),
// 如果主控不支持,会退化成stop+start,多花几十微秒
ret = i2c_transfer(TOUCH_I2C_BUS, msg, 2);
if (ret != 2) {
// 我曾经遇到过这里返回-110(超时),
// 原因是触摸IC在忙,时钟拉伸太久
return -EIO;
}
return 0;
}
排查时重点关注几个参数:
i2c_transfer的超时时间——设太短会频繁超时重试- 是否支持重复起始条件——不支持的话每次传输多花一个stop+start的时间
- 单次读取长度——有些触摸IC支持批量读取,一次读完整帧,比逐字节读快得多
3.6 总结一下排查清单
嗯,这一章的内容差不多就这些。最后给一张排查清单,下次遇到触摸延迟问题,按这个顺序查一遍:
| 排查项 | 检查方法 | 典型优化手段 |
|---|---|---|
| 总线时钟频率 | 示波器测量SCL/SCLK | 调整分频系数,匹配IC最高速率 |
| 信号完整性 | 观察上升沿/下降沿 | 调整上拉电阻、减少总线电容 |
| 传输帧大小 | 读取IC数据手册 | 启用批量读取模式 |
| 中断响应时间 | GPIO toggling + 逻辑分析仪 | 提高中断优先级,减少嵌套 |
| DMA配置 | 检查DMA传输完成标志 | 启用DMA,关闭CPU轮询 |
| 驱动超时参数 | 查看i2c_transfer返回值 | 适当增加超时时间,避免误重试 |