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位地址的传输方式有点绕。它分两个阶段:

  1. 第一阶段:发11110xx + R/W,其中xx是10位地址的高2位。这其实是一个“保留头码”,告诉总线上所有从机:接下来我要发10位地址了。
  2. 第二阶段:发剩下的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地址配置与冲突检测流程。每次调试新板子,我都会按这个步骤走一遍。

I2C从机地址配置与冲突检测流程 开始地址配置 步骤1:查阅数据手册,确定地址位数 (7位还是10位?地址引脚如何配置?) 步骤2:硬件配置地址引脚 (上拉/下拉电阻固定,避免悬空) 步骤3:软件发送地址,检查ACK (逐个枚举0x01~0x7F,记录应答) 有冲突? 是 冲突处理: 修改地址引脚/更换设备 否 无冲突: 地址配置完成 步骤4:验证通信 (读写寄存器,确认数据正确) 配置完成 提示 • 通用呼叫地址0x00不可用作普通地址 • 10位地址需分两阶段发送 • 地址冲突时,SDA应答位会出现毛刺

4.6 常见问题与排查思路

最后,我把自己这些年遇到的地址相关问题整理了一下。你调试的时候,可以对照着查:

现象 可能原因 排查方法
发送地址后无ACK 地址错误、从机未上电、SCL/SDA线序反了 用示波器抓波形,确认地址字节内容
有时有ACK,有时没有 地址冲突、总线竞争、上拉电阻太小 逐个断开从机,确认哪个设备在应答
读数据全是0xFF 从机未应答,主机读到的是总线高电平 检查从机电源和复位引脚
写数据后读回不对 地址左移/右移搞反了 对照数据手册,确认地址格式