STM32 I2C 外设内部硬件结构和状态位 深度详解
一、整体结构总览
STM32 的 I2C 是完整的硬件协议引擎,挂载在 APB1 总线上,所有协议时序(起始/停止、应答、移位、地址检测、仲裁)全部由硬件自动完成,CPU 仅通过寄存器读写数据和配置。
内部整体框图
核心层级
- 总线接口层:和APB1总线对接,CPU通过寄存器配置外设、读写数据
- 核心协议层:时钟分频、发送移位、接收移位、地址匹配,是协议的硬件实现
- 控制逻辑层:起始停止产生、应答控制、总线仲裁、时钟拉伸
- 物理引脚层:开漏输出驱动、输入施密特滤波,直接连外部GPIO引脚
二、模块1:总线接口与时钟单元
2.1 总线接口
- 挂载在 APB1 低速外设总线,输入时钟为 PCLK1(标准配置36MHz)
- 所有寄存器都映射在APB1外设地址空间,CPU通过AHB-APB桥读写寄存器
- 呼应RCC知识点:操作前必须开启
RCC_APB1ENR的I2C时钟位,否则外设冻结,寄存器不可访问2.2 时钟配置与分频单元
这是I2C波特率的硬件来源,对应
I2C_CR2和I2C_CCR两个寄存器。(1)输入时钟配置(CR2的FREQ位)
- CR2寄存器的低6位
FREQ[5:0],写入APB1总线的实际时钟频率(单位MHz)- 作用:作为内部分频、时序补偿的基准
- 高频坑:必须准确写入实际PCLK1频率,写错会导致波特率严重偏差、时序错误
(2)波特率分频器(CCR寄存器)
核心功能:把PCLK1分频得到SCL总线时钟。
- 标准模式(100kHz):SCL低电平和高电平时间相等,各占
CCR个PCLK周期- 快速模式(400kHz):可配置高低电平占空比(16:9 或 2:1) 对应CCR寄存器的
DUTY位(3)上升时间补偿(TRISE寄存器)
- 因为开漏输出+总线电容,SCL上升沿有斜率,不是方波
- TRISE寄存器配置上升沿补偿周期,匹配总线上升时间,保证采样点准确
- 标准模式下默认=PCLK1(MHz)+1,例如36MHz时配置为37
三、模块2:发送数据通路
负责把CPU写入的并行数据,转换成串行逐位输出到SDA总线,自动插入起始、停止、应答时序。
发送通路硬件组成
APB总线 → 发送数据寄存器 TDR → 发送移位寄存器 TSR → 输出控制 → SDA引脚 ↑ 起始/停止产生逻辑(1)发送数据寄存器 TDR
- 就是CPU读写的
I2C_DR寄存器(写操作时对应TDR)- 作用:数据缓冲,CPU写一个字节进来,硬件自动移入移位寄存器
- 状态标志:
TXE(发送数据寄存器空),硬件置1表示可以写下一个字节- 写满后硬件自动清0,数据移入移位寄存器
(2)发送移位寄存器 TSR
- 核心功能:并行转串行,把8位并行数据按位逐位移出
- 移位节拍:由SCL时钟同步,每个SCL周期移出1位
- 传输顺序:高位(MSB)在前,先移出最高位
- 状态标志:
BTF(字节发送完成),8位全部移出后置1(3)起始/停止产生逻辑
- 由
I2C_CR1的START位、STOP位触发- 硬件自动按照协议时序,在SCL高电平时控制SDA跳变,产生起始/停止信号
- 起始完成后硬件自动清零START位,置位**
SB(起始完成)**标志(4)输出驱动
- SDA、SCL都是开漏NMOS输出结构,只能主动拉低,高电平靠外部上拉
- 呼应GPIO知识点:和GPIO开漏输出是同一套硬件电路,复用模式下控制权交给I2C外设
主机发送一个字节的完整内部流程
- CPU把数据写入
I2C_DR,****TXE标志硬件清0- 硬件自动把数据从TDR移入TSR移位寄存器
- TDR空出,
TXE硬件置1,可以写下一个- 移位寄存器在SCL时钟驱动下,逐位把数据移出到SDA引脚
- 8位全部移出,
BTF标志置1,字节发送完成- 硬件自动等待第9个时钟周期,采样SDA的ACK电平
四、模块3:接收数据通路
负责把SDA总线上的串行数据,转换成并行字节,存入接收寄存器,通知CPU读取。
接收通路硬件组成
SDA引脚 → 输入滤波 → 接收移位寄存器 RSR → 接收数据寄存器 RDR → APB总线 ↑ 地址匹配比较器 ↑ 自身地址寄存器OAR(1)输入滤波与整形
- SDA输入先经过数字噪声滤波器,滤除总线上的窄毛刺
- 再经过施密特触发器整形,得到干净的数字电平
- 作用:提高总线抗干扰能力,避免误判
(2)接收移位寄存器 RSR
- 核心功能:串行转并行,SCL每个时钟周期,采样1位SDA电平,移入寄存器
- 采样时机:SCL高电平的中间时刻,和协议时序一致
- 传输顺序:高位在前,先收到的是最高位
(3)接收数据寄存器 RDR
- 和发送TDR共用同一个地址
I2C_DR,读操作时对应RDR- 作用:接收缓冲,一帧数据收完后,硬件自动把RSR的数据移入RDR
- 状态标志:
RXNE(接收数据寄存器非空),置1表示CPU可以读取- CPU读DR后,硬件自动清零RXNE
**(**4)地址匹配单元
- 包含自身地址寄存器OAR,软件写入本设备的从机地址
- 地址比较器:收到地址字节后,自动对比高7位地址和自身OAR地址
- 匹配:置位
ADDR标志,硬件自动拉低SDA回复ACK- 不匹配:忽略本次通信,继续等待下一次起始
主机接收一个字节的完整内部流程
- SDA总线数据经过滤波整形,送入接收移位寄存器
- 每个SCL时钟,采样1位,移入RSR
- 收完8位数据,硬件自动把RSR数据移入RDR
- 置位
RXNE标志,通知CPU来读- CPU读取
I2C_DR,硬件自动清零RXNE- 控制逻辑根据配置,自动输出ACK或NACK应答
五、模块4:控制与状态逻辑
I2C外设的"大脑",负责所有协议控制、状态检测、错误处理。
5.1 核心控制功能
- 起始/停止控制:根据CR1寄存器位,自动产生起始停止时序
- 应答控制:
- 接收时,根据
ACK位配置,自动输出ACK或NACK- 发送时,自动检测对方的ACK电平,置位AF(应答失败)标志
- 多主机仲裁:
- 发送时边发边检测SDA总线电平
- 自己发1却检测到0,判定仲裁失败,自动停止发送,置位
ARLO标志- 时钟拉伸控制:
- 从机模式下,接收数据后如果CPU没及时读走,硬件自动拉低SCL
- 延长时钟低电平时间,让主机等待,实现硬件流控
- 错误检测:
- 总线错误、仲裁失败、应答失败、溢出错误,自动置位对应错误标志
- 可配置中断,通知CPU处理
5.2 状态寄存器组
- SR1:事件和错误标志(SB起始完成、ADDR地址匹配、TXE、RXNE、BTF、AF应答失败等)
- SR2:总线状态(BUSY总线忙、MSL主从模式、TRA收发方向)
六、模块5:引脚级电路
SCL和SDA两个引脚的内部电路完全一致,都是开漏输出+施密特输入的结构。
引脚内部结构
┌────────── 开漏输出 NMOS管 ← 输出控制 SDA引脚 ──┤ └────────── 施密特触发器 → 输入滤波输出部分
- 只有一个NMOS管,栅极由内部控制逻辑驱动
- NMOS导通 → 引脚拉到GND → 输出低电平
- NMOS截止 → 引脚高阻 → 外部上拉电阻拉成高电平
- 完全符合I2C协议的开漏要求,支持线与逻辑
输入部分
- 施密特触发器整形,消除边沿抖动
- 数字滤波滤除窄脉冲干扰
- 送到内部移位寄存器和检测逻辑
复用切换
- GPIO配置为复用开漏模式时,引脚控制权从GPIO外设切换到I2C外设
- 普通GPIO模式下,I2C外设和引脚断开,不受影响
七、完整数据流:一次主机写操作的内部全链路
以主机向从机发一个字节为例,从CPU指令到总线输出的完整内部过程:
- 配置阶段:CPU配置CR1、CR2、CCR寄存器,设置波特率、使能外设、开启应答
- 发起起始:CPU置位CR1的START位,硬件检测总线空闲后,自动产生起始信号
- 起始完成:起始发送完成,硬件置位SB标志,清零START位
- 写地址:CPU把地址字节写入DR寄存器,TXE清0;硬件移入移位寄存器,逐位输出
- 地址应答:8位地址发完,硬件释放SDA,检测从机ACK;收到ACK则继续
- 写数据:CPU写入数据字节,同样经过DR→移位寄存器→SDA总线
- 数据应答:检测从机ACK,确认发送成功
- 发送停止:CPU置位STOP位,硬件自动产生停止信号,释放总线
八、STM32 I2C 核心状态位汇总表
位序号 标志位 所属寄存器 中文名称 置 1 触发条件 清除方式 bit0 SB SR1 起始信号完成 主机模式下,成功发送起始信号 读 SR1 后,写入 DR 寄存器 bit1 ADDR SR1 地址匹配 从机检测到匹配的自身地址; 主机发送地址后收到应答 读 SR1 后,读取 SR2 寄存器 bit2 BTF SR1 字节传输完成 移位寄存器数据全部收发完成,且 DR 寄存器处于空闲状态 读 SR1 后,读写 DR 寄存器 bit6 RXNE SR1 接收数据寄存器非空 接收移位寄存器数据移入 RDR,有新数据可供 CPU 读取 读取 DR 寄存器,硬件自动清零 bit7 TXE SR1 发送数据寄存器空 TDR 数据已移入移位寄存器,可写入下一个字节 写入 DR 寄存器,硬件自动清零 bit8 BERR SR1 总线错误 检测到非法的起始 / 停止信号,总线时序异常 软件向该位写 0 清零 bit9 ARLO SR1 仲裁失败 多主机场景下,本设备总线仲裁丢失 软件向该位写 0 清零 bit10 AF SR1 应答失败 发送地址 / 数据后,未检测到从机 ACK 应答 软件向该位写 0 清零 bit11 OVR SR1 溢出 / 超限错误 接收时旧数据未读又收到新数据(溢出);发送时新数据未及时写入(超限) 软件向该位写 0 清零 bit1 BUSY SR2 总线忙 检测到总线上的起始信号,总线进入通信状态 检测到停止信号后,硬件自动清零 bit0 MSL SR2 主从模式 1 = 主机模式,0 = 从机模式 硬件根据通信角色自动更新 bit2 TRA SR2 收发方向 1 = 发送方向,0 = 接收方向 硬件根据地址字节读写位自动更新
1. 状态流转图(状态机图)
示例结构(主机写流程):
总线空闲 (BUSY=0, TXE=1) │ ▼ 写CR1.START位 起始完成 (SB=1, BUSY=1) │ ▼ 写DR=地址字节 地址发送完成 (ADDR=1, TXE=1) │ ▼ 读SR1+读SR2 清除ADDR 数据发送阶段 (TXE=1 → 写DR → TXE=0 → 发完 → TXE=1) │ ▼ 最后一字节,等待BTF=1 发送完成 (BTF=1) │ ▼ 写CR1.STOP位 总线回到空闲 (BUSY=0)
2. 功能分组结构图
示例结构:
I2C 状态位 ├─ 总线与模式状态(SR2寄存器) │ ├─ BUSY:总线忙/空闲 │ ├─ MSL: 主/从模式 │ └─ TRA: 发送/接收方向 ├─ 发送通路状态(SR1寄存器) │ ├─ SB: 起始信号完成 │ ├─ TXE: 发送寄存器空 │ └─ BTF: 字节传输完成 ├─ 接收通路状态(SR1寄存器) │ ├─ ADDR:地址匹配完成 │ └─ RXNE:接收寄存器非空 └─ 错误类状态(SR1寄存器) ├─ BERR:总线时序错误 ├─ ARLO:仲裁失败 ├─ AF: 应答失败 └─ OVR: 接收溢出/发送超限
3. 时序图标注法
示例(单字节发送时序标注):
SCL: ──┐ ┌──┐ ┌──┐ ┌──┐ ┌────── └──┘ └──┘ ... └──────┘ SDA: ──────── 地址位 ... ────┐ ──── └────── 标注: ↑起始沿 ↑8位发完 ↑第9时钟ACK ↑停止沿 SB置1 TXE置1 ADDR置1 写DR自动清SB 读SR2清ADDR
4. 寄存器位域映射图
示例(SR1 寄存器位域):
SR1 寄存器(32位,低16位有效) ┌────┬────┬────┬────┬────┬────┬────┬────┐ │bit0│bit1│bit2│bit3│bit4│bit5│bit6│bit7│ │ SB │ADDR│BTF │保留│保留│保留│RXNE│TXE│ └────┴────┴────┴────┴────┴────┴────┴────┘ ┌─────┬─────┬────┬────┬────┬────┬────┬────┐ │bit8 │bit9 │bit10│bit11│bit12│bit13│bit14│bit15│ │BERR │ARLO│ AF │OVR │保留 │保留 │保留 │保留 │ └─────┴─────┴────┴────┴────┴────┴────┴────┘
5. 场景化状态清单
步骤 操作 状态位变化 1 置位 CR1.START,发起起始 等待 SB=1 2 向 DR 写入从机地址 SB 硬件自动清零;地址发完后 TXE=1、ADDR=1 3 先读 SR1,再读 SR2 ADDR 硬件自动清零 4 向 DR 写入数据字节 TXE 硬件自动清零 5 等待数据发送完成 移位完成后 BTF=1 6 置位 CR1.STOP,发停止 BTF 清零;总线空闲后 BUSY=0
🚩 高频面试题
I2C外设内部主要有哪些模块?
总线接口与寄存器组、时钟分频单元、发送数据通路(DR+移位寄存器)、接收数据通路(滤波+移位寄存器+地址匹配)、控制逻辑、引脚开漏输入输出电路。
CPU写入I2C_DR后,数据经过哪些步骤发到SDA总线上?
数据先写入发送数据寄存器TDR,硬件自动移入发送移位寄存器TSR;
在SCL时钟驱动下,移位寄存器逐位把数据移出,经过开漏输出驱动到SDA引脚。
I2C的地址匹配是怎么实现的?
从机模式下,收到地址字节后,内部地址比较器把接收的高7位地址和自身OAR寄存器的地址对比;
匹配则置位ADDR标志,硬件自动拉低SDA回复ACK;不匹配则忽略本次通信。
I2C的SCL时钟是怎么产生的?
由APB1总线时钟PCLK1,经过CCR寄存器配置的分频器分频得到;
标准模式下高低电平均分。
什么是时钟拉伸?硬件怎么实现?
从机处理不及时,硬件主动拉低SCL,延长时钟低电平时间,让主机等待。
实现:从机接收数据后如果CPU没及时读走,接收控制逻辑导通SCL的开漏管,拉低总线SCL,直到数据被读取后释放。
I2C发送和接收是共用数据寄存器吗?
物理地址是同一个
I2C_DR;写操作对应发送数据寄存器TDR,读操作对应接收数据寄存器RDR,是两个独立的物理寄存器,共用一个地址。
I2C为什么能实现多主机仲裁?
开漏输出+线与特性,主机边发送边检测SDA电平;
自己发1却检测到0,说明有其他主机拉低总线,判定自己仲裁失败,自动停止发送,过程不破坏数据。
【STM32】 I2C 外设内部硬件结构和状态位
Brilliantwxx2026-09-14 8:25
相关推荐
老当益壮梁奶奶1 小时前
51单片机中断系统详解芒鸽1 小时前
把 Agent 运行时做成插件系统:agent-harness(openJiuwen Rust版) 的实践zhangzhangkeji1 小时前
stm32 笔记 - 3-1:GPIO 输出小七在进步1 小时前
类和对象(二)CHHH_HHH1 小时前
【Linux系统篇】进程间通信揭秘:从管道到共享内存写后端的胖头鱼1 小时前
【高频面试题】面试题里常出现的Bitmap是什么(附源码和多种场景)学习智者1 小时前
《玄》IDE v3.8.2重磅发布:数据外置+全链路优化天空'之城9 小时前
单片机基础核心知识点汇总(三十五)secondyoung10 小时前
I2C通信协议详解


