【STM32】 I2C协议和设备主从( 图解 + 面试题 )


I2C协议和设备主从

1. 空闲状态

状态定义

总线无任何通信活动、所有设备均释放总线控制权的静默状态,是所有I2C通信的基准起点。

电气与硬件特征

  1. 电平表现:SCL、SDA均稳定保持高电平
  2. 硬件本质:总线上所有设备的开漏输出管全部截止,引脚呈高阻态;总线电平完全由外部上拉电阻拉至VCC,是线与逻辑的全1状态。
  3. 持续时间:必须保持至少一个总线时钟周期以上,才能被判定为空闲。

硬件判断标志

STM32的I2C外设通过硬件自动检测总线状态,对应I2C_SR2寄存器的BUSY位:

  • BUSY = 0:总线空闲
  • BUSY = 1:总线处于忙状态,正在通信

状态意义

  1. 只有检测到总线空闲,主机才能合法发起起始信号,启动新通信。
  2. 所有从机处于低功耗监听模式,内部接收逻辑复位,等待起始信号触发。
  3. 通信异常复位后,必须等待总线回到空闲态,才能重新初始化外设。

2. Master发送开始状态和停止状态

起始(START/S)与停止(STOP/P)是I2C的两种总线控制信号,只能由主机发起,用来标记一帧通信的起止边界。

起始+停止 时序波形图


一、起始信号(START)

触发条件

SCL稳定在高电平时,SDA由高向低产生下降沿。

逐拍硬件动作
  1. 主机先确认SCL为高电平(总线空闲态)
  2. 主机控制SDA引脚的开漏管导通,主动拉低SDA
  3. 因为线与特性,其他设备都是高阻,总线SDA电平随之变低
  4. 所有从机在SCL高电平时持续采样SDA,检测到这个下降沿,立即判定为起始信号
  5. 所有从机内部接收逻辑复位,进入地址监听状态,硬件自动置位BUSY标志
协议作用
  • 作为一帧通信的开始标记
  • 强制总线上所有从机同步到初始接收状态
  • 总线从空闲态正式进入忙态

二、停止信号(STOP)

触发条件

SCL稳定在高电平时,SDA由低向高产生上升沿。

逐拍硬件动作
  1. 主机先拉低SDA,确保SDA起始电平为低
  2. 主机拉高SCL,稳定在高电平
  3. 主机关断SDA的开漏管,释放SDA
  4. 外部上拉电阻将SDA慢慢拉高,产生上升沿
  5. 所有从机检测到SCL高电平时的SDA上升沿,判定为停止信号
  6. 所有从机退出通信状态,总线回到空闲态,BUSY标志硬件清零
协议作用
  • 标记本次通信正式结束
  • 释放总线控制权,所有设备回到监听状态
  • 通知从机传输完成,可以开始内部处理(如EEPROM写入)

关键区分:起止信号 vs 普通数据位

信号类型 SDA跳变时机 识别结果
起始/停止信号 SCL高电平期间跳变 总线控制信号,标记通信边界
普通数据位 SCL低电平期间跳变 有效数据,传输0/1

设计本质:

利用跳变时机的不同,在两根线上同时区分控制信号和数据信号,不需要额外控制线。


3. 调整(数据建立与保持)

"调整"指数据位的电平建立与稳定过程,核心是保证SCL高电平采样时,SDA电平已经稳定,避免采样错误。

单比特数据 时序调整图

复制代码
SCL:        ┌──────────┐
            │          │
SDA:  ──────┘          └───────
      ◄ tSU:DAT ► ◄ tHD:DAT ►
      数据建立时间   数据保持时间
      SCL上升沿前  SCL下降沿后
      数据必须稳定  数据保持不变

为什么必须要有调整时间

  1. 硬件电平跳变需要时间 I2C是开漏输出,高电平靠上拉电阻充电,总线存在寄生电容,电平跳变有上升沿/下降沿,不是瞬间完成的。如果跳变过程中采样,会得到错误值。
  2. 接收方采样需要稳定值 SCL上升沿后进入高电平,接收方会在高电平时段采样SDA。如果此时SDA还在跳变,采样结果不可靠。

