# I2C 协议研究
> 嵌入式芯片工程师之家 – 基础研究系列
—
## 1. 概述
I2C(Inter-Integrated Circuit)是由 Philips 公司(现 NXP)于 1982 年开发的一种串行通信总线协议。它最初设计用于连接微控制器和外围设备,现已成为嵌入式系统中最常用的通信协议之一。
### 1.1 主要特点
– **两线制**:仅需 SDA(数据线)和 SCL(时钟线)两根信号线
– **多主多从**:支持多个主设备和多个从设备在同一总线上
– **同步通信**:由主设备提供时钟信号
– **地址寻址**:每个从设备有唯一的地址(7 位或 10 位)
– **开漏输出**:需要外部上拉电阻
– **标准速率**:
– 标准模式(Standard-mode):100 kbps
– 快速模式(Fast-mode):400 kbps
– 快速模式+(Fast-mode Plus):1 Mbps
– 高速模式(High-speed mode):3.4 Mbps
—
## 2. 物理层
### 2.1 总线结构
“`
VCC
|
┌┴┐
│ │ Rpull-up (典型 4.7kΩ)
└┬┘
├────────── SDA
│
┌────┴────┐
│ Master │
│ Slave │
└─────────┘
“`
### 2.2 电气特性
| 参数 | 标准模式 | 快速模式 | 快速模式+ |
|——|———-|———-|———–|
| 电源电压 | 2.5V – 5.5V | 2.5V – 5.5V | 1.65V – 3.6V |
| 上拉电阻 | 1kΩ – 10kΩ | 1kΩ – 4.7kΩ | 500Ω – 1kΩ |
| 上升时间 | ≤ 1000ns | ≤ 300ns | ≤ 120ns |
| 下降时间 | ≤ 300ns | ≤ 300ns | ≤ 120ns |
### 2.3 开漏输出原理
I2C 总线采用开漏(Open-Drain)或开集(Open-Collector)输出结构:
– **低电平**:MOSFET 导通,将总线拉低到 GND
– **高电平**:MOSFET 截止,由上拉电阻将总线拉高到 VCC
– **优点**:
– 支持多设备共享总线(线与逻辑)
– 支持不同电压等级的设备(通过上拉到不同电压)
– 简化总线仲裁
—
## 3. 协议时序
### 3.1 基本信号定义
“`
SDA: ────┐ ┌───────┐ ┌────
│ │ │ │
└───┘ └───┘
↑ ↑ ↑ ↑
S P Data S P
tart top tart top
SCL: ─────┐ ┌─────┐ ┌─────
│ │ │ │
└─┘ └─┘
“`
### 3.2 起始条件(START)
– SCL 为高电平时,SDA 从高电平跳变到低电平
– 标志一次传输的开始
– 只有主设备可以产生起始条件
### 3.3 停止条件(STOP)
– SCL 为高电平时,SDA 从低电平跳变到高电平
– 标志一次传输的结束
– 释放总线
### 3.4 重复起始条件(Repeated START)
– 在不产生 STOP 的情况下再次产生 START
– 用于连续访问同一从设备或切换读写方向
– 保持对总线的控制
### 3.5 数据有效性
“`
┌─────────────┐
SDA: │ 数据位 │
│ 稳定有效 │
└─────────────┘
↑ ↑
SCL 上升 SCL 下降
采样点 变化允许
“`
– 数据在 SCL 高电平期间必须保持稳定
– 数据变化只能在 SCL 低电平期间进行
– 在 SCL 上升沿采样数据
—
## 4. 数据帧格式
### 4.1 字节格式
“`
MSB LSB
┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
│D7 │D6 │D5 │D4 │D3 │D2 │D1 │D0 │ACK│
└───┴───┴───┴───┴───┴───┴───┴───┴───┘
│←─────── 8 位数据 ───────→│← ACK →│
“`
– 每个字节 8 位,MSB 先发
– 每个字节后跟随 1 位应答(ACK/NACK)
### 4.2 应答机制
| 类型 | 产生者 | SDA 电平 | 含义 |
|——|——–|———-|——|
| ACK | 接收方 | 低电平 | 成功接收 |
| NACK | 接收方 | 高电平 | 未成功接收/结束 |
– 发送方在第 9 个时钟周期释放 SDA
– 接收方拉低 SDA 表示 ACK
– 接收方保持 SDA 高电平表示 NACK
### 4.3 完整传输序列
“`
START → [地址 + R/W] → ACK → [数据] → ACK → … → STOP
“`
—
## 5. 寻址方式
### 5.1 7 位地址
“`
┌───┬───┬───┬───┬───┬───┬───┬───┐
│A6 │A5 │A4 │A3 │A2 │A1 │A0 │R/W│
└───┴───┴───┴───┴───┴───┴───┴───┘
│←──── 7 位地址 ────→│← 方向 →│
“`
– 地址范围:0x00 – 0x7F(0x00-0x07 和 0x78-0x7F 保留)
– 可用地址:112 个(0x08-0x77)
– R/W 位:0=写,1=读
### 5.2 10 位地址
“`
第一次传输:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ 1 │ 1 │ 1 │ 1 │ 0 │A9 │A8 │R/W│
└───┴───┴───┴───┴───┴───┴───┴───┘
固定前缀 │← 高 2 位 →│
第二次传输:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│A7 │A6 │A5 │A4 │A3 │A2 │A1 │A0 │
└───┴───┴───┴───┴───┴───┴───┴───┘
│←─────── 低 8 位 ───────→│
“`
– 地址范围:0x000 – 0x3FF
– 需要两次传输完成寻址
– 兼容性较差,使用较少
### 5.3 保留地址
| 地址 | 用途 |
|——|——|
| 0x00 | 通用呼叫地址 |
| 0x01-0x03 | 保留(10 位地址) |
| 0x78-0x7B | 保留(10 位地址) |
| 0x7C-0x7F | 保留 |
—
## 6. 传输模式
### 6.1 主设备写从设备
“`
START → [地址 + 0] → ACK → [寄存器地址] → ACK → [数据] → ACK → STOP
“`
### 6.2 主设备读从设备(单字节)
“`
START → [地址 + 0] → ACK → [寄存器地址] → ACK →
RESTART → [地址 + 1] → ACK → [数据] → NACK → STOP
“`
### 6.3 主设备读从设备(多字节)
“`
START → [地址 + 0] → ACK → [寄存器地址] → ACK →
RESTART → [地址 + 1] → ACK → [数据 1] → ACK → [数据 2] → ACK → … → [数据 N] → NACK → STOP
“`
### 6.4 组合传输
“`
START → [地址 + 0] → ACK → [命令] → ACK →
RESTART → [地址 + 1] → ACK → [数据] → NACK → STOP
“`
—
## 7. 时钟延展(Clock Stretching)
从设备可以通过拉低 SCL 来延缓通信:
“`
SCL: ────┐ ┌───────┐ ┌────
│ │ │ │
└───┘ └───┘
↑
从设备拉低
(处理数据中)
“`
– 从设备需要更多时间处理数据时使用
– 主设备必须等待 SCL 释放
– 某些主设备不支持时钟延展
—
## 8. 总线仲裁
### 8.1 仲裁原理
当多个主设备同时尝试控制总线时:
1. 每个主设备在发送数据时同时监测 SDA
2. 如果发送高电平但检测到低电平,说明有其他主设备在发送
3. 该主设备立即停止发送,释放总线
4. 发送低电平的主设备赢得仲裁
### 8.2 仲裁时序
“`
Master A: 发送 1 → 检测到 0 → 失去仲裁 → 释放总线
Master B: 发送 0 → 检测到 0 → 继续发送 → 赢得仲裁
“`
– 仲裁只发生在地址和数据传输阶段
– 仲裁失败的主设备可以在总线空闲时重试
—
## 9. 常见问题与调试
### 9.1 硬件问题
| 问题 | 可能原因 | 解决方案 |
|——|———-|———-|
| 无 ACK | 从设备未连接/地址错误 | 检查接线和地址 |
| 通信不稳定 | 上拉电阻过大/过小 | 调整上拉电阻值 |
| 波形畸变 | 总线电容过大 | 减少总线长度/设备数量 |
| 电平不匹配 | 电压等级不一致 | 使用电平转换器 |
### 9.2 软件问题
| 问题 | 可能原因 | 解决方案 |
|——|———-|———-|
| 超时 | 从设备无响应 | 添加超时处理 |
| 数据错误 | 时序不匹配 | 调整 I2C 时钟频率 |
| 死锁 | 总线被占用 | 实现总线恢复机制 |
### 9.3 调试技巧
1. **使用逻辑分析仪**:捕获完整时序
2. **检查上拉电阻**:确保阻值合适
3. **验证地址**:确认从设备地址正确
4. **测量波形**:检查上升/下降时间
5. **简化测试**:先单设备通信,再增加设备
—
## 10. 代码示例(STM32 HAL)
### 10.1 初始化
“`c
// I2C 初始化配置
I2C_HandleTypeDef hi2c1;
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00702681; // 根据时钟配置
hi2c1.Init.OwnAddress1 = 0x00;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
“`
### 10.2 写入寄存器
“`c
uint8_t dev_addr = 0x50; // 设备地址
uint8_t reg_addr = 0x00; // 寄存器地址
uint8_t data = 0xAA; // 写入数据
HAL_I2C_Mem_Write(&hi2c1, dev_addr, reg_addr,
I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
“`
### 10.3 读取寄存器
“`c
uint8_t dev_addr = 0x50;
uint8_t reg_addr = 0x00;
uint8_t data;
HAL_I2C_Mem_Read(&hi2c1, dev_addr, reg_addr,
I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
“`
—
## 11. 参考资料
– [NXP I2C-Bus Specification and User Manual](https://www.nxp.com/docs/en/user-guide/UM10204.pdf)
– [Texas Instruments I2C Application Report](https://www.ti.com/lit/an/slva704/slva704.pdf)
– [STMicroelectronics I2C Application Note](https://www.st.com/resource/en/application_note/an2644.pdf)
—
*文档版本:1.0*
*创建日期:2026-04-07*
*作者:狗剩 🌱*