做嵌入式这些年,I2C 协议是绕不开的基本功。按键、EEPROM、传感器、显示驱动、电源管理芯片,大量外设走的就是这两根线。相比 SPI 和 UART,I2C 用最少的引脚把多个设备串在同一条总线上,接线省、地址可扩展、自带应答机制,看起来简单,真正用到深处才发现水很深。信号时序怎么判、地址到底是 7 位还是 8 位、速率为什么上不去、读写事务为什么读出来全是 FF,这些问题如果只靠翻手册,往往越查越迷糊。这篇文章打算把 I2C 协议里的信号、地址、速率、读写四个维度一次说透,从电气基础讲到实际调试,适合正在写驱动的人,也适合那些已经跑通例程、想回头补底层细节的工程师。
1. 两根线的总线:I2C 的电气基础与主从模型
1.1 SCL 与 SDA 的分工,以及"主从"到底指什么
I2C 只有两根信号线:SCL(串行时钟)和 SDA(串行数据)。SCL 由主设备产生,负责给整条总线提供节奏;SDA 负责在主机和从机之间双向传数据。注意这里的"双向"不是两根线各管一个方向,而是同一根线分时复用:主机发送时它当输出,主机读取时它当输入。
总线上挂着多个设备,谁发起传输谁就是主设备(Master),被寻址的那个就是从设备(Slave)。主设备负责产生时钟、发起起始条件、发送设备地址、决定读还是写;从设备则被动监听地址,匹配上了就参与传输。I2C 还允许总线上有多个主设备,这就是所谓多主模式,但实际项目里用到的非常少,因为仲裁逻辑做起来麻烦,多数场景就是一个主 MCU 挂一串从机。
1.2 开漏输出与上拉电阻:为什么 I2C 必须用这种结构
I2C 的信号线不是普通的推挽输出,而是开漏输出。开漏的意思是,设备只能主动把线拉低,不能主动把线拉高。线要恢复高电平,靠的是外部接在 VDD 和信号线之间的上拉电阻。所以 I2C 总线上必须要有上拉电阻,这是新手最容易忽略的一点。
这种"只能拉低、靠电阻拉高"的结构,本质上是线与逻辑。任何设备都可以在不影响其他设备的前提下把线拉低,多个设备同时操作也不会产生短路冲突。如果换成推挽输出,两个设备一个想拉高、一个想拉低,就会直接打架,轻则信号错误,重则烧 IO。
开漏结构还带来一个好处:因为有外部上拉电阻,3.3V 的主机可以直接和 5V 的从机挂在一起,前提是从机引脚允许 3.3V 电平触发,且上拉电阻接到合适电压。但电平兼容这件事不能想当然,后面调试章节会专门讲。
1.3 和 SPI、UART 放一起看:I2C 的优势与代价
做了这么久驱动,我自己的体感是:I2C 在三者之间属于"中庸"的选择。SPI 是全双工、推挽输出,速率能跑几十甚至上百 MHz,但通常需要至少四根线(SCK、MOSI、MISO、CS),且一个从机就要一根片选线,设备一多布线就痛苦。UART 只有两根线,但是点对点,天然不适合一主多从。
I2C 的优势是:两根线支持多个设备,从机不需要片选,设备通过地址区分,自带 ACK 应答能立刻知道从机有没有收到数据。代价也很明显:速率上限远不如 SPI,数据线上有应答机制,时序有严格的建立保持时间要求,总线上电容一大信号就难看,距离稍长就不稳定。
| 对比项 | I2C | SPI | UART |
|---|---|---|---|
| 信号线数量 | 2(SCL+SDA) | 3~4+(每从机一根 CS) | 2(TX+RX) |
| 传输方向 | 半双工 | 全双工 | 全双工 |
| 多从机支持 | 地址寻址 | 片选寻址 | 不支持 |
| 自带应答 | 有 ACK/NACK | 无 | 无 |
| 典型速率 | 100k~3.4M | 几十 MHz | 115200 等 |
| 抗干扰距离 | 弱,适合板内 | 弱 | 稍强 |
选型时如果只是读写少量寄存器,I2C 最舒服;如果要搬大量数据,比如把图像或音频流送到外设,SPI 才是正解。
2. 信号时序的底层规则:起始、停止、数据位与应答位
2.1 起始条件和停止条件:先看 SCL,再看 SDA
I2C 总线上所有传输都从起始条件(START)开始,以停止条件(STOP)结束。这两个条件的判定不看电平高低,而是看 SDA 在 SCL 高电平期间的跳变方向:
- 起始条件:SCL 保持高电平时,SDA 从高跳变到低。
- 停止条件:SCL 保持高电平时,SDA 从低跳变到高。
为什么要强调"SCL 高电平期间"?因为正常的数据位传输不允许在 SCL 高电平期间改变 SDA(下一节会讲),所以起始和停止恰恰是两种"例外"跳变,协议用它们来标记边界。这也是为什么代码里执行 START 时顺序必须是 SDA_HIGH → SCL_HIGH → SDA_LOW → SCL_LOW,不能写成 SCL 先跳再跳 SDA,顺序错误在示波器上会看到怪波形。
用 GPIO 模拟时,标准起始序列是这样的:
void i2c_start(void) { SDA_HIGH(); SCL_HIGH(); SDA_LOW(); // SCL 高电平期间 SDA 下降沿 = START SCL_LOW(); } void i2c_stop(void) { SDA_LOW(); SCL_HIGH(); SDA_HIGH(); // SCL 高电平期间 SDA 上升沿 = STOP }还有一个概念叫重复起始条件(Repeated START):不先发 STOP,而是在一次传输中间再次拉出 START。读寄存器类芯片几乎必用它,第五部分会详细展开。
2.2 数据有效性规则:高电平期间 SDA 必须保持稳定
I2C 每个数据位都遵循一条铁律:在 SCL 为高电平的期间,SDA 必须保持稳定;只有 SCL 为低电平时,SDA 才允许变化。
接收方(无论主机还是从机)都是在 SCL 上升沿附近采样 SDA 的电平。如果你在 SCL 高电平期间把 SDA 改了,接收方采到的电平就不确定,轻则这一位读错,重则整个字节移位。所以写位操作的正确姿势是:先把 SDA 设置好,再拉高 SCL 让接收方采样,采样完再拉低 SCL。这个顺序和上一节 START 的顺序正好形成对照——刚才是跳变,现在是稳定。
数据位按 MSB first 顺序传输,也就是先发最高位。一个字节 8 位,发送时逐位循环:设置 SDA → 拉高 SCL → 拉低 SCL → 设置下一位。这就是为什么"改变 SDA 必须在 SCL 低电平期间"这句话在代码里几乎体现在每一行。
2.3 应答位 ACK/NACK:第九个时钟背后的握手逻辑
I2C 的字节传输不是发完 8 位就完了。每发完一个字节,发送方要释放 SDA,接收方在第 9 个时钟脉冲期间把 SDA 拉低,表示"我收到了",这就是 ACK。
逻辑是这样的:第 9 个时钟到来前,发送方把 SDA 释放成高电平(靠上拉电阻拉高);如果接收方正常,它会主动把 SDA 拉低,主机在第 9 个时钟的高电平期间读取 SDA,读到低电平就是 ACK,读到高电平就是 NACK。NACK 可能是接收方忙、地址不存在、数据错误,也可能是主机主动发出的"别再发了"信号——读操作最后一个字节前,主机会故意回 NACK,告诉从机后面不需要了。
从机不发 ACK 的典型场景是 EEPROM 内部写周期。24C02 这类芯片在写入数据后需要几毫秒把数据烧进存储单元,这段时间你发任何命令它都不应答。所以可靠的写流程要么等够 tWR 时间,要么轮询发送直到收到 ACK。
2.4 时钟拉伸:从机的"刹车"机制
开漏结构还支撑了一个独特功能:时钟拉伸。SCL 虽然是主设备产生的,但从机也可以在需要时把 SCL 拉低,让主设备无法继续运行。因为开漏 + 线与,从机拉低 SCL 后,即使主机想拉高也拉不起来,只能等待。
典型场景是从机还没准备好数据,比如某些温湿度传感器在转换期间主动拉伸时钟,让主机等它。主设备侧如果不处理时钟拉伸,直接按固定延时产生时钟,就可能读到半截数据。好在大部分 MCU 的硬件 I2C 外设会自动处理 SCL 被拉低的情况,但用 GPIO 模拟时序时,一定不要忽略这个可能性,否则会出现偶发的读数据错位。
3. 地址空间是怎么分配的:7 位地址、10 位地址与保留地址
3.1 地址字节的真实结构:7 位地址 + 1 位读写标记
起始条件之后,主机发的第一个字节是地址字节。这 8 位里,高 7 位是从机地址,最低 1 位是读写标记:0 表示写,1 表示读。也就是说,I2C 的"设备地址"严格来说是 7 位,能表示 0x00~0x7F,总共 128 个地址空间,去掉保留地址,实际可用的也就 100 个左右。
这里有个很常见的理解误区:很多人说某个设备的 I2C 地址是 0xA0,其实是把读写标记也算进去了。0xA0 = (0x50 << 1) | 0,也就是 7 位地址 0x50 加上写标记 0;对应用来读的地址就是 0xA1。在写驱动、查手册、看逻辑分析仪抓包之前,先确认你的"地址"到底是 7 位形式还是 8 位形式,能省掉后面一大半的排查时间。
3.2 最容易踩的坑:7 位还是 8 位?0x50 和 0xA0 之争
EEPROM 类的 24C 系列,数据手册里通常直接给 8 位形式:0xA0 是写地址,0xA1 是读地址,0xA0 的低三位由硬件引脚 A0/A1/A2 决定。而 MPU6050 这类传感器手册写的是 7 位地址 0x68,对应 8 位形式是 0xD0(写)/0xD1(读)。
同一颗芯片在不同驱动库里,API 参数要求也不一样。HAL 的HAL_I2C_Mem_Write和HAL_I2C_Mem_Read要求传 8 位地址(0xA0 这种),而 Linux i2c-dev 的用户态I2C_SLAVEioctl 设置的是 7 位地址。移植驱动时最经典的问题就是地址差一位:把 0xA0 当成 7 位地址传给 Linux,它实际访问的是 0x50 这个地址,当然找不到设备。
我在实际调试中总结了一条铁律:在代码里注释清楚地址是 7 位还是 8 位;在逻辑分析仪上看抓包时,把第一字节右移 1 位再和设备地址对比。
3.3 10 位地址的扩展流程
7 位地址空间对大多数应用够用,但总线上设备非常多时就不够了,所以协议还定义了 10 位地址模式。10 位寻址时,起始条件后的第一个字节结构是:11110 A9 A8 R/W,高 5 位固定为 11110,紧接 2 位是 10 位地址的最高两位,最低 1 位还是读写标记。第二个字节才是 A7~A0 的剩余 8 位地址。
设备如何知道自己是不是 10 位寻址?看第一个字节的高 5 位是否是 11110。这个前缀已经在地址空间里划走了 0x78~0x7B 这一段。10 位寻址在实际消费级硬件里很少用到,主要是工业总线或地址实在不够用的时候才会碰见。
3.4 保留地址、广播地址和通用调用
7 位地址里有一部分是协议保留的,不能当普通设备地址用:
| 7 位地址 | 用途 |
|---|---|
| 0x00 | 通用呼叫地址(General Call),写模式下广播 |
| 0x01 | START 字节(CBUS 等兼容场景) |
| 0x02~0x03 | 保留给不同总线格式 |
| 0x04~0x07 | 保留 |
| 0x08~0x0F | Hs 模式主机码 |
| 0x78~0x7B | 10 位地址前缀 |
| 0x7C~0x7D | 保留 |
| 0x7E | 设备 ID 读取 |
| 0x7F | 保留 |
通用呼叫地址 0x00 很有意思,它能同时唤醒总线上所有从机,但在寄存器型芯片里用得极少。日常开发只要记住:地址 0x00 和 0x7C 以上这些不要分配给自己的设备,扫描地址时也可以直接跳过。
3.5 用总线扫描脚本探测设备地址
拿到一块新板子,第一步我会先跑一遍 I2C 地址扫描。原理很简单:向每个可能的 7 位地址发起一次写传输,看有没有设备回 ACK。没有设备应答的地址会 NACK,所以能扫出所有在线设备。用 STM32 HAL 写起来非常短:
for (uint8_t addr = 0x01; addr < 0x7F; addr++) { // 注意 HAL 的 IsDeviceReady 期望 8 位地址,所以 7 位地址要左移 1 位 if (HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 1, 10) == HAL_OK) { printf("found device at 0x%02X (7-bit)\n", addr); } }这个动作帮我省了很多时间。扫描结果和手册一对,地址冲突、虚焊、没上电,基本一眼就能看出来。但要注意,有些传感器对扫描行为比较敏感,比如收到非自身地址的广播可能触发内部状态变化,所以扫描代码最好只在调试阶段跑。
4. 速率等级与上拉电阻的匹配计算
4.1 五种速率模式与关键时序参数
I2C 协议定义了好几个速率等级,不要以为随便把时钟频率配高就能跑快。每个速率模式对时钟高低电平时间、上升沿时间都有硬性要求:
| 模式 | 最大时钟频率 | tLOW 最低 | tHIGH 最低 | 最大上升时间 | 典型应用 |
|---|---|---|---|---|---|
| 标准模式(Sm) | 100 kbit/s | 4.7 µs | 4.0 µs | 1000 ns | 慢速传感器、EEPROM |
| 快速模式(Fm) | 400 kbit/s | 1.3 µs | 0.6 µs | 300 ns | 大多数场景默认 |
| 快速+模式(Fm+) | 1 Mbit/s | 0.5 µs | 0.26 µs | 120 ns | 高吞吐传感器 |
| 高速模式(Hs) | 3.4 Mbit/s | 160 ns | 60 ns | 80 ns | 处理器间通信 |
| 超快速模式(UFm) | 5 Mbit/s | 单向、无 ACK | — | — | 单向大流量传输 |
标准模式和快速模式是绝大多数项目真正在用的。注意上升时间这一列:它直接和上拉电阻、总线电容挂钩,速率越高,允许的上升时间越短,对上拉电阻的要求就越苛刻。这也解释了为什么有些人把主频从 100k 调到 400k 后,设备突然就通信异常——问题不一定在 MCU 配置,而在波形的上升沿实在太慢了。
4.2 上拉电阻的取值区间:下限由灌电流决定,上限由上升时间决定
上拉电阻不是随便拍的,它有两个边界。
下限由从机引脚灌电流能力决定。线被拉低时,电流从 VDD 经过上拉电阻流入器件内部,不能超过器件允许的灌电流。公式是:
Rp(min) = (VDD - VOL(max)) / IOL(max)
以 3.3V 系统、VOL(max)=0.4V、IOL(max)=3mA 为例:
Rp(min) = (3.3 - 0.4) / 0.003 ≈ 967Ω
也就是说电阻小于约 1kΩ 就可能在拉低时超限,导致低电平抬不起来,逻辑判断出错。
上限由 RC 充电时间决定。SDA 从低变高靠上拉电阻给总线电容充电,上升时间 tr 近似等于 0.8473 × R × Cb。要保证 tr 小于该模式规定的最大值,所以:
Rp(max) = tr / (0.8473 × Cb)
4.3 用实例算一遍:100 kbit/s 与 400 kbit/s 分别该选多大电阻
假设 3.3V 供电,总线电容 Cb 约 200pF(几颗芯片加短走线的典型值)。先算 100k 标准模式,tr 上限 1000ns:
Rp(max) = 1000ns / (0.8473 × 200pF) ≈ 5900Ω
所以标准模式下 1k~5.9k 都可用,4.7k 是最稳妥的选择。这是大量开发板默认用 4.7k 的原因。
如果同样 200pF 的总线想跑 400k,tr 上限只有 300ns:
Rp(max) = 300ns / (0.8473 × 200pF) ≈ 1770Ω
此时 4.7k 电阻已经不达标,上升沿会超过规定上限,正确做法是换 1.5k~1.8k。如果你的板子用长杜邦线把传感器飞在面包板上,Cb 可能轻松超过 300pF,这时电阻取值还要再往小走,或者老老实实把速率降到 100k。
实际项目里我一般这样处理:纯板内短走线,100k 用 4.7k;400k 用 2.2k 或 1.8k;如果总线上挂了 5、6 颗芯片且线长参差,我宁愿降到 100k 换稳定,也不愿意为那几百 kbps 的带宽去赌波形。
4.4 时钟拉伸对速率的影响
还有一个容易被忽略的因素:从机时钟拉伸会吃掉总线时间。有些温湿度传感器在转换期间会把 SCL 拉低几百微秒,名义上总线是 400k,实际一帧读事务的耗时可能接近 100k 模式。这类芯片的数据手册通常会写明"测量期间时钟拉伸最长多少",设计轮询周期时一定要把这个量加上,否则你算出来的"每秒最多读多少次"会严重偏高。
5. 读写事务完整拆解:从 EEPROM 到传感器的寄存器操作
5.1 最基本的写事务:地址加数据,逐字节应答
I2C 写事务的骨架是固定套路:START → 设备地址+写位 → 数据字节 → 数据字节 → ... → STOP,中间每一字节后都跟着一个 ACK 位。
拿 24C02 EEPROM 写单个字节举例,完整波形是:
- START
- 发 0xA0(设备写地址),等 ACK
- 发内存地址,比如 0x00,等 ACK
- 发要写入的数据,等 ACK
- STOP
EEPROM 的特殊点在于,STOP 之后还要给芯片几毫秒的内部写周期 tWR。期间芯片不理人,不能立刻读回来验证,要么延时,要么轮询 ACK。
5.2 读事务的三种形式:当前地址读、随机读、连续读
读事务比写事务绕,因为 I2C 的读写方向切换需要重新发一次设备地址,而且中间不能丢总线所有权。
第一种是当前地址读。从上一次操作留下的地址指针处开始读,流程最简单:START → 设备地址+读位 → 读数据 → 主机回 NACK → STOP。适合连续轮询场景,比如先写一次寄存器地址,后面反复读同一个位置。
第二种是随机读。要读指定地址的数据,得先把地址"写"进去,再切方向读出来:START → 设备地址+写位 → 寄存器/内存地址 →重复起始条件→ 设备地址+读位 → 读数据 → NACK → STOP。中间的重复起始条件就是第 2 节说的 Repeated START。
为什么不用 STOP 再 START?因为 STOP 意味着释放总线,期间其他主设备可能抢走总线;重复起始条件则让整个操作看起来像一次原子事务,从机内部的状态也能保持连贯。对 EEPROM 来说,这个特性保证了"先设地址后读数据"不会被其他主机打断。
第三种是连续读。随机读之后,主机不回 NACK,而是继续回 ACK,从机就会自动把内部地址加 1,连续输出下一个字节。想读多少字节就 ACK 多少次,直到最后一个字节前改回 NACK,然后 STOP。
5.3 寄存器型设备:先把寄存器地址写进去,再切方向读数据
EEPROM 的地址是"存储地址",传感器这类设备则换成了"寄存器地址"。读写套路本质一样,只是语义不同。
写一个传感器寄存器,比如配置 MPU6050 的电源管理寄存器,流程是:START → 0xD0(写)→ 寄存器地址 → 配置值 → STOP。读传感器数据也遵循同样的随机读套路,只是读出来的可能是连续好几个寄存器的数据。
这里有个操作习惯值得养成:写完配置寄存器后立刻回读一次,对比写入值和读回值是否一致。I2C 没有 SPI 那种"边发边收"的回环检查,唯一能确认写入生效的手段就是回读验证。我在调驱动时几乎每个寄存器都会做 write-verify,能拦下不少配置没生效的诡异问题。
5.4 页写入与页边界:EEPROM 连续写不能随便跨页
EEPROM 除了单字节写,还支持页写,一页内可以连续写多个字节,效率高很多。但页有边界,地址计数器到页末尾会回卷到页开头,不会自动进下一页。比如某颗 24C02 的页大小是 8 字节,你要从地址 0x04 连续写 6 个字节,实际到达 0x07 后地址会回卷到 0x00,把前面两字节覆盖掉,数据直接乱套。
所以连续写必须做跨页拆分:先算当前地址到页尾还剩多少字节,按这个数量写入,地址越界后重新发起一次写事务。不同型号页大小不同,24C02 常见 8 字节,24C16 常见 16 字节,一定以手册为准。很多"EEPROM 写入数据莫名其妙丢"的 bug,最后查出来都是跨页问题。
5.5 用 HAL 代码演示一次完整的随机读
实际工程里不推荐手写 GPIO 模拟,用 MCU 的硬件 I2C 外设更省心。STM32 HAL 把上面的流程封装成了两个函数,以 24C02 为例:
uint8_t buf[4] = {0}; // 写 4 字节:设备地址 0xA0(8 位写地址),存储地址 0x00,地址宽度 1 字节 HAL_I2C_Mem_Write(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, buf, 4, 100); // 随机读 4 字节:内部自动完成 写地址+重复起始+切读方向 HAL_I2C_Mem_Read(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, buf, 4, 100);HAL_I2C_Mem_Read内部就是随机读流程:发起 START,写设备地址+写位,写内存地址,发重复起始,再写设备地址+读位,连续读满指定长度后回 NACK 并 STOP。理解了这个封装背后的动作,以后再遇到需要手动拼凑的事务(比如读设备 ID、发特殊命令),就能自己写出等价代码了。
6. 实战调试:总线挂死、地址对不上与抓包经验
6.1 总线挂死的典型原因与复位手段
I2C 调试遇到最多的问题就是总线挂死,表现往往是程序卡在某个等待 ACK 的循环里,或者后续所有通信全部失败。最典型的原因是主从通信中途被打断——比如主机复位了,但从机还停留在上一次事务的半路,把 SDA 拉低不肯放。也有人为原因:代码里 STOP 没发出去,或 GPIO 模式没切换对。
恢复手段分几步。第一步,检查空闲时 SCL 和 SDA 的电平,正常都应该被上拉电阻拉到高电平,如果某根线被拉死,说明有设备在扯线。第二步,用 GPIO 模拟方式强制翻转 SCL 9 次,每次翻转之间把 SDA 释放,相当于手动完成一次被中断字节的收尾,让从机恢复空闲状态。第三步,实在不行就断电重启从机。对已量产的产品,更稳妥的做法是设计看门狗定时器,检测到总线挂死后先执行上面的 SCL 复位序列再重启通信。
6.2 用手边工具抓波形:逻辑分析仪的正确用法
调试 I2C,一个 USB 逻辑分析仪比示波器好用得多。选采样率时注意别太低:解码 I2C 至少要保证每个时钟周期采到 4~8 个点,400k 的总线建议用 4MHz 以上采样率,便宜的 24MHz 分析仪足够。触发条件设在 SDA 下降沿,这样每次总线活动都能从 START 开始抓。
抓完波形后,直接看解码结果里的几个关键位置:地址字节是什么、R/W 位是几、哪个字节后面跟了 NACK。逻辑分析仪显示的地址通常是 7 位,但也有的软件显示 8 位,先搞清楚工具设置再下结论。我曾经因为分析仪显示 0xA1 而自己代码里写的是 0x50,排查了半天,最后发现 0xA1 就是 0x50 的读形式,二者本来就在演同一件事。
6.3 排查顺序:先查硬件电平,再查时序,最后查逻辑
遇到 I2C 问题,我习惯按固定顺序排查,不跳步。
第一查硬件:万用表量 SCL、SDA 空闲电平,正常是高;再查电压域,确认主从设备逻辑电平兼容;再查上拉电阻有没有焊、是不是接到了正确的 VDD 上。很多时候"通信时好时坏"就出在虚焊和上拉电阻虚贴。
第二查时序:逻辑分析仪抓包,看起始条件、停止条件、每个字节的 ACK 是否完整。重点看有没有多余的 STOP、地址字节后跟的是 ACK 还是 NACK、重复起始条件是否产生。
第三查逻辑:排除前两类后,再怀疑代码。最常见就是地址位宽不统一、寄存器地址长度配错(有的芯片寄存器地址是 2 字节,需要 MEMADD_SIZE_16BIT 配置)、读时序里少了重复起始。这三层查完,绝大多数问题都能定位。
我还有个习惯:先用 100k 速率把通信打通,验证硬件和地址没问题,再逐步提到 400k。这样能清楚分离"硬件问题"和"时序余量问题",不会把两类故障搅在一起。
6.4 可靠 I2C 布线的几条经验
最后聊几点实际布线习惯。I2C 设计上就是板内短走线协议,超过 10cm 就要开始担心信号完整性问题。长线场景可以串 33Ω 左右的阻尼电阻抑制过冲,或者用 I2C 总线缓冲器做隔离。混合电压域一定要加电平转换器,不要直接硬连。另外,上拉电阻的值需要结合实测,有条件的话用差分探头或逻辑分析仪看一眼波形上升沿,再确认最终取值。
我个人在项目里多会做一次 I2C 总线扫描和回读验证作为出厂前自检。其实这套流程花不了多少时间,却能把绝大多数线序、虚焊、地址配置问题拦截在生产之前。
如果你正准备上手某个新传感器,建议从最简单的"读设备 ID 寄存器"开始。设备 ID 通常固定且不会误写,用它验证 I2C 通路最安全。读出来和手册一致了,再进入寄存器配置和正式数据采集,后面就顺很多。这个顺序,比一上来就全速读传感器数据要稳得多。