本系列分享的是我当初学习 Linux 驱动的来时路。在嵌入式开发中I2C 应该算是出场率非常高的一种通信接口。像触摸芯片、PMIC、电源管理芯片、摄像头 sensor、RTC、EEPROM 等外设很多都是通过 I2C 挂到主控上的。调触摸驱动时我们经常会看到类似这样的信息这里的i2c2、reg 0x5d到底是什么意思主控又是怎么通过两根线找到对应外设的这篇就以 RK3576 为例简单捋一捋 I2C 的通信机制。1. I2C 是什么I2C全称是Inter-Integrated Circuit直译过来就是“内部集成电路总线”。不用纠结这个名字简单理解就是I2C 是一种两线制串行通信协议只需要两根线就能实现主控和多个外设之间通信。这两根线分别是SDASerial Data串行数据线用来传输数据SCLSerial 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 通信里几个最关键的信号。起始信号 SSCL 为高电平期间SDA 由高变低。表示开始通信。应答信号 ACK接收方收到 8bit 数据后在第 9 个时钟周期拉低 SDA。表示数据已收到。停止信号 PSCL 为高电平期间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 是否配置了正确 pinctrlI2C 控制器节点 status 是否为 okay从设备 reg 地址是否正确SDA / SCL 是否有上拉电阻是否存在地址冲突i2cdetect 能否扫到设备很多 I2C 问题最后都不是协议本身有多复杂而是供电、复位、地址、pinctrl 这些基础条件没对上。10. 总结I2C 通信总结起来其实就是几个关键词两根线SDA SCL一个主机RK3576多个从设备触摸、PMIC、RTC、EEPROM 等三个信号Start、ACK、Stop一个地址通过从设备地址找到目标外设整个过程就像主控在总线上点名开始通信 - 发送地址 - 等待应答 - 读写数据 - 停止通信对于驱动开发来说理解到这一层基本就够用了。再往下深挖就是更底层的时序、电气特性、模拟电路和数字电路内容了。那些当然也重要但对大多数基于 SDK 做外设适配的开发者来说先搞懂通信流程、地址匹配和 ACK 应答已经足够解决大部分 I2C 调试问题。完
阅读完成 · 觉得有帮助?