2、I2C通信协议:从原理到实战
各位同学,今天我们来聊聊嵌入式系统里最常用的通信协议之一——I2C。说实话,我最早接触I2C是在做一款加速度传感器驱动的时候,那时候被时序图折腾得够呛。但搞懂了之后你会发现,I2C其实是个很优雅的协议。
2.1 I2C总线原理
I2C总线,全称Inter-Integrated Circuit,是飞利浦公司(现在的NXP)在80年代发明的。它只需要两根线:SDA(数据线)和SCL(时钟线)。嗯,就这么简单。
这两根线都是开漏输出,所以必须接上拉电阻。我习惯用4.7kΩ的电阻,但具体阻值要看总线电容和通信速率。你想想看,如果总线太长,电容大了,上拉电阻就得小一点,不然信号上升沿会变缓。
I2C总线上可以挂多个设备,每个设备都有唯一的地址。主机(通常是MCU)负责产生时钟,从机响应。说白了,就是主机喊话,从机听。
关键点:
- SDA和SCL都是双向的
- 空闲时,两根线都是高电平
- 每个从机有7位或10位地址
- 标准模式100kHz,快速模式400kHz
2.2 I2C时序
时序这东西,光看文字很难理解。我建议你配合逻辑分析仪来看,效果最好。这里我讲几个关键时序点。
起始条件:SCL高电平时,SDA从高变低。这个动作告诉所有从机:主机要开始通信了。
停止条件:SCL高电平时,SDA从低变高。告诉从机:通信结束。
数据传输:每个时钟周期传输1位数据。SCL低电平时,SDA可以变化;SCL高电平时,SDA必须稳定。我曾经在项目里犯过一个低级错误——在SCL高电平时改变了SDA,结果从机完全不响应,查了半天才发现是时序问题。
应答位:每传输完8位数据,接收方会在第9个时钟周期拉低SDA,表示「收到了」。如果没收到应答,主机可以发送停止条件或者重复起始条件。
我的小技巧:调试I2C时序时,先用示波器或逻辑分析仪抓一下波形。看看起始条件、停止条件、数据位和应答位是否都符合规范。很多时候,问题就出在这些细节上。
2.3 I2C读写操作
I2C的读写操作其实就两种:写和读。但组合起来有好几种模式。
写操作:
- 主机发送起始条件
- 主机发送从机地址 + 写位(0)
- 等待从机应答
- 主机发送寄存器地址
- 等待从机应答
- 主机发送数据字节
- 等待从机应答
- 重复第6-7步,直到数据发完
- 主机发送停止条件
读操作:
- 主机发送起始条件
- 主机发送从机地址 + 写位(0)
- 等待从机应答
- 主机发送要读取的寄存器地址
- 等待从机应答
- 主机发送重复起始条件
- 主机发送从机地址 + 读位(1)
- 等待从机应答
- 主机读取数据字节,并发送应答
- 重复第9步,直到数据读完
- 主机发送非应答,然后发送停止条件
为什么会这样?因为读操作需要先告诉从机你要读哪个寄存器,然后再切换成读模式。说白了就是「先写后读」。
注意:有些传感器支持连续读写,但有些不行。我遇到过一款气压传感器,连续读超过4个字节就会出错。所以,一定要看数据手册!
2.4 I2C在传感器中的应用
现在很多传感器都采用I2C接口,比如温度传感器、加速度计、陀螺仪、磁力计等。为什么?因为I2C只需要两根线,而且可以挂多个传感器。
以MPU6050六轴传感器为例,它的I2C地址是0x68(AD0引脚接地)或0x69(AD0引脚接VCC)。初始化流程一般是:
// 伪代码示例
i2c_start();
i2c_write(0x68 << 1 | 0); // 地址+写位
i2c_write(0x6B); // 电源管理寄存器
i2c_write(0x00); // 唤醒传感器
i2c_stop();
// 读取加速度数据
i2c_start();
i2c_write(0x68 << 1 | 0); // 地址+写位
i2c_write(0x3B); // 加速度数据起始地址
i2c_start(); // 重复起始条件
i2c_write(0x68 << 1 | 1); // 地址+读位
accel_x_h = i2c_read(1); // 读取并发送应答
accel_x_l = i2c_read(1);
accel_y_h = i2c_read(1);
accel_y_l = i2c_read(1);
accel_z_h = i2c_read(1);
accel_z_l = i2c_read(0); // 最后一个字节发送非应答
i2c_stop();
这段代码看起来简单,但实际调试时可能会遇到各种问题。比如从机不应答、数据错位、时序不对等。我曾经在一个项目里,因为上拉电阻选得太小,导致SDA信号失真,读出来的数据全是0xFF。后来换成4.7kΩ就好了。
避坑指南:
- 上拉电阻一般选2.2kΩ~10kΩ,具体看总线长度和速率
- 总线电容不要太大,否则信号会变形
- 多个从机地址不能冲突
- 从机地址通常是7位,左移1位后加上读写位
- 调试时先用逻辑分析仪抓波形,确认时序正确
好了,I2C协议就讲到这里。下一章我们会讲SPI协议,到时候对比着看,你会发现每种协议都有自己的特点。记住,没有最好的协议,只有最合适的协议。
本章要点回顾:
- I2C总线只需要SDA和SCL两根线
- 起始条件、停止条件、数据位、应答位是基本时序
- 读写操作遵循「先写地址,再读写数据」的流程
- 传感器应用中,注意从机地址、寄存器地址和连续读写限制