第1章:I2C通信协议详解

各位同学,咱们今天聊聊I2C。说实话,这玩意儿在美容仪里太常见了。EEPROM用它,传感器用它,甚至有些马达驱动芯片也用它。我做了这么多年嵌入式,I2C可以说是打交道最多的协议之一。

1.1 I2C总线原理

I2C总线,说白了就是两根线搞定一切通信。一根叫SCL(时钟线),一根叫SDA(数据线)。你想想看,就两根线,却能挂几十个设备,是不是很神奇?

这两根线都是开漏输出,需要外接上拉电阻。我刚开始做项目时,有次忘了加上拉电阻,结果通信死活不通。查了半天,最后发现是这个问题。嗯,这里要注意,上拉电阻一般选4.7kΩ左右,具体要看总线电容和通信速率。

I2C总线上有两种角色:主设备和从设备。主设备负责产生时钟信号,发起通信。从设备就是被动响应。在美容仪里,主设备通常是MCU,从设备就是EEPROM、传感器这些。

关键点:每个I2C从设备都有一个唯一的7位地址。比如我们常用的AT24C02 EEPROM,地址一般是0x50(1010000)。

1.2 起始条件与停止条件

I2C通信的开始和结束,都有特定的信号。我习惯叫它们「握手信号」。

起始条件:SCL为高电平时,SDA从高电平跳变到低电平。

停止条件:SCL为高电平时,SDA从低电平跳变到高电平。

为什么这么设计?因为SCL高电平时,SDA本应保持稳定。一旦SDA变化,就表示有特殊含义。这个设计很巧妙,对吧?

我的经验:写代码时,起始条件和停止条件的时序一定要严格。我曾经遇到一个坑:起始条件后没有等足够的时间就发数据,结果从设备没反应过来,数据全丢了。后来我习惯在起始条件后加一个微秒级的延时。

1.3 数据帧格式

I2C的数据帧格式其实很简单。每次传输8位数据,后面跟一个应答位。我给大家拆解一下:

起始条件 | 从设备地址(7位) + 读写位(1位) | 应答位 | 数据字节(8位) | 应答位 | ... | 停止条件

举个例子,我们要往EEPROM写一个字节:

  1. 主设备发送起始条件
  2. 发送设备地址0x50 + 写位0(即0xA0)
  3. 等待从设备应答
  4. 发送要写入的存储器地址(比如0x00)
  5. 等待从设备应答
  6. 发送要写入的数据(比如0x55)
  7. 等待从设备应答
  8. 主设备发送停止条件

注意:读写位为0表示写操作,为1表示读操作。这个别搞反了,我刚开始学的时候就犯过这个错。

1.4 应答机制

应答机制是I2C可靠性的保证。每次主设备发送完8位数据后,会释放SDA线,然后从设备拉低SDA表示应答(ACK),或者保持高电平表示非应答(NACK)。

为什么会收到NACK?常见原因有:

  • 从设备地址不对
  • 从设备正忙,没来得及响应
  • 数据传完了,从设备不想再接收

避坑指南:我曾经遇到一个情况,EEPROM正在写数据时,我连续发命令,结果一直收到NACK。后来查手册才知道,EEPROM写操作需要5-10ms的写入时间。在这期间,它不会响应任何命令。所以写完后一定要加延时,或者轮询等待它准备好。

1.5 实际应用中的注意事项

做美容仪项目时,I2C通信有几个坑要特别注意:

问题 原因 解决方法
总线锁死 SDA被从设备一直拉低 发送9个时钟脉冲复位从设备
通信速率不匹配 主从设备速率不一致 统一使用100kHz或400kHz
地址冲突 两个设备地址相同 检查硬件地址引脚配置

我个人习惯,在初始化I2C时,先发一个停止条件,确保总线处于空闲状态。这个小技巧帮我避免了很多莫名其妙的问题。

实用技巧:调试I2C时,用逻辑分析仪看波形是最直观的。我每次调I2C都会先抓波形,确认起始条件、地址、数据、应答都正确,再写上层代码。这样能省很多时间。

好了,I2C的基础知识就讲到这里。下一章我们会具体讲怎么在美容仪里用I2C操作EEPROM,包括读写时序、页写入这些实战内容。记得动手实践,光看是学不会的。