2. I2C通信协议详解:总线结构、起始/停止条件、数据帧格式、地址与读写位
好,咱们进入正题。I2C协议,说白了就是一套“谁说话、谁听话、什么时候说”的规矩。你想想看,一条总线上挂着好几个设备,如果没有规矩,大家一起抢着发数据,那不乱套了?
我个人习惯把I2C比作一个会议室。主设备是会议主持人,从设备是参会者。主持人控制会议节奏,点名谁发言,谁才能说话。这个比喻虽然简单,但能帮你快速理解I2C的核心逻辑。
2.1 I2C总线结构:两条线,多个设备
I2C总线物理上只需要两根线:
- SDA(串行数据线):用来传输地址和数据。
- SCL(串行时钟线):用来同步数据传输的时钟信号。
这两根线都是开漏输出结构。什么叫开漏?就是芯片只能把线拉低,不能主动拉高。所以总线上必须接上拉电阻(通常4.7kΩ或10kΩ)。空闲时,SDA和SCL都被上拉电阻拉到高电平。
总线上可以挂多个主设备和多个从设备。但同一时刻只能有一个主设备控制总线。每个从设备都有一个唯一的7位或10位地址。
2.2 起始条件与停止条件
I2C通信的开始和结束,由SDA和SCL的特定电平变化来标志。这就像会议主持人说“会议开始”和“会议结束”。
| 条件 | SDA状态 | SCL状态 | 说明 |
|---|---|---|---|
| 起始条件(S) | 高→低 | 高电平 | SCL为高时,SDA从高跳变到低 |
| 停止条件(P) | 低→高 | 高电平 | SCL为高时,SDA从低跳变到高 |
关键点:起始和停止条件都是SCL为高电平时,SDA发生跳变。而在数据传输过程中,SDA只能在SCL为低电平时变化。这个区别非常重要,我当年调试时就是因为没注意这个细节,逻辑分析仪抓出来的波形怎么看怎么不对。
我曾经在一个项目中,因为代码里起始条件之后忘了加一个小延时,导致从设备没识别到起始信号,整个通信都乱了。后来用示波器一看,SDA的下降沿和SCL的上升沿几乎重叠了,从设备根本分不清是起始条件还是数据位。
2.3 数据帧格式:一个字节一个字节地传
I2C的数据传输是以字节为单位的。每传输一个字节(8位),后面紧跟着一个应答位(ACK)。所以实际上每帧是9位。
数据传输的规则很简单:
- 高位先行:先传最高位(MSB),再传低位。
- SCL低电平时,发送方改变SDA数据。
- SCL高电平时,接收方读取SDA数据。
- 每字节后,接收方在第9个SCL周期拉低SDA表示应答(ACK),或保持高表示非应答(NACK)。
2.4 地址与读写位:点名与指令
主设备发起通信时,发送的第一个字节就是从设备地址 + 读写位。格式如下:
| 位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 含义 | 7位从设备地址 | R/W | ||||||
| 说明 | 高7位是设备地址 | 0=写,1=读 | ||||||
举个例子:我们用的LCD模块,I2C地址通常是0x27(7位地址)。如果要写数据,发送的字节就是 0x27 << 1 | 0 = 0x4E;如果要读数据,就是 0x27 << 1 | 1 = 0x4F。
注意:很多数据手册上给出的地址是7位地址,但实际发送时要左移一位,最低位补上读写位。我见过不少新手直接把0x27发出去,结果通信失败,折腾半天才发现是地址没移位。
2.5 完整通信流程:一次典型的写操作
咱们以向LCD写一个字节数据为例,看看完整的I2C通信流程:
- 主设备发送起始条件:SDA在SCL高电平时拉低。
- 主设备发送地址字节:0x4E(0x27左移1位 + 写位0)。
- 从设备应答:LCD模块拉低SDA表示“我在”。
- 主设备发送控制字节:比如0x08(背光开)或0x00(背光关)。
- 从设备应答:再次拉低SDA。
- 主设备发送数据字节:比如要显示的字符' A '的ASCII码0x41。
- 从设备应答:拉低SDA。
- 主设备发送停止条件:SDA在SCL高电平时拉高。
你看,整个过程就像一场有来有回的对话。主设备说一句,从设备应一声,确保信息传到了。
2.6 常见问题与避坑指南
做I2C驱动,有几个坑我踩过,分享给你:
- 总线锁死:从设备异常时可能一直拉低SDA不放。主设备发停止条件也无效。解决办法是发送9个以上的SCL时钟脉冲,让从设备复位状态机。我一般写一个“总线恢复”函数,专门处理这种情况。
- 地址冲突:两个从设备用了相同地址。这时候要么换硬件地址(如果芯片有地址选择引脚),要么用I2C多路复用器。我在一个项目里同时用了两个相同型号的传感器,就是靠一个PCA9548A解决的。
- 速率不匹配:主设备跑400kHz,从设备只支持100kHz。通信会间歇性失败。解决办法是统一降到100kHz,或者用时钟拉伸(如果从设备支持)。
重要提醒:调试I2C时,逻辑分析仪是你的好朋友。别光靠眼睛看代码,抓一下波形,什么问题都一目了然。我每次调I2C,第一件事就是挂上逻辑分析仪,看起始条件、地址、ACK,三步走完基本就能定位问题。