第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写一个字节:
- 主设备发送起始条件
- 发送设备地址0x50 + 写位0(即0xA0)
- 等待从设备应答
- 发送要写入的存储器地址(比如0x00)
- 等待从设备应答
- 发送要写入的数据(比如0x55)
- 等待从设备应答
- 主设备发送停止条件
注意:读写位为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,包括读写时序、页写入这些实战内容。记得动手实践,光看是学不会的。