I²C(IIC)通信详解:从总线原理、时序到单片机实战

I²C 是嵌入式开发中最常见的板级通信总线之一。只用 SDA 和 SCL 两根信号线,一个控制器就能连接传感器、EEPROM、RTC、OLED 等多个器件。国内开发者也常把它写成 IIC;本文统一使用规范写法 I²C。

它的引脚少、寻址清晰、外设生态成熟,但“能看到时钟”并不等于“通信一定正确”。地址写法、上拉电阻、总线电容、ACK、重复起始和时钟拉伸,都是实际调试中最容易踩坑的地方。下面从电气结构开始,完整走一遍 I²C。

一、I²C 总线由什么组成

I2C 总线典型连接拓扑,SDA 和 SCL 各自通过上拉电阻连接 3.3V,并连接 MCU、传感器、EEPROM 和 OLED
I²C 典型连接:所有器件共享 SDA 与 SCL,每根线都需要上拉电阻。

I²C 是同步串行总线。SCL 提供时钟,SDA 传输数据。总线通常由一个控制器发起事务,目标器件根据地址响应。NXP 最新规范使用 Controller/Target 描述这两个角色,很多旧资料中仍会看到 Master/Slave,它们指的是同一组角色。

同一总线上的目标器件必须使用不冲突的地址。图中的温湿度传感器、EEPROM 和 OLED 虽然共用两根线,但控制器能够通过地址选择其中一个器件。

二、为什么 SDA 和 SCL 都要上拉

I²C 引脚通常采用开漏(Open-drain)结构:器件可以主动把总线拉低,却不会主动输出高电平。没有器件拉低时,外部上拉电阻把总线恢复为高电平。因此,总线上的高电平实际表示“所有器件都释放了这根线”。

这种结构带来两个关键能力:

  • 线与逻辑:任何一个器件拉低,总线读到的就是低电平,避免多个器件直接推挽对打。
  • 多器件协作:ACK、时钟拉伸和多控制器仲裁,都建立在“低电平优先”的电气特性上。

常见的 4.7 kΩ 只是经验起点,并不是所有 I²C 电路的固定答案。速度越快、走线越长、器件越多,总线电容越大,对上拉电阻的要求就越严格。

三、START、STOP 与数据采样

总线空闲时,SDA 和 SCL 都保持高电平。一次事务由控制器产生 START 条件开始,并由 STOP 条件结束:

  • START:SCL 为高电平时,SDA 从高变低。
  • STOP:SCL 为高电平时,SDA 从低变高。
  • 普通数据位:SDA 应在 SCL 低电平期间改变,在 SCL 高电平期间保持稳定。
  • 发送顺序:每个字节都从最高位 MSB 开始传输。
I2C 写帧时序图,向 7 位地址 0x3C 写入数据 0xA5,包含 START、地址、写方向位、ACK、数据与 STOP
向 7 位地址 0x3C 写入 0xA5:每传输 8 位,接收方在第 9 个时钟给出 ACK 或 NACK。

四、地址、方向位与 ACK

最常见的 I²C 地址长度是 7 位。START 之后的第一个字节由“7 位地址 + 1 位读写方向”组成:方向位为 0 表示写,为 1 表示读。紧接着的第 9 个时钟用于应答。

  • ACK:接收方在第 9 个时钟把 SDA 拉低,表示已接收。
  • NACK:SDA 保持高电平,可能表示地址无人响应、目标器件尚未准备好,或者读取方不再需要更多字节。

最常见的地址混淆

假设器件的 7 位地址是 0x3C,放入总线首字节后,写地址字节是 0x78,读地址字节是 0x79。有些数据手册给 7 位地址,有些旧代码直接给左移后的 8 位地址。调用驱动前一定要确认 API 需要哪一种;多数现代 HAL 接口要求传入 7 位地址,但也有接口要求调用者自行左移。

五、典型的寄存器读写流程

写一个寄存器

START
  -> 地址 + W -> ACK
  -> 寄存器地址 -> ACK
  -> 数据 -> ACK
  -> STOP

读取一个寄存器

START
  -> 地址 + W -> ACK
  -> 寄存器地址 -> ACK
  -> REPEATED START
  -> 地址 + R -> ACK
  -> 数据 -> NACK
  -> STOP

读取寄存器时,前半段写入的是“接下来要读哪个寄存器”。随后控制器发出重复起始(Repeated START),不释放总线便切换到读方向。读取最后一个字节后,控制器发送 NACK,告诉目标器件读取结束,再产生 STOP。

六、常见速率模式

模式 最高速率 常见用途
Standard-mode 100 kbit/s 兼容性优先、低速传感器
Fast-mode 400 kbit/s 常见 MCU 与显示、传感器
Fast-mode Plus 1 Mbit/s 支持 Fm+ 的高速器件
High-speed mode 3.4 Mbit/s 对器件和总线设计要求较高
Ultra Fast-mode 5 Mbit/s 单向传输,使用场景较少