两个核心时序参数(标准模式)

参数 全称 最小值 定义
tSU:DAT 数据建立时间 250ns SDA电平变化稳定 → SCL上升沿 的最小时间差
tHD:DAT 数据保持时间 0ns SCL下降沿 → SDA开始变化 的最小时间差

完整的一位数据调整流程

  1. SCL拉低,进入调整期 主机拉低SCL,总线进入低电平阶段。
  2. 发送方切换SDA电平 发送方改变SDA输出,从当前位切换到下一位的电平。
  3. 等待建立时间 等待SDA电平完全跳变并稳定,满足tSU:DAT要求。
  4. SCL拉高,进入采样期 主机拉高SCL,进入高电平阶段。
  5. 接收方采样 SCL高电平时,SDA保持稳定,接收方完成电平采样。
  6. SCL再次拉低 完成一位传输,进入下一位的调整周期。

通俗总结:

SCL低电平是"摆数据"的准备时间,SCL高电平是"读数据"的采样时间。

调整就是在准备时间里把数据摆稳。


4. Master发送从设备地址

起始信号之后,主机发送的第一个字节是地址字节,作用是选中目标从机,并指定本次通信的读写方向。

地址字节结构图(标准7位地址)

复制代码
  Bit7  Bit6  Bit5  Bit4  Bit3  Bit2  Bit1  Bit0
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│  A6 │  A5 │  A4 │  A3 │  A2 │  A1 │  A0 │ R/W │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
◄────────── 7位从机地址 ──────────► ◄─►
                                  读写控制位

字段详解

  1. 高7位:从机硬件地址(A6~A0)
    • 每个从机出厂时分配唯一地址,部分设备可通过AD0/AD1等引脚灵活配置地址
    • 地址范围:0x00 ~ 0x7F,共128个,其中0x000x07、0x780x7F为协议保留地址
    • 寻址过程:总线上所有从机同时接收这7位,硬件自动和自身地址比对,只有匹配的从机才参与本次通信
  2. 最低位Bit0:读写控制位 R/W
    • R/W = 0:写方向,主机向从机写入数据
    • R/W = 1:读方向,主机从从机读取数据

地址字节计算示例

已知某EEPROM的7位设备地址为0x50

  • 写操作地址字节:0x50 << 1 = 0xA0(最低位补0)
  • 读操作地址字节:(0x50 << 1) | 0x01 = 0xA1(最低位置1)

发送后的硬件行为

  1. 主机按高位在前(MSB first)的顺序,逐位发送地址字节
  2. 所有从机同步接收每一位,存入内部移位寄存器
  3. 8位接收完成后,所有从机同时比对高7位地址
    • 地址不匹配:忽略本次通信,继续等待下一次起始信号
    • 地址匹配:准备好应答逻辑,等待第9个时钟周期输出ACK

特殊地址

  • 广播地址0x00:写操作时所有从机同时响应,读操作时无设备响应
  • 通用呼叫地址:属于保留地址,用于特殊功能如广播复位

5. 应答ACK/无应答位NACK

每传输完8位数据,紧跟第9个SCL时钟周期,传输应答信号,是I2C的可靠传输机制。

ACK与NACK 时序对比图


一、应答位 ACK

定义

接收方主动拉低SDA,代表成功接收前一个字节。

核心原则:谁接收,谁发ACK
  • 主机发送地址/数据 → 从机回复ACK
  • 从机发送数据 → 主机回复ACK
硬件过程(主机发地址、从机回ACK为例)
  1. 第8个SCL下降沿,8位地址发送完毕,主机释放SDA(开漏管截止,输出高阻)
  2. SDA总线的控制权交给从机
  3. 地址匹配的从机,在第9个SCL低电平期间,导通开漏管拉低SDA
  4. 第9个SCL高电平时,主机检测SDA电平:低电平 = 收到ACK
  5. 第9个时钟结束,从机关断开漏管,释放SDA,总线回到高电平

二、无应答位 NACK

定义

