调触摸驱动时,我们经常会看到类似这样的信息:

这里的i2c2、reg = <0x5d>到底是什么意思?主控又是怎么通过两根线找到对应外设的?这篇就以 RK3576 为例,简单捋一捋 I2C 的通信机制。
1. I2C 是什么?
I2C,全称是Inter-Integrated Circuit,直译过来就是“内部集成电路总线”。
不用纠结这个名字,简单理解就是:
I2C 是一种两线制串行通信协议,只需要两根线,就能实现主控和多个外设之间通信。
这两根线分别是:
SDA:SerialData,串行数据线,用来传输数据;SCL:Serial Clock,串行时钟线,用来同步通信节奏。
也就是说,I2C 通信至少需要两根线:一根负责“说话”,一根负责“打节拍”。

在 RK3576 这类 SoC 中,I2C 控制器一般由主控内部提供,比如 I2C0、I2C1、I2C2 等。外部设备则挂在对应的 I2C 总线上。
2. I2C 通信像什么?
为了好理解,可以把 I2C 通信理解成 RK3576 发起的一次“点名对话”。
假设:
主机:RK3576从设备:GT911 触摸芯片设备地址:0x5d
整个过程大概是:
RK3576:大家注意,我要开始通信了!RK3576:地址是0x5d 的设备在不在?GT911:在!RK3576:我要给你写数据 / 我要从你这里读数据。GT911:收到。RK3576:通信结束,总线释放。
I2C 协议看起来有点绕,但核心流程就是这么回事。
3. 起始信号:准备通信
I2C 通信开始前,主机会先发送一个起始信号,也就是Start Condition。
条件是:SCL 保持高电平期间,SDA 从高电平跳变为低电平。
这个动作的意思就是:总线上的外设注意,我要开始通信了。
此时挂在这条 I2C 总线上的所有从设备都会“听着”,等待后面的地址匹配。
这里有个关键点:
I2C 总线上,SDA 的变化不是随便什么时候都算有效。一般来说,只有在 SCL 为高电平时,SDA 的跳变才有特殊意义。
比如:
SCL高电平时,SDA 高 -> 低:起始信号SCL 高电平时,SDA 低 -> 高:停止信号
而在数据传输过程中,SDA 通常会在 SCL 低电平期间准备数据,在 SCL 高电平期间保持稳定,供接收方采样。
这些时序一般由 RK3576 的 I2C 控制器自动处理,我们写驱动时通常不用手动去拉高拉低 SDA/SCL。
4. 从设备地址:喊谁谁应答
起始信号之后,RK3576 会发送从设备地址。
I2C 常见地址是 7bit,再加 1bit 读写控制位,组成 8bit 数据。
格式如下:
前 7 位:从设备地址最后 1 位:读写位0:写1:读
比如 GT911 的设备地址是0x5d,那么 RK3576 会在总线上发出这个地址。
总线上所有从设备都会收到这个地址,但只有地址匹配的设备才会应答。这个应答信号叫ACK。
ACK 的表现是:接收方在第 9 个时钟周期,将 SDA 拉低。
也就是说,I2C 发送 1 个字节并不是只需要 8 个时钟,而是需要 9 个时钟:
前 8 个时钟:传输 8bit 数据第 9 个时钟:传输 ACK 应答
如果主机没有检测到 ACK,通常说明:
设备地址不对;设备没有上电;I2C 引脚配置不对;外设没有正常工作;总线硬件异常。
所以调 I2C 设备时,如果设备一直不应答,第一反应就应该去查地址、供电、复位、pinctrl、上拉电阻这些基础条件。
5. 数据传输:一个字节一个字节来
地址匹配成功后,就进入真正的数据传输阶段。
I2C 数据传输的单位是字节,也就是 8bit。
并且传输时遵循:高位在前,MSB first。
比如一个字节0xA5,会从最高位 bit7 开始传。
根据读写位不同,通信方向也不同。
写操作
写操作是:RK3576 -> 从设备。比如主控给 GT911 写配置、写寄存器地址等。
过程大概是:
RK3576发送 8bit 数据从设备回复 ACKRK3576 再发送下一个 8bit 数据从设备继续 ACK直到数据发送完成
读操作
读操作是:从设备 -> RK3576。比如主控读取触摸坐标、读取芯片 ID、读取状态寄存器等。
过程大概是:
从设备发送 8bit 数据RK3576 回复 ACK从设备继续发送下一个 8bit 数据RK3576 继续 ACK直到读取完成
以上这些通信细节,大部分由 I2C 控制器和 Linux I2C 子系统帮我们处理好了。我们写驱动时,更多是调用类似i2c_transfer()、i2c_smbus_read_byte_data()这类接口,而不是手动模拟时序。
6. 停止信号:通信结束
数据传输完成后,主机会发送停止信号,也就是Stop Condition。
条件是:SCL 保持高电平期间,SDA 从低电平跳变为高电平。
这个动作表示:本次通信结束,总线释放。
停止信号发出后,从设备回到等待状态,RK3576 的 I2C 控制器也可以继续和其他外设通信。
7. I2C 的三个关键信号
通过前面的“点名对话”,可以总结出 I2C 通信里几个最关键的信号。