实际速率不能只看 MCU 能跑多快,还要确认总线上每一个器件、双向电平转换器、上拉网络和走线电容都支持该模式。对常见开发板来说,100 kHz 和 400 kHz 通常最实用。

七、时钟拉伸与多控制器仲裁

目标器件如果暂时来不及处理数据,可以把 SCL 保持在低电平,这叫时钟拉伸(Clock Stretching)。控制器释放 SCL 后,应读取引脚的真实电平,等它确实变高再继续。使用硬件 I²C 外设时,需要确认控制器及其驱动是否支持目标器件要求的拉伸行为。

在多控制器系统中,多个控制器可能同时发起通信。仲裁同样利用线与逻辑:一个控制器释放 SDA、准备发送 1,却读到低电平,说明另一个控制器正在发送 0,此时前者失去仲裁并停止驱动。普通单控制器项目通常遇不到这一过程,但理解它有助于解释 I²C 为什么必须使用开漏结构。

八、上拉电阻怎么选

电阻太小,会让器件拉低时承受过大的灌电流;电阻太大,又会使 RC 上升沿过慢。TI 的应用说明给出了实用边界:

Rₚ(min) = (VCC - VOL(max)) / IOL
Rₚ(max) = tr / (0.8473 × Cb)

其中,tr 是规范允许的最大上升时间,Cb 是总线总电容。举例:3.3 V 总线、VOL(max)=0.4 V、允许灌电流 3 mA,则最小值约为 967 Ω;若 Fast-mode 的上升时间按 300 ns、总线电容约 100 pF 估算,最大值约为 3.54 kΩ。这种情况下可从 2.2 kΩ 或 3.3 kΩ 开始验证。

还要注意,开发板和模块上可能已经焊有上拉电阻。多个模块的上拉会并联,例如两组 4.7 kΩ 并联后等效约 2.35 kΩ。最终应结合器件手册,并用示波器观察 SDA、SCL 的低电平和上升沿。

九、一个简化的写寄存器示例

bool i2c_write_register(uint8_t address7,
                        uint8_t reg,
                        uint8_t value)
{
    if (!i2c_start()) return false;

    if (!i2c_write_byte((address7 << 1) | 0u)) goto fail;
    if (!i2c_write_byte(reg)) goto fail;
    if (!i2c_write_byte(value)) goto fail;

    i2c_stop();
    return true;

fail:
    i2c_stop();
    return false;
}

这段伪代码展示的是总线事务,不对应某个具体芯片。实际工程优先使用 MCU 的硬件 I²C 外设和经过验证的 HAL/驱动,并补充超时、错误码、总线恢复和并发保护。

十、常见故障与排查顺序

现象 优先检查
地址后没有 ACK 7 位/8 位地址是否混用;供电、共地、引脚复用和上拉是否正确
SDA 一直为低 目标器件是否在传输中复位;先复位器件,必要时输出 9 个 SCL 脉冲后再产生 STOP
上升沿缓慢 减小上拉阻值、缩短走线、减少器件和连接线带来的电容
100 kHz 正常,400 kHz 失败 上升时间、布线、电平转换器、目标器件速率上限
偶发错码或读数跳变 逻辑电平阈值、供电噪声、信号完整性、时钟拉伸和软件超时

推荐使用逻辑分析仪或示波器按以下顺序观察:先确认空闲电平,再找 START,然后检查地址与方向位,最后看第 9 个时钟是否出现 ACK。把问题定位到“电气层、地址层还是数据层”,通常比反复修改驱动代码更快。

十一、连接前的快速检查表

  • SDA、SCL 是否接对,所有器件是否共地。
  • 总线电压是否满足每个器件的输入阈值,跨电压域时是否使用合适的双向转换器。
  • SDA、SCL 是否都有上拉,等效阻值是否合理。
  • 地址是 7 位还是已经左移的 8 位表示。
  • 目标器件的地址选择脚、复位脚和启动延时是否符合手册。
  • 速率、时钟拉伸、重复起始是否被控制器驱动正确支持。

结语

I²C 的协议并不复杂:两根开漏信号线、START/STOP、按字节传输以及每字节后的 ACK。但真正稳定的系统,还依赖正确的电气设计和清晰的错误处理。理解“谁在拉低总线、什么时候采样、为什么没有 ACK”,就能把大多数 I²C 故障从猜测变成可测量、可定位的问题。

参考资料

点击网页底部链接入群,欢迎大家进群交流!

评论

  1. 汉堡哇
    iPhone Safari
    17 小时前
    2026-9-04 19:24:06

    打卡打卡!

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