第1章:I2C协议基础回顾

各位工程师朋友,大家好。我是你们的老朋友,一个在嵌入式领域摸爬滚打了十几年的硬件工程师。今天咱们开始《Retimer芯片配置与I2C编程实战手册》的第一章。说实话,I2C这协议看起来简单,但坑是真不少。我见过太多项目因为I2C时序问题翻车了。所以,咱们先把地基打牢。

1.1 I2C总线物理层

I2C总线,说白了就是两根线:SCL(时钟线)和SDA(数据线)。这两根线都是开漏输出,需要外接上拉电阻。为什么这么设计?因为这样可以让多个设备共享总线,谁想说话谁就把线拉低,不想说话就释放总线,让上拉电阻把电平拉高。

关键参数:

  • 标准模式:100kHz
  • 快速模式:400kHz
  • 高速模式:3.4MHz
  • 超快速模式:5MHz

上拉电阻的选择是个学问。我刚开始做项目时,随便焊了个10kΩ电阻,结果在400kHz下波形惨不忍睹。后来才明白,电阻值取决于总线电容和通信速率。一般经验值:100kHz用4.7kΩ,400kHz用2.2kΩ。当然,具体还得看你的负载情况。

我的经验:如果总线上挂的设备多,或者走线长,建议用示波器看看上升沿。上升时间太长的话,换小一点的上拉电阻。但别太小,否则功耗会上去。

1.2 起始条件与停止条件

I2C通信的开始和结束,有特定的信号时序。起始条件:SCL高电平时,SDA从高变低。停止条件:SCL高电平时,SDA从低变高。

嗯,这里要注意:起始和停止条件只能由主机产生。从机只能被动响应。我曾经遇到一个案例,从机芯片的I2C状态机卡死了,怎么发起始条件都没反应。后来发现是停止条件没发对,总线一直处于忙状态。

避坑指南:我曾经在调试时,连续发了两个起始条件,结果从机直接罢工了。记住:每次通信结束,一定要发停止条件释放总线。除非你想用重复起始条件(Restart)来切换读写方向。

1.3 数据帧格式

I2C的数据传输,每个字节8位,后面跟一个应答位(ACK/NACK)。主机发送数据时,从机每收到一个字节,必须拉低SDA表示应答。如果从机不应答(NACK),说明出问题了。

数据帧的基本格式:

起始条件 | 7位地址 + R/W位 | 应答 | 数据字节 | 应答 | ... | 停止条件

我习惯把I2C通信想象成打电话:起始条件是拨号,地址是电话号码,R/W位是告诉对方我要说还是听,数据就是通话内容,停止条件是挂电话。

1.4 7位地址与10位地址

I2C设备地址有两种:7位和10位。7位地址能挂127个设备(0x00是广播地址,不能用)。10位地址能挂1023个设备,但实际很少用。

地址类型 地址范围 常用场景
7位地址 0x01 - 0x7F 绝大多数传感器、EEPROM、Retimer芯片
10位地址 0x000 - 0x3FF 复杂系统、多路复用器

你想想看,7位地址够用吗?其实很多时候不够。比如一个系统里挂8个同样的传感器,每个地址只能通过引脚配置成2-4种,很容易冲突。这时候就得用I2C多路复用器或者改用10位地址。

我的习惯:设计电路时,先把所有I2C设备的地址列出来,确保没有冲突。如果冲突了,要么换引脚配置,要么加多路复用器。别指望软件能解决硬件冲突。

1.5 时钟同步与仲裁

时钟同步是多主机通信的基础。多个主机同时发送时钟时,SCL线会被拉低到最慢的主机时钟。说白了,就是慢的说了算。

仲裁呢,是解决多个主机同时发送数据时的冲突问题。如果两个主机同时发送,谁先拉低SDA谁就赢。输的那个主机自动退出,等下次再试。

我记得有一次做多主机系统,两个MCU同时访问同一个从机,结果数据老是出错。查了半天,发现是仲裁逻辑没处理好。后来加了个总线占用检测,才解决问题。

仲裁规则:

  • 数据线SDA上的低电平优先
  • 失去仲裁的主机必须释放总线
  • 仲裁过程中不能丢失数据

本章知识体系

下面这张图,是我自己画的I2C协议知识体系。你看一遍,基本就能把整个协议串起来了。

I2C协议基础 物理层 SCL/SDA开漏输出 上拉电阻选择 速率模式 起始/停止条件 起始:SCL高→SDA下降沿 停止:SCL高→SDA上升沿 重复起始条件 数据帧格式 8位数据+应答位 地址+R/W位 ACK/NACK机制 地址类型 7位地址(常用) 10位地址(扩展) 地址冲突解决 时钟同步 SCL线与逻辑 慢速主机决定时钟 时钟拉伸 总线仲裁 低电平优先 多主机冲突解决 数据不丢失 掌握这些,I2C协议就算入门了

好了,第一章的内容就到这里。I2C协议看起来简单,但每个细节都值得深挖。下一章,咱们会深入Retimer芯片的I2C配置实战,到时候会用到今天讲的所有知识。


公众号:蓝海资料掘金营,微信deep3321