起始信号 S:SCL 为高电平期间,SDA 由高变低。表示开始通信。
应答信号 ACK:接收方收到 8bit 数据后,在第 9 个时钟周期拉低 SDA。表示数据已收到。
停止信号 P:SCL 为高电平期间,SDA 由低变高。表示通信结束。
理解这三个信号,基本就能看懂 I2C 通信的大致过程了。
8. I2C 地址冲突怎么办?
I2C 是一条总线上挂多个设备,因此每个从设备都需要有自己的地址。
但实际项目中,可能会遇到两个外设地址一样的情况。比如两个相同型号的触摸芯片,默认地址都一样,那就会冲突。
因为主机一喊0x5d,两个设备都应答,总线就乱套了。针对这种情况,一般有两种解决办法。
方案一:挂到不同 I2C 控制器
RK3576 有多个通用 I2C 控制器,比如 I2C0、I2C1、I2C2 等。如果两个设备地址一样,可以把它们分别挂到不同的 I2C 总线上。
比如:
设备A-> I2C2设备B-> I2C3
这样虽然地址一样,但它们不在同一条总线上,就不会冲突。
方案二:修改从设备地址
有些外设支持通过硬件引脚配置地址。比如某些芯片可以通过拉高或拉低地址选择引脚,切换不同的 I2C 地址。这种情况下,只要硬件设计时把地址区分开,就能避免冲突。
所以画原理图时,I2C 地址一定要提前确认,不然后期调试会很难受。
9. 调试 I2C 时重点看什么?
实际调 I2C 外设时,不要一上来就怀疑驱动。
可以先按下面几个点查:
[ ]外设供电是否正常?[ ]reset / enable 引脚是否拉到正确电平?[ ]SDA / SCL 是否配置了正确 pinctrl?[ ]I2C 控制器节点 status 是否为 okay?[ ]从设备 reg 地址是否正确?[ ]SDA / SCL 是否有上拉电阻?[ ]是否存在地址冲突?[ ]i2cdetect 能否扫到设备?
很多 I2C 问题,最后都不是协议本身有多复杂,而是供电、复位、地址、pinctrl 这些基础条件没对上。
10. 总结
I2C 通信总结起来其实就是几个关键词:
两根线:SDA + SCL一个主机:RK3576多个从设备:触摸、PMIC、RTC、EEPROM 等三个信号:Start、ACK、Stop一个地址:通过从设备地址找到目标外设
整个过程就像主控在总线上点名:
开始通信 -> 发送地址 -> 等待应答 -> 读写数据 -> 停止通信
对于驱动开发来说,理解到这一层基本就够用了。再往下深挖,就是更底层的时序、电气特性、模拟电路和数字电路内容了。

那些当然也重要,但对大多数基于 SDK 做外设适配的开发者来说,先搞懂通信流程、地址匹配和 ACK 应答,已经足够解决大部分 I2C 调试问题。
(完)
下期继续聊:为什么 I2C需要上拉?
本人专注Linux 嵌入式全栈开发,可提供从硬件方案评估与设计、Linux/AndroidBSP 适配、驱动开发、外设调试、系统移植到产品交付的全流程技术支持。
-
嵌入式
+关注
关注
5213文章
20871浏览量
339891 -
I2C通信
+关注
关注
0文章
33浏览量
9475 -
rk3576
+关注
关注
1文章
332浏览量
1787
发布评论请先 登录
RK3576平台PCA9548 I2C开关设备树配置与生效全解析
RK3576的MIPI CSI-2接口,4K视频输入与兼容实现技巧
RK3576开发板OpenGL性能大起底,这数据我真的服了
基于米尔RK3576的环视实时性方案解析
360环视硬件平台为什么推荐使用米尔RK3576开发板?
【作品合集】米尔RK3576开发板测评
【作品合集】灵眸科技EASY EAI Orin Nano(RK3576)开发板测评
RK3576助力智慧安防:8路高清采集与AI识别
瑞芯微RK3576与RK3576S有什么区别,性能参数配置与型号差异解析
驱动之路#40:I2C通信机制分析(RK3576视角)
评论