嵌入式 I2C 通信全解析:从硬件原理到驱动实现

一、I2C 硬件基础:线与特性与电平驱动

理解 I2C 的硬件原理是掌握协议的第一步,其核心是 "线与" 逻辑和开漏输出设计,这决定了 I2C 的多设备通信能力。

1.1 线与(Wire-AND)特性

I2C 总线的核心电气特性是线与逻辑:总线上所有设备的 SDA/SCL 引脚均为开漏输出,总线电平由所有设备的输出共同决定。

chipA 输出 chipB 输出 实际总线电平
1(高阻态) 0(拉低) 0
0(拉低) 1(高阻态) 0
1(高阻态) 1(高阻态) 1(由上拉电阻决定)
0(拉低) 0(拉低) 0

核心原理:任何一个设备输出低电平,总线就会被拉低;只有所有设备都输出高阻态时,总线才会被上拉电阻拉到高电平。这一特性是 I2C 多设备通信的基础,也是 "仲裁" 和 "应答" 机制的硬件保障。

1.2 开漏输出与推挽输出的区别

I2C 设备的引脚必须配置为开漏输出模式,而常见的 GPIO 默认是推挽输出,两者的核心区别如下:

特性 开漏输出 推挽输出
驱动能力 仅能拉低总线,高电平依赖上拉电阻 可主动输出高 / 低电平
线与支持 支持,允许多设备并联 不支持,多设备并联会导致短路
应用场景 I2C、SMBus 等多设备总线 单设备 GPIO 输出、LED 驱动等

在 I2C 硬件设计中,SDA 和 SCL 线上必须外接4.7k~10kΩ 的上拉电阻,以保证总线在空闲时处于高电平状态。


二、I2C 时序协议:信号定义与通信流程

I2C 的通信完全由时序信号控制,掌握起始 / 停止信号、数据传输、应答机制是解析协议的核心。

2.1 核心时序信号定义

  • START(起始信号) :当 SCL 为高电平时,SDA 产生一个下降沿,表示通信开始。
  • STOP(停止信号) :当 SCL 为高电平时,SDA 产生一个上升沿,表示通信结束。
  • 数据传输:在 SCL 为低电平时,发送方可以改变 SDA 电平;在 SCL 为高电平时,SDA 电平必须保持稳定,接收方此时采样数据。
  • 应答(ACK/NACK) :接收方在接收到 1 字节数据后,在第 9 个时钟周期内拉低 SDA 表示ACK(应答) ,保持高电平表示NACK(非应答)

2.2 数据传输规则

  1. 字节序 :数据传输遵循MSB 优先原则,即每个字节的最高位最先发送。
  2. 时钟同步:SCL 由主机产生,但从设备可以通过拉低 SCL 实现 "时钟拉伸",以减慢通信速率,适配自身处理速度。
  3. 仲裁机制:当多个主机同时发送数据时,通过线与逻辑自动仲裁,首先发送低电平的主机获得总线控制权,避免数据冲突。

2.3 完整通信帧结构

一次典型的 I2C 通信分为 "主机写" 和 "主机读" 两种场景,帧结构如下:

(1)主机写数据到从设备

plaintext

复制代码
START → 从机地址(7位)+ 写标志(0)→ ACK → 寄存器地址 → ACK → 数据字节 → ACK → ... → STOP
(2)主机从设备读数据

plaintext

复制代码
START → 从机地址(7位)+ 写标志(0)→ ACK → 寄存器地址 → ACK → RESTART → 从机地址(7位)+ 读标志(1)→ ACK → 数据字节 → NACK → STOP

注:读操作需要先通过 "写" 操作指定寄存器地址,再发送重复起始信号(RESTART)切换为读模式。


三、I2C 驱动实现:从流程图到代码逻辑

I2C 驱动的开发核心是严格遵循时序协议,结合硬件寄存器配置实现完整的通信流程。下面结合你提供的流程图,解析 "主机写" 和 "主机读" 的驱动逻辑。

