4. I2C从机地址配置:7位地址与10位地址格式、地址冲突检测、通用呼叫地址
地址这个东西,是I2C通信的第一步。地址错了,后面全是白搭。我见过不少工程师,调了半天I2C,波形都抓出来了,就是没响应。最后发现——从机地址配错了。嗯,今天咱们就把地址这块彻底讲透。
4.1 7位地址格式:最常用的寻址方式
I2C最经典的寻址方式就是7位地址。说白了,主机发一个起始条件后,紧跟着发7位地址 + 1位读写位。这8位数据,就是整个通信的“门牌号”。
7位地址帧结构:
- S:起始条件(Start Condition)
- A6~A0:7位从机地址,高位在前
- R/W:读写标志位(0=写,1=读)
- ACK:从机应答位
举个例子。你买了一个温度传感器,数据手册说它的7位地址是0x48。那你在代码里怎么发?
// 假设7位地址为0x48,写操作
uint8_t addr = 0x48 << 1; // 左移1位,变成0x90
// 或者直接写:0x90
// 读操作:0x48 << 1 | 0x01 = 0x91
这里有个坑,我刚开始做的时候也犯过。很多芯片的数据手册里写的地址,是7位原始值。但你在代码里发的时候,必须左移一位,再拼上读写位。你想想看,如果直接发0x48,那从机收到的地址其实是0x24——完全对不上。
我的习惯:在代码里用宏定义把7位地址和读写位分开写,清晰明了。
#define SLAVE_ADDR_7BIT 0x48
#define I2C_WRITE 0
#define I2C_READ 1
// 写操作地址
uint8_t addr_w = (SLAVE_ADDR_7BIT << 1) | I2C_WRITE;
// 读操作地址
uint8_t addr_r = (SLAVE_ADDR_7BIT << 1) | I2C_READ;
4.2 10位地址格式:什么时候用?怎么用?
7位地址最多支持112个设备(0x00~0x07保留)。对于大多数板级设计,够用了。但有些场景——比如复杂的传感器网络、多通道ADC阵列——112个地址不够用。这时候就需要10位地址。
10位地址的传输方式有点绕。它分两个阶段:
- 第一阶段:发11110xx + R/W,其中xx是10位地址的高2位。这其实是一个“保留头码”,告诉总线上所有从机:接下来我要发10位地址了。
- 第二阶段:发剩下的8位地址(低8位)。
10位地址帧结构:
- S:起始条件
- 11110 A9 A8:保留头码 + 地址高2位
- R/W:读写位
- ACK:从机应答
- A7~A0:地址低8位
- ACK:从机应答
我记得有一次调试一个多路复用器,它用了10位地址。我一开始没仔细看数据手册,按7位地址去发,死活没应答。后来抓波形一看,才发现前两个字节的格式完全不对。
注意:10位地址的从机,在应答第一个字节时,所有从机都会检查11110头码。只有高2位匹配的从机,才会继续等待第二个字节。其他从机直接忽略后续数据。
4.3 地址冲突检测:别让两个设备抢同一个门牌号
地址冲突,是I2C调试中最常见的问题之一。两个从机配了相同的地址,主机一呼叫,两个设备同时应答——总线就乱了。
怎么检测?我一般用这几个方法:
| 方法 | 操作 | 判断依据 |
|---|---|---|
| 逐个枚举 | 依次发送0x00~0x7F地址,看哪个有ACK | 同一个地址出现两次ACK |
| 波形对比 | 抓取SCL/SDA波形,观察应答位 | 应答位出现毛刺或电平不确定 |
| 读ID寄存器 | 发送地址后,读设备ID寄存器 | 返回的ID与预期不符 |
我曾经在一个项目里,用了两片相同的EEPROM,地址引脚都接地了——结果两个都是0x50。写数据的时候没问题,读数据的时候数据全乱套了。后来我加了一个地址选择引脚,一个拉高一个拉低,才解决。
避坑指南:设计原理图时,就把地址引脚用电阻上拉或下拉固定好。别留空,也别用跳线帽——生产的时候工人可能忘了跳。
4.4 通用呼叫地址:广播通信的秘密武器
通用呼叫地址(General Call Address)是0x00。所有支持通用呼叫的从机,收到这个地址后都会应答。它主要用于:
- 主机广播数据:比如更新所有从机的配置参数
- 从机地址分配:某些智能从机可以通过通用呼叫获取动态地址
- 系统复位:发送特定数据让所有从机复位
通用呼叫的第二个字节决定了具体行为:
| 第二个字节 | 含义 |
|---|---|
| 0x04 | 复位所有从机 |
| 0x06 | 写入可编程地址 |
| 0x08~0xFF | 保留或厂商自定义 |
不过要注意,不是所有从机都支持通用呼叫。数据手册里一般会写“Supports General Call”或者“GC Support”。不支持的话,你发0x00它也不会理你。
重要:通用呼叫地址0x00是保留地址,不能用作普通从机地址。如果你在总线上看到0x00有应答,别高兴太早——那可能是所有从机都在响应广播,而不是某个特定设备。
4.5 地址配置实战:一个完整的流程图
下面这张图,是我自己总结的I2C地址配置与冲突检测流程。每次调试新板子,我都会按这个步骤走一遍。
4.6 常见问题与排查思路
最后,我把自己这些年遇到的地址相关问题整理了一下。你调试的时候,可以对照着查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 发送地址后无ACK | 地址错误、从机未上电、SCL/SDA线序反了 | 用示波器抓波形,确认地址字节内容 |
| 有时有ACK,有时没有 | 地址冲突、总线竞争、上拉电阻太小 | 逐个断开从机,确认哪个设备在应答 |
| 读数据全是0xFF | 从机未应答,主机读到的是总线高电平 | 检查从机电源和复位引脚 |
| 写数据后读回不对 | 地址左移/右移搞反了 | 对照数据手册,确认地址格式 |