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 读取。典型流程是:

  1. 主设备发送起始条件
  2. 发送从设备地址 + 写位(0x90)
  3. 发送寄存器地址(比如 0x00)
  4. 重新发送起始条件
  5. 发送从设备地址 + 读位(0x91)
  6. 读取两个字节的温度数据
  7. 发送停止条件

读回来的数据一般是 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(主设备) SCL/SDA 从设备(光模块内部) 温度传感器 地址:0x48 读取温度值 电压监测 地址:0x4A 读取电压值 TEC 控制器 地址:0x50 写入控制电压 应用层:温度监控 / 电压保护 / TEC 温控 通过 I2C 协议与从设备交互 I2C 总线连接 MCU 与多个从设备,实现数据采集与控制

这张图把 I2C 在光模块中的角色说清楚了。MCU 通过 I2C 总线连接温度传感器、电压监测和 TEC 控制器,完成数据采集和控制输出。说白了,I2C 就是光模块的神经系统,把各个部件串起来。

调试建议:刚开始做 I2C 驱动时,先单独测试每个从设备。温度能读对了,再读电压,最后才搞 TEC 控制。一步步来,出问题也好定位。

好了,I2C 协议这部分就讲到这里。记住:时序是基础,速率要匹配,应用要灵活。你在项目里遇到 I2C 问题,多半能从这三方面找到原因。


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