【嵌入式硬核精讲】I2C通信协议万字详解(时序帧格式、同步半双工、重复起始、一对多挂载、寄存器、面试必考)

前言

在嵌入式开发中,I2C 是使用频率最高、面试提问最多、坑最多的通信协议之一。

几乎所有传感器(温湿度、陀螺仪)、存储芯片(AT24C02、EEPROM)、OLED屏幕都基于 I2C 通信。

很多同学只会调 HAL 库,看不懂时序、分不清收发方向、不懂重复起始原理、一对多容易踩坑,也不了解片上I2C核心寄存器,面试一问就废。

本文一次性讲透:I2C硬件原理、通信模式、片上外设核心寄存器、完整帧格式、一对多挂载、读写时序、重复起始、高频面试题,零基础也能彻底吃透。

一、I2C 基础核心属性(面试必背)

  1. 通信类型:同步通信

I2C 是同步串行通信。

  • 自带独立时钟线 SCL

  • 时钟完全由主机(STM32) 产生

  • 所有从机跟随主机时钟采样数据

同步通信特点

  • 同步通信(I2C / SPI):有公共时钟线,收发双方严格对齐时钟边沿,传输稳定、速率可控

一句话总结:有时钟线=同步

  1. 传输模式:半双工通信

I2C 是半双工,不是全双工!

  • 半双工:同一时刻,只能单向传输

  • 要么主机发、从机收

  • 要么从机发、主机收

  • 全双工:收发可以同时进行(代表:SPI)

为什么 I2C 只能半双工?

核心硬件原因:只有一根双向数据线 SDA

同一根线不能同时收发数据,必须分时复用。

  1. 主从架构 & 一对多总线特性

I2C属于总线型,支持一对多挂载

  • 主机:STM32,一条总线只允许1个主机,产生SCL时钟,掌控总线。

  • 从机:AT24C02、传感器、OLED等外设,同一条SDA/SCL可以并联挂载多个从设备。

SPI是片选CS区分设备;I2C不靠片选引脚,依靠7位从设备地址区分总线上不同外设。

⚠一对多硬性约束:

  1. 总线上所有从机必须地址互不相同,地址冲突直接通信彻底错乱。

  2. SDA、SCL都需要上拉电阻(4.7K‑10K),多个设备并联,上拉电阻只需要一组,不需要每个设备单独加。

  3. 总线有电容负载上限,挂载设备不能无限多,设备越多,总线电容越大,最高通信速率要降下来。

对比:SPI每多一个从机就要多一根CS片选线;I2C一对多只需要SDA、SCL两根线,硬件接线简洁,这是I2C最大优势。

4.硬件接线特点

仅两根总线,总线式拓扑,支持多设备挂载:

  • SCL :时钟线(主机输出)

  • SDA :数据线(双向收发)

所有I2C设备并联挂载在总线上,依靠设备地址区分设备,互不冲突。

二、STM32 I2C片上外设重要寄存器(面试常考)

说明:以标准STM32F1系列I2C寄存器为例,HAL库底层就是操作这些寄存器。

  1. I2C_CR1 控制寄存器1

核心控制开关寄存器

  • PE:I2C外设使能位,1开启I2C,0关闭

  • START:写1,硬件产生起始信号Start

  • STOP:写1,硬件产生停止信号Stop

  • ACK:应答使能位;接收时,1=发送ACK,0=发送NACK

  • POS:应答位置控制位

重点:重复起始,就是软件再次置位START位,不置STOP。

  1. I2C_CR2 控制寄存器2

配置时钟与中断

  • FREQ:I2C外设输入时钟频率,决定时序基准

  • ITERREN / ITEVTEN / ITBUFEN:错误、事件、缓冲区中断使能

  1. I2C_OAR1 自身地址寄存器

本机(STM32当做从机时)的I2C设备地址。

注意:这是STM32自己的从机地址,不是外部AT24C02的从机地址!很多同学搞混。主机模式下该寄存器可以不配置。

  1. I2C_DR 数据寄存器(数据移位缓冲区)

唯一数据出入口,8位。

  • 发送:往DR写入字节,硬件自动把字节搬到移位寄存器,输出到SDA

  • 接收:总线上收到字节,硬件存入DR,CPU读取DR拿到数据

不是直接操作SDA引脚,所有收发全部经过DR寄存器中转。

  1. I2C_SR1 状态寄存器1(事件标志,极其重要)

全部是硬件自动置位,软件清零。

  • SB:起始信号发送完成标志。发完Start,SB置1。

  • ADDR:地址发送完成,收到ACK后置1。

  • TXE:发送寄存器为空。DR已经空,可以写下一个待发送字节。

  • RXNE:接收寄存器非空。DR里面收到新数据,可以读走。

  • TC:传输完成标志,当前字节序列全部发送完毕。

  • AF:应答失败NACK标志,没有收到ACK,通信异常。