接收方不动作,SDA保持高电平,代表接收失败、拒绝接收或结束接收。

三种典型场景
  1. 无设备应答:总线上没有匹配地址的从机,没人拉低SDA,主机检测到高电平,判定设备不存在/不在线。
  2. 接收出错:从机检测到数据错误,不回复ACK,通知主机重发。
  3. 读操作结束:主机读取最后一个字节时,回复NACK,明确告诉从机"不用再发了",准备结束通信。

读操作的NACK是必须的:

如果最后一个字节也回复ACK,从机会继续发送下一个字节,导致总线失控。


6. Master写数据

主机向从机写入数据的完整流程,是I2C最基础的主写从操作。

完整写操作 时序图

逐步骤完整流程

  1. 发起起始信号 主机检测总线空闲后,发送起始信号,总线进入忙状态。
  2. 发送从机写地址主机发送7位从机地址 + 写位(0),高位在前。
  3. 接收地址应答 地址匹配的从机拉低SDA,回复ACK。
    • 收到ACK:继续通信
    • 未收到ACK(NACK):判定设备无响应,主机直接发停止信号终止
  4. 发送第一个数据字节 主机按高位在前顺序,逐位发送8位数据。 SCL低电平时主机调整SDA电平,SCL高电平时从机采样。
  5. 接收数据应答 8位数据发完,主机释放SDA,从机回复ACK,确认收到数据。
  6. 连续多字节传输 重复"发数据→收ACK"的循环,可以连续发送任意多个字节。 多数从机内部地址指针会自动递增,依次写入连续的存储单元。
  7. 发送停止信号 所有数据发送完成,主机发送停止信号,释放总线,通信结束。

典型应用

向传感器写配置寄存器、向EEPROM写数据、向OLED屏写显存。


7. Master读数据

主机从从机读取数据的流程,核心是SDA控制权的交接------从机主动发数据,主机接收并应答。

带重复起始的完整读操作 时序图

这是最常用的"指定内部地址读"流程:先写从机内部寄存器地址,再读数据,中间用重复起始切换方向。

逐步骤完整流程

  1. 起始 + 写设备地址****主机发起起始,发送从机写地址,收到ACK。
  2. 发送内部寄存器地址****发送要读取的从机内部存储/寄存器地址,收到从机ACK。 作用:告诉从机,接下来要读它内部的哪个地址单元。
  3. 重复起始信号(ReSTART) 不发送停止信号,直接再次发起起始信号。
    • 作用:切换总线读写方向,同时不释放总线,避免其他主机抢占
    • 对比停止再起始:停止会释放总线,可能被占用,且时序开销更大
  4. 发送读设备地址 发送从机地址 + 读位(1)****,收到从机ACK。 此时SDA控制权正式从主机移交到从机。
  5. 接收第一个数据字节 从机主动逐位发送数据,高位在前。 SCL低电平时从机调整SDA电平,SCL高电平时主机采样。
  6. 主机回复ACK 8位接收完毕,主机拉低SDA回复ACK,告诉从机:继续发下一个。 从机收到ACK,就会发送下一个字节。
  7. 连续读取多字节 重复"收数据→发ACK",连续读取多个字节,从机内部地址自动递增。
  8. 最后一字节回复NACK****读取最后一个字节后,主机不拉动SDA,回复NACK。 作用:通知从机停止发送,释放SDA总线控制权。
  9. 发送停止信号****主机发送停止信号,释放总线,通信结束。

关键注意点

  • 读操作时,主机只提供SCL时钟,SDA由从机控制
  • 必须用NACK标记最后一个字节,否则从机会持续发送
  • 指定地址读必须用"写地址+重复起始+读数据"的复合帧结构

8. 总结

I2C协议核心规则总表

维度 规则
物理层 开漏输出 + 外部上拉 + 线与逻辑
信号线 SCL同步时钟 + SDA串行数据,半双工
起止信号 SCL高电平时,SDA下降沿=起始;SDA上升沿=停止
数据规则 SCL低电平时SDA跳变,SCL高电平时采样;MSB高位在前
应答机制 每8位数据后跟1位应答;谁接收谁发应答
寻址方式 7位从机地址 + 1位读写位,地址匹配才响应
方向切换 重复起始信号切换方向,不释放总线
主导权 全程主机主导,从机被动响应

