3、I2C通信协议详解:I2C总线原理、主从模式、时序分析、速率配置(100kHz/400kHz)、光模块中I2C的应用场景(读取温度、电压、控制TEC)
做光模块固件开发,I2C 是你绕不开的坎儿。说实话,我见过太多新手在 I2C 上栽跟头——要么读回来的温度跳来跳去,要么 TEC 控制根本不起作用。其实搞懂了 I2C 的本质,这些问题都能迎刃而解。
3.1 I2C 总线原理:两根线怎么干活?
I2C 总线就两根线:SCL(时钟线)和 SDA(数据线)。但你别小看这两根线,它们用一套精巧的协议,就能让多个设备互相通信。
核心思想很简单:SCL 负责节奏,SDA 负责数据。SCL 每跳一次,SDA 就送一个 bit 出去。我习惯把 SCL 想象成节拍器,SDA 就是跟着节拍跳舞的人。
总线上的设备都是开漏输出,通过上拉电阻拉到高电平。这意味着:谁想说话,谁就把线拉低;不说话的时候,线自动回到高电平。这个设计有个好处——多个设备不会打架,因为谁拉低谁就占住了总线。
重要概念:I2C 总线空闲时,SCL 和 SDA 都是高电平。这是判断总线是否被占用的基本依据。
3.2 主从模式:谁说了算?
I2C 通信分主设备和从设备。主设备负责发起通信、产生时钟、决定什么时候开始什么时候结束。从设备只能被动响应。
在光模块里,MCU 永远是主设备,光模块内部的 DAC、温度传感器、TEC 控制器都是从设备。每个从设备都有一个唯一的 7 位地址,比如温度传感器可能是 0x48,DAC 可能是 0x50。
我遇到过一个问题:两个从设备地址冲突了。查了半天才发现,厂家默认地址一样,但其中一个可以通过引脚配置改地址。嗯,选型的时候一定要确认地址不冲突。
3.3 时序分析:看懂波形图
I2C 的时序其实不复杂,就几个关键动作:
- 起始条件:SCL 高电平时,SDA 从高变低
- 停止条件:SCL 高电平时,SDA 从低变高
- 数据采样:SCL 低电平时改变 SDA,SCL 高电平时采样 SDA
- 应答位:每发送 8 个 bit 后,接收方拉低 SDA 表示收到
你想想看,起始和停止条件都是 SCL 高电平时发生的,而数据传输时 SCL 高电平期间 SDA 不能变。这个设计很巧妙,把控制信号和数据信号分开了。
我的经验:调试时用逻辑分析仪抓波形,重点看起始条件、停止条件和应答位。90% 的 I2C 问题都能从波形上看出来。
3.4 速率配置:100kHz 还是 400kHz?
I2C 标准模式是 100kHz,快速模式是 400kHz。光模块里常用 400kHz,因为要读取的数据量不小。
但速率不是越高越好。我踩过一个坑:把 I2C 速率设到 400kHz,结果总线电容太大,波形都变形了。后来老老实实降到 100kHz,问题就解决了。
配置速率时要注意几点:
- 上拉电阻要匹配:400kHz 时上拉电阻要小一些,一般 2.2kΩ 到 4.7kΩ
- 总线电容要控制:走线不要太长,从设备不要太多
- 从设备支持:不是所有从设备都支持 400kHz,查数据手册确认
| 模式 | 频率 | 上拉电阻推荐值 | 典型应用 |
|---|---|---|---|
| 标准模式 | 100kHz | 4.7kΩ - 10kΩ | 低速传感器、EEPROM |
| 快速模式 | 400kHz | 2.2kΩ - 4.7kΩ | 光模块内部通信 |
3.5 光模块中的 I2C 应用场景
光模块里 I2C 主要干三件事:读温度、读电压、控制 TEC。我一个个说。
3.5.1 读取温度
光模块内部有个温度传感器,通过 I2C 读取。典型流程是:
- 主设备发送起始条件
- 发送从设备地址 + 写位(0x90)
- 发送寄存器地址(比如 0x00)
- 重新发送起始条件
- 发送从设备地址 + 读位(0x91)
- 读取两个字节的温度数据
- 发送停止条件
读回来的数据一般是 16 位,需要换算成实际温度。不同传感器换算公式不一样,我习惯在代码里写个宏定义,方便调试时改。
// 温度读取示例代码
uint16_t read_temperature(void) {
uint8_t buf[2];
i2c_start();
i2c_write(0x90); // 地址 + 写
i2c_write(0x00); // 寄存器地址
i2c_restart();
i2c_write(0x91); // 地址 + 读
buf[0] = i2c_read(ACK);
buf[1] = i2c_read(NACK);
i2c_stop();
return (buf[0] << 8) | buf[1];
}
3.5.2 读取电压
电压读取和温度类似,只是寄存器地址不同。光模块里一般监控 3.3V、5V 等电源电压。我建议在初始化时先读一次电压,作为基准值,后面读到的电压和基准值比较,判断是否异常。
注意:电压读取要加滤波。我遇到过电源纹波导致电压读数跳变,加了个简单的滑动平均滤波就好了。
3.5.3 控制 TEC
TEC 控制是光模块里最复杂的 I2C 应用。TEC 控制器一般是个 DAC,通过 I2C 设置输出电压,控制 TEC 的制冷或加热。
控制流程大致是:
- 读取当前温度
- 和目标温度比较
- 计算需要调整的电压值
- 通过 I2C 写入 DAC
我曾经在 TEC 控制上吃过亏:写入 DAC 的频率太快,导致 TEC 来回震荡。后来加了死区控制和限幅,才稳定下来。说白了,TEC 控制是个 PID 问题,I2C 只是传输通道,算法才是关键。
3.6 知识体系总览
下面这张图把 I2C 在光模块中的应用串起来了,你可以对照着看:
这张图把 I2C 在光模块中的角色说清楚了。MCU 通过 I2C 总线连接温度传感器、电压监测和 TEC 控制器,完成数据采集和控制输出。说白了,I2C 就是光模块的神经系统,把各个部件串起来。
调试建议:刚开始做 I2C 驱动时,先单独测试每个从设备。温度能读对了,再读电压,最后才搞 TEC 控制。一步步来,出问题也好定位。
好了,I2C 协议这部分就讲到这里。记住:时序是基础,速率要匹配,应用要灵活。你在项目里遇到 I2C 问题,多半能从这三方面找到原因。
公众号:蓝海资料掘金营,微信deep3321