一、I2C 硬件外设与状态监控概述
STM32 的 I2C 外设完全兼容标准 I2C 协议,能够自动处理起始信号、停止信号、地址帧、数据帧和应答信号的收发。在通讯过程中,I2C 外设会通过 状态寄存器 SR1 和 SR2 实时更新当前的工作状态。我们通过读取这些寄存器的特定位,就能精确掌握通讯的每一个环节是否正常完成。
生活类比:I2C 通讯就像两个人打电话。你(主机)先拨号(发送起始信号+从机地址),对方接听(应答),然后你说话(发送数据)或者听对方说话(接收数据),最后挂电话(停止信号)。状态寄存器就像是电话的通话状态提示,告诉你"对方已接听"、"对方正在说话"、"通话已结束"等信息。
二、I2C 主发送器通讯过程
主发送器模式是指 STM32 作为主机,向 I2C 总线上的从设备发送数据。

2.1 整体通讯流程
标准 I2C 主发送流程如下:
起始信号(S) → 从机地址(7位)+写方向(0) → 应答(A) → 数据1 → 应答(A) → ... → 数据N → 应答(A) → 停止信号(P)
2.2 分步详解与硬件事件
每一步操作都会触发相应的硬件事件,我们通过检测这些事件来确认操作完成。
1. 产生起始信号 (S)
-
控制操作 :将 控制寄存器 CR1 的 START 位 置 1
-
硬件事件 :产生 EV5 事件 ,对应 SR1 寄存器的 SB 位 = 1
-
事件含义:起始条件已成功发送到 I2C 总线
2. 发送从机地址与写方向
-
控制操作 :向 数据寄存器 DR 写入从机地址 + 写方向位(最低位为 0)
-
硬件事件 :产生 EV6 事件 (SR1 的 ADDR 位 = 1)和 EV8 事件(SR1 的 TXE 位 = 1)
-
事件含义:
-
ADDR = 1:从机地址已发送完毕,且收到了从机的应答信号
-
TXE = 1:数据寄存器 DR 已为空,可以写入下一个数据
-
3. 发送数据字节
-
控制操作 :向 数据寄存器 DR 写入要发送的字节数据
-
硬件事件 :产生 EV8 事件(SR1 的 TXE 位 = 1)
-
事件含义:当前字节已发送完毕,数据寄存器为空,可以写入下一个字节
-
重复此过程:直到所有数据字节都发送完成
4. 产生停止信号 (P)
-
控制操作 :将 控制寄存器 CR1 的 STOP 位 置 1
-
前置条件 :必须先检测 EV8_2 事件(SR1 的 TXE 位 = 1 且 BTF 位 = 1)
-
事件含义:BTF(字节发送完成)位 = 1 表示不仅数据寄存器为空,移位寄存器中的最后一位数据也已经发送到总线上
重要区别:TXE 只表示数据寄存器为空,而 BTF 表示整个字节都已发送完毕。发送最后一个字节后必须检测 BTF 位,否则可能导致最后一个字节丢失。
三、I2C 主接收器通讯过程
主接收器模式是指 STM32 作为主机,从 I2C 总线上的从设备读取数据。

3.1 整体通讯流程
标准 I2C 主接收流程如下:
起始信号(S) → 从机地址(7位)+读方向(1) → 应答(A) → 数据1 → 主机应答(A) → ... → 数据N → 主机非应答(NACK) → 停止信号(P)
3.2 分步详解与硬件事件
1. 产生起始信号 (S)
-
控制操作 :将 控制寄存器 CR1 的 START 位 置 1
-
硬件事件 :产生 EV5 事件 ,对应 SR1 寄存器的 SB 位 = 1
-
事件含义:起始条件已成功发送到 I2C 总线
2. 发送从机地址与读方向
-
控制操作 :向 数据寄存器 DR 写入从机地址 + 读方向位(最低位为 1)
-
硬件事件 :产生 EV6 事件(SR1 的 ADDR 位 = 1)
-
事件含义:从机地址已发送完毕,且收到了从机的应答信号,从机即将开始发送数据
3. 接收数据字节(非最后一个字节)
-
前置配置 :将 控制寄存器 CR1 的 ACK 位 置 1(使能应答)
-
硬件事件 :产生 EV7 事件(SR1 的 RXNE 位 = 1)
-
事件含义:已成功接收一个字节,数据寄存器 DR 中有有效数据
-
控制操作 :读取 数据寄存器 DR 的值,存入内存变量
-
重复此过程:直到接收完倒数第二个字节
4. 接收最后一个字节并结束通讯
-
前置配置 :在接收最后一个字节之前,将 控制寄存器 CR1 的 ACK 位 置 0(禁止应答),同时将 STOP 位 置 1
-
硬件事件 :产生 EV7 事件(SR1 的 RXNE 位 = 1)
-
控制操作 :读取 数据寄存器 DR 的值,存入内存变量
-
自动操作:I2C 硬件会自动发送非应答信号 (NACK) 和停止信号 (P)
关键要点:I2C 协议规定,主机接收最后一个字节时必须发送非应答信号,通知从机停止发送数据。这个操作必须在读取最后一个字节之前完成。
四、状态寄存器标志位的清除方法
所有状态寄存器的标志位都需要通过特定的软件操作来清除,否则会影响下一次通讯。常用标志位的清除方式如下表:
| 标志位 | 所在寄存器 | 清除方法 |
|---|---|---|
| SB(起始位发送) | SR1 | 先读 SR1,再写 DR 寄存器 |
| ADDR(地址发送完成) | SR1 | 先读 SR1,再读 SR2 寄存器 |
| TXE(发送数据寄存器空) | SR1 | 向 DR 寄存器写入数据 |
| RXNE(接收数据寄存器非空) | SR1 | 读取 DR 寄存器 |
| BTF(字节发送完成) | SR1 | 先读 SR1,再读/写 DR 寄存器 |
| AF(应答失败) | SR1 | 软件写 0 清除 |
五、为什么必须检测状态事件?
STM32 的 CPU 内核运行速度非常快(最高 72 MHz),而 I2C 总线的最高速度只有 400 kHz。CPU 执行一条指令只需要十几个纳秒,而 I2C 发送一个字节需要几十微秒。
如果 CPU 不等待 I2C 硬件完成当前操作就直接进行下一步,会导致:
-
数据还没发送完就被新数据覆盖
-
还没接收到数据就读取 DR 寄存器得到无效值
-
通讯时序混乱,从设备无法正确识别信号
检测状态事件本质上是让 CPU "等一等" I2C 硬件,确保每一步操作都正确完成后再进行下一步。
六、注意事项(避坑指南)
-
必须正确清除标志位:每个标志位的清除方式都不同,错误的清除方式会导致通讯卡死。
-
最后一个字节必须发送非应答:主接收模式下,忘记在最后一个字节前关闭应答会导致从机持续发送数据,总线被占用。
-
发送停止信号前必须检测 BTF 位:只检测 TXE 位会导致最后一个字节丢失。
-
总线忙检测:通讯开始前应检测 SR2 的 BUSY 位,确保总线空闲。
-
超时处理:所有状态检测都应添加超时机制,防止总线故障导致程序无限等待。
参考出处
《零死角玩转 STM32F103 - 指南者》第 24 章 I2C - 读写 EEPROM
STM32F10x 中文参考手册 第 24 章 I2C 接口