3.1 主机写驱动流程

  1. 初始化阶段:清除 I2C 状态寄存器的中断标志,配置为主机发送模式。
  2. 发送起始信号 :置位I2CR_MTX位产生 START 信号。
  3. 发送从机地址 :将从机地址 + 写标志写入数据寄存器I2DR,等待从机应答。
  4. 判断应答:若收到 NACK 则通信失败,直接发送 STOP 信号;若收到 ACK 则继续。
  5. 发送寄存器地址:写入目标寄存器地址,等待从机应答。
  6. 发送数据:依次发送数据字节,每发送 1 字节等待一次 ACK。
  7. 发送停止信号:所有数据发送完成后,产生 STOP 信号,释放总线。

3.2 主机读驱动流程

  1. 初始化与起始信号:与写流程一致,先发送起始信号和从机地址 + 写标志,指定寄存器地址。
  2. 重复起始信号:发送 RESTART 信号,切换为读模式。
  3. 发送从机地址 + 读标志:等待从机应答。
  4. 接收数据
    • 若接收长度 > 1,主机回复 ACK,继续接收下一字节;
    • 若接收最后 1 字节,主机回复 NACK,通知从机停止发送。
  5. 发送停止信号:接收完成后产生 STOP 信号。

3.3 关键寄存器说明

  • I2CR(控制寄存器):配置主机 / 从机模式、发送 / 接收模式,控制 START/STOP 信号。
  • I2SR(状态寄存器):指示中断标志、应答状态(RXAK位表示是否收到 ACK)、仲裁结果。
  • I2DR(数据寄存器):用于发送 / 接收数据字节。

四、开发避坑指南

  1. 上拉电阻选择:上拉电阻的阻值直接影响通信速率,4.7kΩ 适合 100kHz 标准模式,10kΩ 适合低速模式,阻值过大会导致信号上升沿过慢,影响稳定性。
  2. 时钟拉伸处理:从设备可能拉低 SCL 实现时钟拉伸,驱动中需检测 SCL 电平,等待从设备释放时钟后再继续传输。
  3. 应答检测:每发送 1 字节后必须检测从机的 ACK 信号,若未收到 ACK 需及时终止通信并处理错误。
  4. 总线冲突处理 :多主机场景下需处理仲裁失败的情况,通常通过检测I2SR_AL位(仲裁丢失标志)并重新发起通信。

五、总结

I2C 协议的核心是 "简洁高效":仅用两根线就实现了多设备组网,通过线与逻辑和应答机制保证了通信的可靠性。开发 I2C 驱动的关键在于:

  1. 理解硬件特性:开漏输出 + 上拉电阻的设计是 I2C 的物理基础。
  2. 严格遵循时序:起始 / 停止信号、数据采样、应答机制的时序必须精准。
  3. 健壮的错误处理:应答失败、仲裁丢失、时钟拉伸等异常场景需妥善处理。
相关推荐
蓬荜生灰4 小时前
STM32(6)-- GPIO外设
单片机·嵌入式硬件
我爱我家diyer5 小时前
使用STM32的HAL库开发GD32F303CGT6
stm32·单片机·嵌入式硬件
新能源BMS佬大6 小时前
【仿真到实战】STM32落地EKF算法实现锂电池SOC高精度估算(含硬件驱动与源码)
stm32·嵌入式硬件·算法·电池soc估计·bms电池管理系统·扩展卡尔曼滤波估计soc·野火开发板
点灯小铭6 小时前
基于单片机的井盖安全监测与报警上位机监测系统设计
单片机·嵌入式硬件·毕业设计·课程设计
Hello_Embed7 小时前
USB 虚拟串口源码改造与 FreeRTOS 适配
笔记·单片机·嵌入式·freertos·usb
无垠的广袤7 小时前
【CPKCOR-RA8D1】RUHMI 转换 AI 模型
人工智能·python·嵌入式硬件·开发板
望眼欲穿的程序猿8 小时前
SDCC+Ai8051U 中断点灯
stm32·单片机·嵌入式硬件
youcans_8 小时前
【动手学STM32G4】(15)三路互补带死区 PWM 输出
stm32·单片机·嵌入式硬件·pwm·死区
小慧10248 小时前
外部中断与回调函数
stm32·单片机·嵌入式硬件