1. I2C总线物理层与协议层基础:SDA/SCL线、开漏输出、上拉电阻、总线仲裁
各位同学,咱们今天聊I2C最底层的那些事儿。说实话,搞了这么多年嵌入式,我见过太多工程师上来就调代码,结果示波器一挂,波形乱七八糟,根本不知道问题出在哪。说白了,物理层没吃透,后面全是坑。
这一节,咱们就把I2C的“地基”打牢。你想想看,两根线就能挂几十个设备,还能双向通信,这背后是有道理的。
1.1 两线制:SDA与SCL的分工
I2C总线就两条线:SDA(串行数据线)和SCL(串行时钟线)。
- SDA:负责传数据,地址、寄存器号、读写数据都走这根线。
- SCL:负责传时钟,由主设备产生,从设备跟着这个节奏走。
我刚开始学的时候有个误区,以为SDA和SCL是“对称”的。其实不是。SCL永远是主设备在驱动,从设备只能拉低它(时钟拉伸),但绝不能主动产生时钟。这一点,你在调试时一定要记住。
1.2 开漏输出:为什么非它不可?
I2C的SDA和SCL引脚,内部都是开漏输出结构。什么叫开漏?说白了,就是MOS管的漏极直接接到引脚上,没有内部上拉。管子导通时,引脚被拉到GND(低电平);管子截止时,引脚浮空(高阻态)。
为什么要这么设计?两个原因:
- 多设备“线与”:多个设备的开漏输出可以直接连在一起。只要有一个设备拉低,总线就是低电平。所有设备都不拉低,总线才是高电平。这就实现了“多主仲裁”的基础。
- 电平转换方便:你可以给总线配上3.3V的上拉电阻,那高电平就是3.3V;配上5V的上拉,高电平就是5V。同一个芯片,换个上拉电压就能接不同电平的系统。
嗯,这里要注意:开漏输出只能输出低电平,不能主动输出高电平。高电平全靠外部上拉电阻提供。所以,如果你发现SDA或SCL一直低电平拉不上去,八成是上拉电阻没焊,或者阻值太小了。
1.3 上拉电阻:阻值选不对,通信全白费
上拉电阻是I2C总线的“命根子”。阻值选大了,上升沿太缓,高速通信时波形畸变;选小了,灌电流太大,芯片可能烧掉。
标准I2C规范里,上拉电阻的推荐范围是1kΩ ~ 10kΩ。但实际选型要看几个因素:
| 总线电容 | 通信速率 | 推荐上拉电阻 |
|---|---|---|
| < 100 pF | 100 kHz(标准模式) | 4.7kΩ ~ 10kΩ |
| 100 ~ 200 pF | 400 kHz(快速模式) | 2.2kΩ ~ 4.7kΩ |
| 200 ~ 400 pF | 1 MHz(快速+模式) | 1kΩ ~ 2.2kΩ |
我曾经在一个项目里,板子上挂了8个I2C从设备,总线长度接近30cm。一开始用了10kΩ上拉,结果400kHz下波形上升沿都快赶上半个时钟周期了。换成2.2kΩ,问题立刻解决。所以,总线电容越大、速率越高,上拉电阻就要越小。
1.4 总线仲裁:多主设备怎么不打架?
I2C支持多主设备。也就是说,总线上可以有好几个主设备,谁先发起通信,谁就占用总线。但如果两个主设备同时发起呢?这就靠总线仲裁来解决。
仲裁的原理很简单:
- 每个主设备在发送数据时,都会同时监测SDA的电平。
- 如果自己发送的是高电平,但SDA上却是低电平(被另一个主设备拉低了),那它就知道自己“输”了。
- 输掉仲裁的主设备立即停止发送,转为从设备模式,等待下一次机会。
你想想看,这个机制依赖的就是开漏输出和上拉电阻。没有开漏,多个设备同时输出高低电平,直接就短路了。有了开漏,才能实现“线与”,才能让仲裁顺理成章。
下面这张图,把I2C物理层和协议层的关系梳理了一下,你看完应该能有个整体印象。
你看,物理层提供了“线与”的能力,协议层才得以实现仲裁。这两层是相辅相成的。搞懂了这些,后面讲时序、讲驱动,你才能知道为什么代码要那么写,为什么波形要那样看。