面试高频区分:

TXE:数据寄存器空,可以继续填下一个数据;

TC:整段所有数据全部发完;

TXE是字节级别,TC是整个传输序列级别。

  1. I2C_SR2 状态寄存器2

总线状态、主从模式标志位。

  • MSL:1代表当前是主机模式;0代表从机模式。

  • BUSY:总线忙标志位,1说明I2C总线正在通信。

寄存器简单对比总结

表格

补充理解:HAL库的 HAL_I2C_IsDeviceReady 底层就是操作寄存器,发送从机地址,看ADDR标志位是否置位,判断有没有ACK应答。

三、I2C 四大核心时序信号(底层根基)

所有 I2C 通信,全部由这四个信号拼接而成:

  1. 起始信号 Start

SCL 高电平期间,SDA 由高拉低

标志:一次通信正式开始,总线被主机占用

  1. 停止信号 Stop

SCL 高电平期间,SDA 由低拉高

标志:一次通信彻底结束,释放I2C总线

  1. 数据有效电平

SCL 高电平读取数据

SCL 低电平切换 SDA 电平

  1. ACK / NACK 应答(字节握手)

每传输完 1 个8位字节,必须应答一次:

  • ACK(应答):SDA 拉低 → 接收成功

  • NACK(无应答):SDA 拉高 → 接收失败 / 停止传输

重点区分(90%新手混淆)

Stop:整次通信结束,释放总线,只在最后出现一次

ACK:单个字节握手,每发一字节必应答

四、I2C 完整帧格式(逐帧拆解)

一次标准 I2C 通信帧结构:

起始位 → 设备地址帧 → ACK → 字地址帧 → ACK → 数据帧 → ACK → 停止位

  1. 起始帧(Start)

开启总线会话,锁定总线,禁止其他设备干扰

  1. 设备地址帧(7位地址 + 1位读写位)

字节格式: 7位设备地址 + 1位读写控制位

  • 最低位 0:写操作(主机→从机)

  • 最低位 1:读操作(从机→主机)

示例 AT24C02:

  • 写地址: 0xA0

  • 读地址: 0xA1

✨一对多关键点:主机发送设备地址,总线上所有从机同时收到该字节;只有地址匹配的那一台从机回复ACK,其余从机直接忽略后面的数据。

  1. 字地址帧

从机设备内部存储地址

比如 AT24C02 有 0~255 共256个存储字节,字地址就是指定「读写哪一格存储单元」

通俗区分:

  • 设备地址:总线上找哪个芯片(一对多靠这个选中目标设备)

  • 字地址:找芯片里面的哪个存储位置

  1. 数据帧

8位一字节,可连续多字节传输

  • 写阶段:主机发数据、从机应答

  • 读阶段:从机发数据、主机应答

  1. 停止帧(Stop)

结束本次会话,释放总线,其他从设备才有机会被访问。

五、最核心难点:重复起始信号(Repeated Start)

  1. 什么是重复起始?

不发送 Stop、不释放总线,直接再次发送 Start 信号(CR1寄存器START位置1)

  1. 为什么必须用重复起始?(面试必考,一对多场景尤其重要)

以 AT24C02 随机读 为例:

读数据必须分两步:

  1. 写操作:发送设备写地址 + 字地址(定位存储位置)

  2. 读操作:读取该位置数据

错误写法:中间发 Stop

Start → 写地址 → 字地址 → Stop → Start → 读地址 → 读数据

致命缺陷(一对多总线会放大这个bug):

  1. Stop 会释放总线,留出空闲窗口期

  2. 总线上其他挂载的从设备,有可能抢占、干扰总线波形

  3. 芯片内部地址指针可能丢失、偏移,导致读错数据

标准正确写法(手册规定)

Start → 写地址 → 字地址 → 重复起始 → 读地址 → 读数据 → Stop

  1. 重复起始核心作用

  2. 全程占用总线,杜绝一对多总线上其他设备干扰

  3. 保留从机内部地址指针,不重置位置

  4. 无缝切换「写定位」→「读数据」模式,通信稳定无bug

一句话绝杀面试答案

重复起始无需释放总线,保留从机地址指针,避免一对多总线上其他外设干扰,实现读写方向无缝切换,是 I2C 随机读写的标准时序。

六、I2C 读写全时序彻底拆解(以 AT24C02 为例)

I2C 写时序(主机发、从机收)

  1. 起始信号

  2. 设备写地址 + ACK(一对多总线只有地址匹配设备应答)

  3. 内部字地址 + ACK

  4. 待写入数据(可多字节连续发送)+ ACK

  5. 停止信号

