2、从机地址的硬件决定:硬件引脚配置与数据手册解读

I2C 从机地址怎么来的?很多人第一反应是「在驱动里写死一个 0x50 不就完了」。嗯,确实能跑,但换一块板子就翻车。我见过太多工程师拿着 A 板的驱动往 B 板上一烧,I2C 通信死活不通,最后发现是地址引脚电平没对上。

说白了,从机地址不是软件随便定的。它由硬件引脚决定,数据手册里写得清清楚楚。你读懂了手册,就拿到了地址的「最终解释权」。

2.1 地址引脚:硬件上的「拨码开关」

大多数 I2C 从机芯片会留出 1~3 个地址配置引脚,比如 A0、A1、A2。这些引脚接高电平(VDD)或低电平(GND),组合起来决定芯片的 7 位从机地址。

举个例子,一颗常见的温度传感器 TMP102:

  • 它只有一个地址引脚 ADD0
  • ADD0 接 GND → 地址 0x48
  • ADD0 接 VDD → 地址 0x49
  • ADD0 接 SDA → 地址 0x4A
  • ADD0 接 SCL → 地址 0x4B

你看,一个引脚就能变出 4 个地址。我在项目中遇到过一块板子上挂了 4 颗 TMP102,就是靠这 4 种接法区分开的。

关键点:地址引脚的电平必须在芯片上电前就确定。上电后芯片会锁存引脚状态,中途再改电平是无效的。

2.2 数据手册里的地址公式

数据手册一般会给一个地址计算表。我习惯先找到 「Slave Address」 或 「Address Map」 章节。里面通常长这样:

引脚配置 A2 A1 A0 7位地址 写地址 读地址
全部接 GND 0 0 0 0x50 0xA0 0xA1
A0 接 VDD 0 0 1 0x51 0xA2 0xA3
A1 接 VDD 0 1 0 0x52 0xA4 0xA5
A1、A0 接 VDD 0 1 1 0x53 0xA6 0xA7

注意看,7 位地址左移 1 位,最低位补 0 就是写地址,补 1 就是读地址。这是 I2C 协议的老规矩了。

我的习惯:拿到新芯片,第一件事就是复印地址表贴在工作台上。调试时不用翻手册,扫一眼就知道该发哪个地址。

2.3 硬件原理图怎么读地址

光看手册还不够,你得去读板子的原理图。找到芯片的 A0/A1/A2 引脚,看它们连到了哪里。

常见的接法有三种:

  1. 直接接 VDD 或 GND:最干脆,地址固定。
  2. 通过 10kΩ 电阻上拉或下拉:防干扰,本质还是固定电平。
  3. 悬空(NC):有些芯片内部有弱下拉,悬空等效于 GND。但我不建议依赖这个,万一芯片批次不同就翻车了。

我曾经调试一块温湿度传感器,原理图上 A0 标着「NC」,我以为默认是 0。结果通信一直失败,用逻辑分析仪抓出来地址是 0x4F 而不是 0x4E。后来查勘误表才发现,这颗芯片的 NC 引脚内部是弱上拉,等效于 1。从那以后,我遇到 NC 引脚都会用万用表量一下实际电平。

避坑指南:原理图上标「NC」不代表芯片内部没处理。一定要看数据手册的「Pin Description」里对 NC 引脚的说明。有些 NC 是「Do Not Connect」,有些是「Internal Pull-up/Pull-down」。

2.4 地址冲突:多设备共线时的噩梦

I2C 总线上每个从机地址必须唯一。如果两个设备地址一样,总线就会乱套。你发一个地址,两个设备同时应答,数据就打架了。

我遇到过最典型的情况:板子上用了两颗 EEPROM(AT24C02),原理图把 A0/A1/A2 都接了 GND。结果两颗芯片地址都是 0x50,读出来的数据全是乱码。最后只能飞线改其中一颗的 A0 到 VDD,地址变成 0x51 才解决。

所以设计阶段就要算好:

  • 总线上有几颗 I2C 设备?
  • 每颗设备有多少个可用地址?
  • 地址会不会重叠?

下面这张图帮你理清地址决策流程:

I2C 从机地址决策流程 拿到芯片数据手册 步骤1:找到 Address Map 章节 步骤2:确认地址引脚数量(A0/A1/A2) 步骤3:读取原理图引脚连接 地址冲突? 修改引脚配置或换芯片 地址确定,开始驱动移植 💡 小提示 用万用表量引脚电平 比看原理图更可靠

2.5 实战:从原理图到驱动地址

假设你拿到一块板子,上面有一颗 LM75A 温度传感器。原理图上 A0 接了 GND,A1 接了 VDD,A2 接了 GND。

查数据手册的地址表:

A2 A1 A0 7位地址
0 1 0 0x4A

那么写地址就是 0x94,读地址是 0x95。在 Linux 驱动里,你只需要填 7 位地址 0x4A:

static struct i2c_board_info lm75a_info = {
    .type = "lm75a",
    .addr = 0x4A,  // 7位地址,驱动框架会自动左移
};

或者用设备树:

&i2c1 {
    lm75a@4a {
        compatible = "national,lm75a";
        reg = <0x4A>;  // 注意这里是7位地址
    };
};

注意:设备树里 reg 属性填的是 7 位地址,不是 8 位读写地址。内核会自己处理左移和读写位。你填错了就等着抓狂吧。

2.6 常见坑点总结

  • 坑点 1:数据手册里的地址是 7 位还是 8 位?有些手册直接给 8 位地址(比如 0xA0),你得自己右移 1 位再填到驱动里。
  • 坑点 2:地址引脚有内部上拉/下拉,原理图没画出来。用万用表量一下最保险。
  • 坑点 3:同一颗芯片不同批次,地址引脚内部处理可能不一样。我吃过这个亏,后来都养成了「到手先量」的习惯。
  • 坑点 4:有些芯片支持「软件地址编程」,通过写入某个寄存器来改地址。这种芯片上电后默认地址是固定的,你得先发默认地址去改它。

地址搞定了,下一步就是时钟拉伸。嗯,那是另一个故事了。