前言
在嵌入式开发中,I2C 是使用频率最高、面试提问最多、坑最多的通信协议之一。
几乎所有传感器(温湿度、陀螺仪)、存储芯片(AT24C02、EEPROM)、OLED屏幕都基于 I2C 通信。
很多同学只会调 HAL 库,看不懂时序、分不清收发方向、不懂重复起始原理、一对多容易踩坑,也不了解片上I2C核心寄存器,面试一问就废。
本文一次性讲透:I2C硬件原理、通信模式、片上外设核心寄存器、完整帧格式、一对多挂载、读写时序、重复起始、高频面试题,零基础也能彻底吃透。
一、I2C 基础核心属性(面试必背)
- 通信类型:同步通信

I2C 是同步串行通信。

-
自带独立时钟线 SCL
-
时钟完全由主机(STM32) 产生
-
所有从机跟随主机时钟采样数据
同步通信特点
- 同步通信(I2C / SPI):有公共时钟线,收发双方严格对齐时钟边沿,传输稳定、速率可控
一句话总结:有时钟线=同步
- 传输模式:半双工通信

I2C 是半双工,不是全双工!
-
半双工:同一时刻,只能单向传输
-
要么主机发、从机收
-
要么从机发、主机收
-
全双工:收发可以同时进行(代表:SPI)
为什么 I2C 只能半双工?
核心硬件原因:只有一根双向数据线 SDA
同一根线不能同时收发数据,必须分时复用。
- 主从架构 & 一对多总线特性

I2C属于总线型,支持一对多挂载
-
主机:STM32,一条总线只允许1个主机,产生SCL时钟,掌控总线。
-
从机:AT24C02、传感器、OLED等外设,同一条SDA/SCL可以并联挂载多个从设备。
SPI是片选CS区分设备;I2C不靠片选引脚,依靠7位从设备地址区分总线上不同外设。
⚠一对多硬性约束:
-
总线上所有从机必须地址互不相同,地址冲突直接通信彻底错乱。
-
SDA、SCL都需要上拉电阻(4.7K‑10K),多个设备并联,上拉电阻只需要一组,不需要每个设备单独加。
-
总线有电容负载上限,挂载设备不能无限多,设备越多,总线电容越大,最高通信速率要降下来。
对比:SPI每多一个从机就要多一根CS片选线;I2C一对多只需要SDA、SCL两根线,硬件接线简洁,这是I2C最大优势。
4.硬件接线特点
仅两根总线,总线式拓扑,支持多设备挂载:
-
SCL :时钟线(主机输出)
-
SDA :数据线(双向收发)
所有I2C设备并联挂载在总线上,依靠设备地址区分设备,互不冲突。
二、STM32 I2C片上外设重要寄存器(面试常考)
说明:以标准STM32F1系列I2C寄存器为例,HAL库底层就是操作这些寄存器。

- I2C_CR1 控制寄存器1
核心控制开关寄存器
-
PE:I2C外设使能位,1开启I2C,0关闭
-
START:写1,硬件产生起始信号Start
-
STOP:写1,硬件产生停止信号Stop
-
ACK:应答使能位;接收时,1=发送ACK,0=发送NACK
-
POS:应答位置控制位
重点:重复起始,就是软件再次置位START位,不置STOP。
- I2C_CR2 控制寄存器2
配置时钟与中断
-
FREQ:I2C外设输入时钟频率,决定时序基准
-
ITERREN / ITEVTEN / ITBUFEN:错误、事件、缓冲区中断使能
- I2C_OAR1 自身地址寄存器
本机(STM32当做从机时)的I2C设备地址。
注意:这是STM32自己的从机地址,不是外部AT24C02的从机地址!很多同学搞混。主机模式下该寄存器可以不配置。
- I2C_DR 数据寄存器(数据移位缓冲区)
唯一数据出入口,8位。
-
发送:往DR写入字节,硬件自动把字节搬到移位寄存器,输出到SDA
-
接收:总线上收到字节,硬件存入DR,CPU读取DR拿到数据
不是直接操作SDA引脚,所有收发全部经过DR寄存器中转。
- I2C_SR1 状态寄存器1(事件标志,极其重要)
全部是硬件自动置位,软件清零。
-
SB:起始信号发送完成标志。发完Start,SB置1。
-
ADDR:地址发送完成,收到ACK后置1。
-
TXE:发送寄存器为空。DR已经空,可以写下一个待发送字节。
-
RXNE:接收寄存器非空。DR里面收到新数据,可以读走。
-
TC:传输完成标志,当前字节序列全部发送完毕。
-
AF:应答失败NACK标志,没有收到ACK,通信异常。
面试高频区分:
TXE:数据寄存器空,可以继续填下一个数据;
TC:整段所有数据全部发完;
TXE是字节级别,TC是整个传输序列级别。
- I2C_SR2 状态寄存器2
总线状态、主从模式标志位。
-
MSL:1代表当前是主机模式;0代表从机模式。
-
BUSY:总线忙标志位,1说明I2C总线正在通信。
寄存器简单对比总结
表格

