1. I2C协议基础:物理层、协议层、起始条件、停止条件、数据有效性、应答机制
各位同学,咱们正式开始这门课之前,先把I2C协议的老底儿翻出来晒晒。你可能会想:“I2C协议嘛,谁不知道?” 但说实话,很多人在STM32上栽跟头,恰恰就是这些基础概念没吃透。我见过太多工程师拿着逻辑分析仪抓波形,却看不懂为什么设备不应答——说白了,就是协议理解不到位。
1.1 物理层:两条线怎么干活?
I2C总线就两条线:SCL(时钟线)和SDA(数据线)。嗯,就这么简单。但这两条线的电气特性,决定了整个协议的脾气。
- 开漏输出:SCL和SDA都是开漏结构。这意味着什么?芯片只能把线拉低,不能主动拉高。拉高靠上拉电阻。
- 线与逻辑:多个设备挂在同一条线上,谁都可以拉低。如果有一个设备拉低了,整条线就是低电平。这就是“线与”——低电平优先。
- 上拉电阻:一般4.7kΩ或10kΩ。总线速度越高,电阻要越小。我做过一个项目,总线长度半米,用了2.2kΩ才稳定。
| 参数 | 标准模式 (100kHz) | 快速模式 (400kHz) | 高速模式 (3.4MHz) |
|---|---|---|---|
| 上拉电阻典型值 | 10kΩ | 4.7kΩ | 1.5kΩ |
| 总线电容上限 | 400pF | 400pF | 100pF |
| 最大从设备数量 | 127(7位地址) | 127 | 127 |
1.2 协议层:谁说话,谁听话?
I2C是主从结构。总线上只有一个主设备(Master),其他都是从设备(Slave)。主设备负责产生时钟、发起通信、决定什么时候结束。从设备只能被动响应。
你想想看,如果两个设备同时想当主设备,怎么办?I2C有仲裁机制。当多个主设备同时拉低SDA时,谁先释放谁就输了。这个机制保证了总线上不会乱套。
关键点:在STM32的硬件I2C外设中,仲裁是由硬件自动处理的。但如果你用软件模拟I2C,就得自己写仲裁逻辑——我建议新手别碰这个坑。
1.3 起始条件与停止条件
这两个条件是I2C通信的“开关”。没有它们,总线上的设备不知道什么时候开始、什么时候结束。
- 起始条件(S):SCL为高电平时,SDA从高电平跳变到低电平。
- 停止条件(P):SCL为高电平时,SDA从低电平跳变到高电平。
为什么这样设计?因为数据在SCL低电平时变化,高电平时采样。起始和停止条件利用了SCL高电平时SDA的变化——这是一种“带外信号”,不会和数据混淆。
1.4 数据有效性:什么时候读数据?
I2C的数据传输有严格的时序要求:
- SCL低电平时:发送设备改变SDA电平。
- SCL高电平时:接收设备读取SDA电平。
每个字节8位,高位在前(MSB first)。传输完8位后,发送设备释放SDA,接收设备在第9个时钟周期拉低SDA作为应答。
说白了,就是“低电平时随便改,高电平时别乱动”。这个规则保证了数据在采样时刻是稳定的。
1.5 应答机制:通信的握手
应答是I2C协议中最容易被忽视、却又最容易出问题的环节。
- ACK(应答):接收设备在第9个时钟周期拉低SDA,表示“收到,请继续”。
- NACK(非应答):接收设备在第9个时钟周期保持SDA高电平,表示“没收到”或“不想收了”。
什么时候会出现NACK?
- 从设备地址不匹配
- 从设备正忙,无法响应
- 主设备读数据时,读完最后一个字节后发送NACK,告诉从设备“别发了”
1.6 知识结构总览
下面这张图把I2C协议的核心要素串起来了。你可以把它当作调试时的“检查清单”。
1.7 一个完整的字节传输示例
咱们来看一个实际的传输过程。假设主设备要向地址为0x50的从设备写一个字节0xA5:
- 主设备发送起始条件(S)
- 主设备发送7位地址 + 写位:0x50 << 1 | 0 = 0xA0
- 从设备在第9个时钟拉低SDA,发送ACK
- 主设备发送数据字节:0xA5
- 从设备再次发送ACK
- 主设备发送停止条件(P)
整个过程,SCL一共产生了18个脉冲(9个用于地址,9个用于数据)。
记住这个流程:S → 地址+读写位 → ACK → 数据 → ACK → ... → P。这是I2C通信的“标准句式”,所有操作都基于这个模板。
1.8 写在最后
I2C协议看起来简单,但细节决定成败。我见过有人把上拉电阻焊成0Ω,结果总线一直拉不上去;也见过有人把从设备地址搞反了,调了一整天。嗯,这些坑咱们后面都会讲到怎么避免。