一次通信的完整主线

复制代码
总线空闲 → 主机发起起始 → 发送地址+读写位 → 从机应答 → 
多字节数据传输 → 主机发起停止 → 总线空闲

9. 设备主从

I2C是典型的主从式总线架构,角色分工明确,同时支持多主机扩展。

主从架构拓扑图

复制代码
                  ┌─────────────┐
                  │   主机      │
                  │  Master     │
                  │ 产生SCL     │
                  │ 发起通信     │
                  └──────┬──────┘
                         │
          ┌──────────────┼──────────────┐
          │              │              │
    SCL ──┴──────────────┴──────────────┴── 总线
    SDA ─────────────────────────────────── 总线
          │              │              │
    ┌─────┴────┐   ┌─────┴────┐   ┌─────┴────┐
    │ 从机1    │   │ 从机2    │   │ 从机3    │
    │ Slave    │   │ Slave    │   │ Slave    │
    │ 被动响应 │   │ 被动响应 │   │ 被动响应 │
    └──────────┘   └──────────┘   └──────────┘

9.1 核心角色定义

角色 定位 时钟控制权 通信发起权 数量限制
主机 Master 总线主导者 ✅ 产生SCL时钟,决定波特率 ✅ 发起起止、控制流程 可多个,同一时间仅一个占用总线
从机 Slave 被动响应者 ❌ 跟随时钟,不产生时钟 ❌ 不主动发起 7位地址最多127个,实际受电容限制

9.2 主机核心职责

  1. 时钟生成:配置波特率分频器,输出稳定的SCL时钟
  2. 总线管理:检测总线空闲,发起起始、停止、重复起始
  3. 设备寻址:发送从机地址,选中目标设备并指定方向
  4. 流程控制:通过ACK/NACK控制传输长度,切换读写方向
  5. 错误处理:检测NACK、总线错误,异常时终止并复位

9.3 从机核心职责

  1. 地址监听:持续监听总线,检测起始信号后开始比对地址
  2. 地址响应:地址匹配则输出ACK,不匹配则忽略本次通信
  3. 数据收发:写方向接收数据,读方向主动发送数据
  4. 时钟拉伸(可选):处理不及时则拉低SCL,延长时钟周期,让主机等待
  5. 从不主动通信:永远不会主动发起传输,所有动作由主机驱动

9.4 多主机与总线仲裁

I2C支持多主机,硬件通过线与特性自动完成仲裁,无需额外控制线。

仲裁原理

多个主机同时发数据时,边发边检测SDA总线电平:

  • 自己输出0,总线也是0 → 正常,继续发送
  • 自己输出1,总线却是0 → 其他主机拉低了总线,自己仲裁失败,立即停止发送
仲裁特点
  • 过程不破坏正在传输的数据
  • 数据中0出现越早的主机,优先级越高
  • 失败的主机等待总线空闲后可再次发起

9.5 时钟拉伸(从机流控)

从机的核心功能,也是主从交互的典型机制:

  1. 触发:从机处理速度跟不上,在SCL低电平期间持续拉低SCL
  2. 响应:主机检测到SCL无法拉高,自动等待
  3. 结束:从机处理完成,释放SCL,总线继续
  4. 作用:硬件级流控,低速从机也能和高速主机正常通信

🚩 高频面试题库

基础概念题

  1. I2C有几根线?分别是什么?

    两根信号:SCL串行时钟线、SDA串行数据线;

    另外必须共地,提供电平参考基准。

  2. I2C起始信号和停止信号怎么定义?

    SCL高电平时,SDA由高变低是起始信号

    SCL高电平时,SDA由低变高是停止信号

  3. I2C数据是高位在前还是低位在前?

    高位(MSB)在前,和UART的低位在前相反。

  4. ACK和NACK是什么?谁发出?

    ACK是应答,接收方拉低SDA,表示成功接收;

    NACK是非应答,接收方释放SDA,表示失败/结束。

    遵循"谁接收谁发"的原则**:****主机发数据时从机发ACK;**从机发数据时主机发ACK/NACK。

  5. I2C是同步还是异步?半双工还是全双工?

    同步通信,所有数据和SCL时钟严格同步;

    半双工,只有一根数据线,同一时间只能单向传输。