补充理解:HAL库的 HAL_I2C_IsDeviceReady 底层就是操作寄存器,发送从机地址,看ADDR标志位是否置位,判断有没有ACK应答。
三、I2C 四大核心时序信号(底层根基)
所有 I2C 通信,全部由这四个信号拼接而成:
- 起始信号 Start
SCL 高电平期间,SDA 由高拉低
标志:一次通信正式开始,总线被主机占用
- 停止信号 Stop
SCL 高电平期间,SDA 由低拉高
标志:一次通信彻底结束,释放I2C总线
- 数据有效电平
SCL 高电平读取数据
SCL 低电平切换 SDA 电平
- ACK / NACK 应答(字节握手)
每传输完 1 个8位字节,必须应答一次:
-
ACK(应答):SDA 拉低 → 接收成功
-
NACK(无应答):SDA 拉高 → 接收失败 / 停止传输
重点区分(90%新手混淆)
Stop:整次通信结束,释放总线,只在最后出现一次
ACK:单个字节握手,每发一字节必应答
四、I2C 完整帧格式(逐帧拆解)
一次标准 I2C 通信帧结构:
起始位 → 设备地址帧 → ACK → 字地址帧 → ACK → 数据帧 → ACK → 停止位


- 起始帧(Start)
开启总线会话,锁定总线,禁止其他设备干扰
- 设备地址帧(7位地址 + 1位读写位)
字节格式: 7位设备地址 + 1位读写控制位
-
最低位 0:写操作(主机→从机)
-
最低位 1:读操作(从机→主机)
示例 AT24C02:
-
写地址: 0xA0
-
读地址: 0xA1
✨一对多关键点:主机发送设备地址,总线上所有从机同时收到该字节;只有地址匹配的那一台从机回复ACK,其余从机直接忽略后面的数据。
- 字地址帧
从机设备内部存储地址
比如 AT24C02 有 0~255 共256个存储字节,字地址就是指定「读写哪一格存储单元」
通俗区分:
-
设备地址:总线上找哪个芯片(一对多靠这个选中目标设备)
-
字地址:找芯片里面的哪个存储位置
- 数据帧
8位一字节,可连续多字节传输
-
写阶段:主机发数据、从机应答
-
读阶段:从机发数据、主机应答
- 停止帧(Stop)
结束本次会话,释放总线,其他从设备才有机会被访问。
五、最核心难点:重复起始信号(Repeated Start)
- 什么是重复起始?
不发送 Stop、不释放总线,直接再次发送 Start 信号(CR1寄存器START位置1)
- 为什么必须用重复起始?(面试必考,一对多场景尤其重要)
以 AT24C02 随机读 为例:
读数据必须分两步:
-
写操作:发送设备写地址 + 字地址(定位存储位置)
-
读操作:读取该位置数据
错误写法:中间发 Stop
Start → 写地址 → 字地址 → Stop → Start → 读地址 → 读数据
致命缺陷(一对多总线会放大这个bug):
-
Stop 会释放总线,留出空闲窗口期
-
总线上其他挂载的从设备,有可能抢占、干扰总线波形
-
芯片内部地址指针可能丢失、偏移,导致读错数据
标准正确写法(手册规定)
Start → 写地址 → 字地址 → 重复起始 → 读地址 → 读数据 → Stop
-
重复起始核心作用
-
全程占用总线,杜绝一对多总线上其他设备干扰
-
保留从机内部地址指针,不重置位置
-
无缝切换「写定位」→「读数据」模式,通信稳定无bug
一句话绝杀面试答案
重复起始无需释放总线,保留从机地址指针,避免一对多总线上其他外设干扰,实现读写方向无缝切换,是 I2C 随机读写的标准时序。
六、I2C 读写全时序彻底拆解(以 AT24C02 为例)
I2C 写时序(主机发、从机收)
-
起始信号
-
设备写地址 + ACK(一对多总线只有地址匹配设备应答)
-
内部字地址 + ACK
-
待写入数据(可多字节连续发送)+ ACK
-
停止信号
I2C 随机读时序(最常考)
-
起始
-
设备写地址 + ACK(写方向:定位地址)
-
字地址 + ACK(指定读取位置)
-
重复起始(关键!不释放总线)
-
设备读地址 + ACK(切换为读方向)
-
从机发送数据 → 主机接收
-
主机 NACK(终止传输)
-
停止信号
核心收发方向总结
-
写阶段:SDA由主机驱动,STM32发数据
-
读阶段:SDA由从机驱动,外设发数据
-
全程SCL时钟永远由主机产生
七、I2C一对多挂载踩坑点(工程&面试)
-
地址绝对不能冲突
两个设备7位I2C地址一样,总线直接异常,ACK乱跳,读写随机出错。部分芯片支持引脚修改硬件地址,就是为一对多设计。
-
上拉电阻只需要一组
多条从机并联,SDA/SCL外部只放一组4.7K上拉,不要每个设备都焊接上拉,并联阻值过小会导致电平异常。
-
总线负载不能过重
挂载设备越多,总线电容越大,速率要降低;设备很多时,不要跑400K高速模式,降为100K标准模式。
-
访问完一个设备最好发Stop
访问完设备后发出Stop释放总线,别的从设备才可以被主机访问;如果一直占用总线,其他外设完全无法通信。
-
HAL库 HAL_I2C_IsDeviceReady() 一对多用法
可以写循环遍历所有设备地址,批量检测总线上哪些从设备在线,用于设备热检测。
误区:I2C一对多,不是多个主机,只能一个主机,多个从机;多主机I2C很少在单片机工程使用,竞赛、项目基本不用。
八、I2C面试高频题(直接背)
Q1:I2C是同步还是异步?单工/半双工/全双工?
答:同步通信,半双工;有SCL时钟线;单根SDA数据线,同一时刻只能单向传输。
Q2:I2C一对多靠什么区分不同外设?SPI靠什么?
答:I2C依靠7位从设备地址区分,不需要额外引脚;SPI依靠CS片选引脚,每增加一个从机就要增加一根CS信号线。I2C一对多接线简洁。
Q3:什么是重复起始Repeated Start,底层寄存器怎么产生?作用?
答:不发送Stop、不释放总线,再次置位CR1寄存器START位产生起始;用于随机读,保留从机内部指针;一对多总线上避免其他设备干扰总线,无缝切换读写方向。
Q4:TXE 和 TC标志位有什么区别?
答:TXE:发送数据寄存器为空,可以填入下一个字节;TC:整个序列全部字节发送完成。TXE是单字节级别,TC代表整段传输结束。
Q5:Stop和ACK的区别?
答:ACK是每一个字节后的握手应答;Stop代表整轮通信结束,释放I2C总线,Stop一趟通信末尾只出现一次。
Q6:I2C总线上多个从机,主机发地址帧发生了什么?
答:全部从机都会收到该字节;只有地址匹配的从机回复ACK,其余从机忽略后续所有数据。
Q7:I2C通信出现NACK无应答,SR1哪个标志置1?可能什么原因?
答:AF(应答失败标志)置1。
-
从设备硬件没上电;
-
SDA/SCL接反,缺少上拉电阻;
-
设备地址写错,一对多发生地址冲突;
-
EEPROM写周期忙,处于内部烧写等待(AT24C02最多5ms忙等待);
-
总线受电磁干扰。
Q8:AT24C02写完之后为什么要延时?
答:EEPROM接收到停止条件后,内部需要页烧写时间,最大5ms,此时设备处于忙状态,不会回复ACK;需要等待写周期完成,再继续读写,可以调用 HAL_I2C_IsDeviceReady() 轮询就绪状态。
Q9:7位I2C地址,读写位怎么处理?OAR1寄存器存的是什么?
答:7位设备地址左移1位,最低bit置0为写地址,置1为读地址。例:7位地址0x50,写0xA0,读0xA1。OAR1保存STM32自己作为从机的地址,不是外部外设地址。
九、小结
-
I2C:同步、半双工,SCL时钟+SDA双向数据线。
-
STM32 I2C核心寄存器:CR1控制启停,DR数据缓冲区,SR1存放TXE/RXNE/TC/SB/ADDR事件标志,SR2判断总线忙、主从模式。分清TXE与TC。
-
总线一对多,依靠从设备地址选中外设,禁止地址冲突。
-
Start、Repeated Start、Stop、ACK/NACK构成全部时序。
-
重复起始解决随机读时序,一对多场景规避总线干扰。
-
Stop释放总线,一对多工程中,访问完设备务必释放总线。
-
EEPROM写完有内部烧写忙期,需要等待就绪,不能连续高速读写。
结语
本文完整梳理了I2C协议原理、STM32片上寄存器、时序帧格式、一对多总线挂载,同时整理了嵌入式面试高频考点。I2C看着简单,但实际开发、电赛调试里坑点很多,建议大家收藏⭐起来,遇到问题可以回来翻阅复习。
觉得内容有帮助的话,欢迎点赞👍支持。后续会继续更新SPI通信协议详解、STM32实战驱动例程以及嵌入式面试刷题内容,点个关注不迷路,我们下期再见!