I2C 随机读时序(最常考)

  1. 起始

  2. 设备写地址 + ACK(写方向:定位地址)

  3. 字地址 + ACK(指定读取位置)

  4. 重复起始(关键!不释放总线)

  5. 设备读地址 + ACK(切换为读方向)

  6. 从机发送数据 → 主机接收

  7. 主机 NACK(终止传输)

  8. 停止信号

核心收发方向总结

  • 写阶段:SDA由主机驱动,STM32发数据

  • 读阶段:SDA由从机驱动,外设发数据

  • 全程SCL时钟永远由主机产生

七、I2C一对多挂载踩坑点(工程&面试)

  1. 地址绝对不能冲突

    两个设备7位I2C地址一样,总线直接异常,ACK乱跳,读写随机出错。部分芯片支持引脚修改硬件地址,就是为一对多设计。

  2. 上拉电阻只需要一组

    多条从机并联,SDA/SCL外部只放一组4.7K上拉,不要每个设备都焊接上拉,并联阻值过小会导致电平异常。

  3. 总线负载不能过重

    挂载设备越多,总线电容越大,速率要降低;设备很多时,不要跑400K高速模式,降为100K标准模式。

  4. 访问完一个设备最好发Stop

    访问完设备后发出Stop释放总线,别的从设备才可以被主机访问;如果一直占用总线,其他外设完全无法通信。

  5. 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。

  1. 从设备硬件没上电;

  2. SDA/SCL接反,缺少上拉电阻;

  3. 设备地址写错,一对多发生地址冲突;

  4. EEPROM写周期忙,处于内部烧写等待(AT24C02最多5ms忙等待);

  5. 总线受电磁干扰。

Q8:AT24C02写完之后为什么要延时?

答:EEPROM接收到停止条件后,内部需要页烧写时间,最大5ms,此时设备处于忙状态,不会回复ACK;需要等待写周期完成,再继续读写,可以调用 HAL_I2C_IsDeviceReady() 轮询就绪状态。

Q9:7位I2C地址,读写位怎么处理?OAR1寄存器存的是什么?

答:7位设备地址左移1位,最低bit置0为写地址,置1为读地址。例:7位地址0x50,写0xA0,读0xA1。OAR1保存STM32自己作为从机的地址,不是外部外设地址。

九、小结

  1. I2C:同步、半双工,SCL时钟+SDA双向数据线。

  2. STM32 I2C核心寄存器:CR1控制启停,DR数据缓冲区,SR1存放TXE/RXNE/TC/SB/ADDR事件标志,SR2判断总线忙、主从模式。分清TXE与TC。

  3. 总线一对多,依靠从设备地址选中外设,禁止地址冲突。

  4. Start、Repeated Start、Stop、ACK/NACK构成全部时序。

  5. 重复起始解决随机读时序,一对多场景规避总线干扰。

  6. Stop释放总线,一对多工程中,访问完设备务必释放总线。

  7. EEPROM写完有内部烧写忙期,需要等待就绪,不能连续高速读写。

结语

本文完整梳理了I2C协议原理、STM32片上寄存器、时序帧格式、一对多总线挂载,同时整理了嵌入式面试高频考点。I2C看着简单,但实际开发、电赛调试里坑点很多,建议大家收藏⭐起来,遇到问题可以回来翻阅复习。

觉得内容有帮助的话,欢迎点赞👍支持。后续会继续更新SPI通信协议详解、STM32实战驱动例程以及嵌入式面试刷题内容,点个关注不迷路,我们下期再见!

相关推荐
粥里有勺糖1 小时前
Harness 学习笔记分享(Part 1)
面试·github·agent
十月的皮皮2 小时前
STM32从零到量产开发:四路继电器工业控制模块开发 -上位机 Program.cs 应用程序入口设计说明
c语言·stm32·单片机·stm32cubemx·hal库
胡萝卜术2 小时前
数据主权与渲染防线:从受控组件到性能优化的完整图景
前端·javascript·面试
LCG元2 小时前
STM32F103+MPU6050互补滤波姿态解算与OLED实时显示实战(附完整工程代码)
stm32·单片机·嵌入式硬件
胡萝卜术2 小时前
复用与并行:从自定义 Hooks 封装状态逻辑,到 Web Worker 的多线程计算
前端·javascript·面试
求知喻2 小时前
FM33L023D外部中断
单片机·嵌入式硬件
小帽子_1233 小时前
STM32+BMS 嵌入式开发:ADC 电压温度采集与总线通信落地教程
stm32·单片机·嵌入式硬件
ShineWinsu3 小时前
对于C++中unordered_map的详细介绍
数据结构·c++·算法·面试·stl·哈希表·unordered_map
szxinmai主板定制专家3 小时前
Zynq MPSoC半导体高精度运动控制方案|FPGA硬件插补实现亚微米级伺服实时控制
人工智能·嵌入式硬件·fpga开发·zynq·运动控制·ethercat