进阶协议题

  1. 什么是重复起始信号?和停止再起始有什么区别?

    重复起始是不发停止信号,直接再次发起起始,用于切换读写方向。

    区别:重复起始不释放总线,不会被其他主机抢占,时序开销小;停止再起始会释放总线,可能被占用,开销大。

  2. 读操作最后一个字节为什么要发NACK?发ACK会怎么样?

    发NACK是通知从机停止发送数据,结束传输。

    如果发ACK,从机会认为还要继续,主动发送下一个字节,导致总线失控。

  3. I2C的7位地址和8位地址有什么区别?

    7位是设备本身的硬件地址;

    8位是包含读写控制位的地址字节,高7位是设备地址,最低位是读写位

  4. I2C总线上可以挂多少个设备?由什么决定?

    理论上7位地址最多127个可用地址;

    实际受总线寄生电容限制,一般单板不超过10~20个设备。

硬件原理题

  1. I2C为什么必须外接上拉电阻?内部上拉行不行?

    I2C是开漏输出,芯片本身不能输出高电平,必须靠外部上拉电阻产生高电平。 内部上拉是40KΩ弱上拉,阻值太大,总线上升沿慢,驱动能力不足,通信不稳定,不能替代外部上拉。

  2. I2C为什么要用开漏输出?推挽行不行?

    开漏才能实现线与逻辑,多设备挂总线不冲突,支持仲裁和时钟拉伸。

    推挽不行,两个设备一个拉高一个拉低会造成VCC到GND的直流通路,大电流烧坏引脚。

  3. 什么是线与?I2C线与有什么作用?

    总线上只要一个设备拉低就是低电平,全部释放才是高电平,逻辑上等于与运算。 作用:多设备不短路、多主机仲裁、时钟拉伸、应答机制。

  4. 什么是时钟拉伸?有什么用?

    从机主动拉低SCL延长时钟周期,告诉主机自己还没准备好。

    作用是实现硬件级流控,让低速从机也能和高速主机正常通信。

  5. SCL高电平时SDA为什么不能跳变?跳变会怎么样?

    SCL高电平时SDA跳变会被硬件识别为起始或停止信号,破坏正常数据传输,导致通信异常。 普通数据位的SDA跳变必须在SCL低电平期间完成。

  6. I2C和UART有什么核心区别?

    I2C:两线、同步、半双工、多设备总线、开漏线与、地址寻址;

    UART:三线、异步、全双工、点对点、TTL推挽电平、靠波特率同步。


相关推荐
QQ_21696290961 小时前
基于微服务架构的店铺管理系统的设计与实现
大数据·spring boot·后端·spring·微服务·小程序·架构
威嵌神州1 小时前
星载计算机产品方案:商业卫星国产化平台
嵌入式硬件
老当益壮梁奶奶1 小时前
51单片机中断系统详解
c语言·单片机·嵌入式硬件·学习·51单片机
Brilliantwxx1 小时前
【STM32】 I2C 外设内部硬件结构和状态位
开发语言·stm32·单片机·嵌入式硬件
zhangzhangkeji1 小时前
stm32 笔记 - 3-1:GPIO 输出
stm32·arm
天空'之城10 小时前
单片机基础核心知识点汇总(三十五)
单片机·嵌入式硬件
secondyoung10 小时前
I2C通信协议详解
arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu·51单片机
头茬韭菜10 小时前
第 1 篇:「Glass-Box 的骨架」——全景架构与六层数据流水线
架构·agent·semantica
anew___10 小时前
蜂鸣器播放音乐——让 Arduino 唱出旋律
c++·stm32·单片机·嵌入式硬件·c