I2C协议和设备主从
1. 空闲状态
状态定义
总线无任何通信活动、所有设备均释放总线控制权的静默状态,是所有I2C通信的基准起点。
电气与硬件特征
- 电平表现:SCL、SDA均稳定保持高电平
- 硬件本质:总线上所有设备的开漏输出管全部截止,引脚呈高阻态;总线电平完全由外部上拉电阻拉至VCC,是线与逻辑的全1状态。
- 持续时间:必须保持至少一个总线时钟周期以上,才能被判定为空闲。
硬件判断标志
STM32的I2C外设通过硬件自动检测总线状态,对应I2C_SR2寄存器的BUSY位:
BUSY = 0:总线空闲BUSY = 1:总线处于忙状态,正在通信状态意义
- 只有检测到总线空闲,主机才能合法发起起始信号,启动新通信。
- 所有从机处于低功耗监听模式,内部接收逻辑复位,等待起始信号触发。
- 通信异常复位后,必须等待总线回到空闲态,才能重新初始化外设。
2. Master发送开始状态和停止状态
起始(START/S)与停止(STOP/P)是I2C的两种总线控制信号,只能由主机发起,用来标记一帧通信的起止边界。
起始+停止 时序波形图
一、起始信号(START)
触发条件
SCL稳定在高电平时,SDA由高向低产生下降沿。
逐拍硬件动作
- 主机先确认SCL为高电平(总线空闲态)
- 主机控制SDA引脚的开漏管导通,主动拉低SDA
- 因为线与特性,其他设备都是高阻,总线SDA电平随之变低
- 所有从机在SCL高电平时持续采样SDA,检测到这个下降沿,立即判定为起始信号
- 所有从机内部接收逻辑复位,进入地址监听状态,硬件自动置位BUSY标志
协议作用
- 作为一帧通信的开始标记
- 强制总线上所有从机同步到初始接收状态
- 总线从空闲态正式进入忙态
二、停止信号(STOP)
触发条件
SCL稳定在高电平时,SDA由低向高产生上升沿。
逐拍硬件动作
- 主机先拉低SDA,确保SDA起始电平为低
- 主机拉高SCL,稳定在高电平
- 主机关断SDA的开漏管,释放SDA
- 外部上拉电阻将SDA慢慢拉高,产生上升沿
- 所有从机检测到SCL高电平时的SDA上升沿,判定为停止信号
- 所有从机退出通信状态,总线回到空闲态,BUSY标志硬件清零
协议作用
- 标记本次通信正式结束
- 释放总线控制权,所有设备回到监听状态
- 通知从机传输完成,可以开始内部处理(如EEPROM写入)
关键区分:起止信号 vs 普通数据位
信号类型 SDA跳变时机 识别结果 起始/停止信号 SCL高电平期间跳变 总线控制信号,标记通信边界 普通数据位 SCL低电平期间跳变 有效数据,传输0/1 设计本质:
利用跳变时机的不同,在两根线上同时区分控制信号和数据信号,不需要额外控制线。
3. 调整(数据建立与保持)
"调整"指数据位的电平建立与稳定过程,核心是保证SCL高电平采样时,SDA电平已经稳定,避免采样错误。
单比特数据 时序调整图
SCL: ┌──────────┐ │ │ SDA: ──────┘ └─────── ◄ tSU:DAT ► ◄ tHD:DAT ► 数据建立时间 数据保持时间 SCL上升沿前 SCL下降沿后 数据必须稳定 数据保持不变为什么必须要有调整时间
- 硬件电平跳变需要时间 I2C是开漏输出,高电平靠上拉电阻充电,总线存在寄生电容,电平跳变有上升沿/下降沿,不是瞬间完成的。如果跳变过程中采样,会得到错误值。
- 接收方采样需要稳定值 SCL上升沿后进入高电平,接收方会在高电平时段采样SDA。如果此时SDA还在跳变,采样结果不可靠。
两个核心时序参数(标准模式)
参数 全称 最小值 定义 tSU:DAT 数据建立时间 250ns SDA电平变化稳定 → SCL上升沿 的最小时间差 tHD:DAT 数据保持时间 0ns SCL下降沿 → SDA开始变化 的最小时间差 完整的一位数据调整流程
- SCL拉低,进入调整期 主机拉低SCL,总线进入低电平阶段。
- 发送方切换SDA电平 发送方改变SDA输出,从当前位切换到下一位的电平。
- 等待建立时间 等待SDA电平完全跳变并稳定,满足tSU:DAT要求。
- SCL拉高,进入采样期 主机拉高SCL,进入高电平阶段。
- 接收方采样 SCL高电平时,SDA保持稳定,接收方完成电平采样。
- SCL再次拉低 完成一位传输,进入下一位的调整周期。
通俗总结:
SCL低电平是"摆数据"的准备时间,SCL高电平是"读数据"的采样时间。
调整就是在准备时间里把数据摆稳。
4. Master发送从设备地址
起始信号之后,主机发送的第一个字节是地址字节,作用是选中目标从机,并指定本次通信的读写方向。
地址字节结构图(标准7位地址)
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │ A6 │ A5 │ A4 │ A3 │ A2 │ A1 │ A0 │ R/W │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ ◄────────── 7位从机地址 ──────────► ◄─► 读写控制位字段详解
- 高7位:从机硬件地址(A6~A0)
- 每个从机出厂时分配唯一地址,部分设备可通过AD0/AD1等引脚灵活配置地址
- 地址范围:0x00 ~ 0x7F,共128个,其中0x000x07、0x780x7F为协议保留地址
- 寻址过程:总线上所有从机同时接收这7位,硬件自动和自身地址比对,只有匹配的从机才参与本次通信
- 最低位Bit0:读写控制位 R/W
R/W = 0:写方向,主机向从机写入数据R/W = 1:读方向,主机从从机读取数据地址字节计算示例
已知某EEPROM的7位设备地址为
0x50:
- 写操作地址字节:
0x50 << 1 = 0xA0(最低位补0)- 读操作地址字节:
(0x50 << 1) | 0x01 = 0xA1(最低位置1)发送后的硬件行为
- 主机按高位在前(MSB first)的顺序,逐位发送地址字节
- 所有从机同步接收每一位,存入内部移位寄存器
- 8位接收完成后,所有从机同时比对高7位地址
- 地址不匹配:忽略本次通信,继续等待下一次起始信号
- 地址匹配:准备好应答逻辑,等待第9个时钟周期输出ACK
特殊地址
- 广播地址0x00:写操作时所有从机同时响应,读操作时无设备响应
- 通用呼叫地址:属于保留地址,用于特殊功能如广播复位
5. 应答ACK/无应答位NACK
每传输完8位数据,紧跟第9个SCL时钟周期,传输应答信号,是I2C的可靠传输机制。
ACK与NACK 时序对比图
一、应答位 ACK
定义
接收方主动拉低SDA,代表成功接收前一个字节。
核心原则:谁接收,谁发ACK
- 主机发送地址/数据 → 从机回复ACK
- 从机发送数据 → 主机回复ACK
硬件过程(主机发地址、从机回ACK为例)
- 第8个SCL下降沿,8位地址发送完毕,主机释放SDA(开漏管截止,输出高阻)
- SDA总线的控制权交给从机
- 地址匹配的从机,在第9个SCL低电平期间,导通开漏管拉低SDA
- 第9个SCL高电平时,主机检测SDA电平:低电平 = 收到ACK
- 第9个时钟结束,从机关断开漏管,释放SDA,总线回到高电平
二、无应答位 NACK
定义
接收方不动作,SDA保持高电平,代表接收失败、拒绝接收或结束接收。
三种典型场景
- 无设备应答:总线上没有匹配地址的从机,没人拉低SDA,主机检测到高电平,判定设备不存在/不在线。
- 接收出错:从机检测到数据错误,不回复ACK,通知主机重发。
- 读操作结束:主机读取最后一个字节时,回复NACK,明确告诉从机"不用再发了",准备结束通信。
读操作的NACK是必须的:
如果最后一个字节也回复ACK,从机会继续发送下一个字节,导致总线失控。
6. Master写数据
主机向从机写入数据的完整流程,是I2C最基础的主写从操作。
完整写操作 时序图
逐步骤完整流程
- 发起起始信号 主机检测总线空闲后,发送起始信号,总线进入忙状态。
- 发送从机写地址主机发送7位从机地址 + 写位(0),高位在前。
- 接收地址应答 地址匹配的从机拉低SDA,回复ACK。
- 收到ACK:继续通信
- 未收到ACK(NACK):判定设备无响应,主机直接发停止信号终止
- 发送第一个数据字节 主机按高位在前顺序,逐位发送8位数据。 SCL低电平时主机调整SDA电平,SCL高电平时从机采样。
- 接收数据应答 8位数据发完,主机释放SDA,从机回复ACK,确认收到数据。
- 连续多字节传输 重复"发数据→收ACK"的循环,可以连续发送任意多个字节。 多数从机内部地址指针会自动递增,依次写入连续的存储单元。
- 发送停止信号 所有数据发送完成,主机发送停止信号,释放总线,通信结束。
典型应用
向传感器写配置寄存器、向EEPROM写数据、向OLED屏写显存。
7. Master读数据
主机从从机读取数据的流程,核心是SDA控制权的交接------从机主动发数据,主机接收并应答。
带重复起始的完整读操作 时序图
这是最常用的"指定内部地址读"流程:先写从机内部寄存器地址,再读数据,中间用重复起始切换方向。
逐步骤完整流程
- 起始 + 写设备地址****主机发起起始,发送从机写地址,收到ACK。
- 发送内部寄存器地址****发送要读取的从机内部存储/寄存器地址,收到从机ACK。 作用:告诉从机,接下来要读它内部的哪个地址单元。
- 重复起始信号(ReSTART) 不发送停止信号,直接再次发起起始信号。
- 作用:切换总线读写方向,同时不释放总线,避免其他主机抢占
- 对比停止再起始:停止会释放总线,可能被占用,且时序开销更大
- 发送读设备地址 发送从机地址 + 读位(1)****,收到从机ACK。 此时SDA控制权正式从主机移交到从机。
- 接收第一个数据字节 从机主动逐位发送数据,高位在前。 SCL低电平时从机调整SDA电平,SCL高电平时主机采样。
- 主机回复ACK 8位接收完毕,主机拉低SDA回复ACK,告诉从机:继续发下一个。 从机收到ACK,就会发送下一个字节。
- 连续读取多字节 重复"收数据→发ACK",连续读取多个字节,从机内部地址自动递增。
- 最后一字节回复NACK****读取最后一个字节后,主机不拉动SDA,回复NACK。 作用:通知从机停止发送,释放SDA总线控制权。
- 发送停止信号****主机发送停止信号,释放总线,通信结束。
关键注意点
- 读操作时,主机只提供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 主机核心职责
- 时钟生成:配置波特率分频器,输出稳定的SCL时钟
- 总线管理:检测总线空闲,发起起始、停止、重复起始
- 设备寻址:发送从机地址,选中目标设备并指定方向
- 流程控制:通过ACK/NACK控制传输长度,切换读写方向
- 错误处理:检测NACK、总线错误,异常时终止并复位
9.3 从机核心职责
- 地址监听:持续监听总线,检测起始信号后开始比对地址
- 地址响应:地址匹配则输出ACK,不匹配则忽略本次通信
- 数据收发:写方向接收数据,读方向主动发送数据
- 时钟拉伸(可选):处理不及时则拉低SCL,延长时钟周期,让主机等待
- 从不主动通信:永远不会主动发起传输,所有动作由主机驱动
9.4 多主机与总线仲裁
I2C支持多主机,硬件通过线与特性自动完成仲裁,无需额外控制线。
仲裁原理
多个主机同时发数据时,边发边检测SDA总线电平:
- 自己输出0,总线也是0 → 正常,继续发送
- 自己输出1,总线却是0 → 其他主机拉低了总线,自己仲裁失败,立即停止发送
仲裁特点
- 过程不破坏正在传输的数据
- 数据中0出现越早的主机,优先级越高
- 失败的主机等待总线空闲后可再次发起
9.5 时钟拉伸(从机流控)
从机的核心功能,也是主从交互的典型机制:
- 触发:从机处理速度跟不上,在SCL低电平期间持续拉低SCL
- 响应:主机检测到SCL无法拉高,自动等待
- 结束:从机处理完成,释放SCL,总线继续
- 作用:硬件级流控,低速从机也能和高速主机正常通信
🚩 高频面试题库
基础概念题
I2C有几根线?分别是什么?
两根信号:SCL串行时钟线、SDA串行数据线;
另外必须共地,提供电平参考基准。
I2C起始信号和停止信号怎么定义?
SCL高电平时,SDA由高变低是起始信号;
SCL高电平时,SDA由低变高是停止信号。
I2C数据是高位在前还是低位在前?
高位(MSB)在前,和UART的低位在前相反。
ACK和NACK是什么?谁发出?
ACK是应答,接收方拉低SDA,表示成功接收;
NACK是非应答,接收方释放SDA,表示失败/结束。
遵循"谁接收谁发"的原则**:****主机发数据时从机发ACK;**从机发数据时主机发ACK/NACK。
I2C是同步还是异步?半双工还是全双工?
同步通信,所有数据和SCL时钟严格同步;
半双工,只有一根数据线,同一时间只能单向传输。
进阶协议题
什么是重复起始信号?和停止再起始有什么区别?
重复起始是不发停止信号,直接再次发起起始,用于切换读写方向。
区别:重复起始不释放总线,不会被其他主机抢占,时序开销小;停止再起始会释放总线,可能被占用,开销大。
读操作最后一个字节为什么要发NACK?发ACK会怎么样?
发NACK是通知从机停止发送数据,结束传输。
如果发ACK,从机会认为还要继续,主动发送下一个字节,导致总线失控。
I2C的7位地址和8位地址有什么区别?
7位是设备本身的硬件地址;
8位是包含读写控制位的地址字节,高7位是设备地址,最低位是读写位。
I2C总线上可以挂多少个设备?由什么决定?
理论上7位地址最多127个可用地址;
实际受总线寄生电容限制,一般单板不超过10~20个设备。
硬件原理题
I2C为什么必须外接上拉电阻?内部上拉行不行?
I2C是开漏输出,芯片本身不能输出高电平,必须靠外部上拉电阻产生高电平。 内部上拉是40KΩ弱上拉,阻值太大,总线上升沿慢,驱动能力不足,通信不稳定,不能替代外部上拉。
I2C为什么要用开漏输出?推挽行不行?
开漏才能实现线与逻辑,多设备挂总线不冲突,支持仲裁和时钟拉伸。
推挽不行,两个设备一个拉高一个拉低会造成VCC到GND的直流通路,大电流烧坏引脚。
什么是线与?I2C线与有什么作用?
总线上只要一个设备拉低就是低电平,全部释放才是高电平,逻辑上等于与运算。 作用:多设备不短路、多主机仲裁、时钟拉伸、应答机制。
什么是时钟拉伸?有什么用?
从机主动拉低SCL延长时钟周期,告诉主机自己还没准备好。
作用是实现硬件级流控,让低速从机也能和高速主机正常通信。
SCL高电平时SDA为什么不能跳变?跳变会怎么样?
SCL高电平时SDA跳变会被硬件识别为起始或停止信号,破坏正常数据传输,导致通信异常。 普通数据位的SDA跳变必须在SCL低电平期间完成。
I2C和UART有什么核心区别?
I2C:两线、同步、半双工、多设备总线、开漏线与、地址寻址;
UART:三线、异步、全双工、点对点、TTL推挽电平、靠波特率同步。









