【STM32】 I2C 外设内部硬件结构和状态位


STM32 I2C 外设内部硬件结构和状态位 深度详解

一、整体结构总览

STM32 的 I2C 是完整的硬件协议引擎,挂载在 APB1 总线上,所有协议时序(起始/停止、应答、移位、地址检测、仲裁)全部由硬件自动完成,CPU 仅通过寄存器读写数据和配置。

内部整体框图

核心层级

  1. 总线接口层:和APB1总线对接,CPU通过寄存器配置外设、读写数据
  2. 核心协议层:时钟分频、发送移位、接收移位、地址匹配,是协议的硬件实现
  3. 控制逻辑层:起始停止产生、应答控制、总线仲裁、时钟拉伸
  4. 物理引脚层:开漏输出驱动、输入施密特滤波,直接连外部GPIO引脚

二、模块1:总线接口与时钟单元

2.1 总线接口

  • 挂载在 APB1 低速外设总线,输入时钟为 PCLK1(标准配置36MHz)
  • 所有寄存器都映射在APB1外设地址空间,CPU通过AHB-APB桥读写寄存器
  • 呼应RCC知识点:操作前必须开启RCC_APB1ENR的I2C时钟位,否则外设冻结,寄存器不可访问

2.2 时钟配置与分频单元

这是I2C波特率的硬件来源,对应I2C_CR2I2C_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_CR1START位、STOP位触发
  • 硬件自动按照协议时序,在SCL高电平时控制SDA跳变,产生起始/停止信号
  • 起始完成后硬件自动清零START位,置位**SB(起始完成)**标志
(4)输出驱动
  • SDA、SCL都是开漏NMOS输出结构,只能主动拉低,高电平靠外部上拉
  • 呼应GPIO知识点:和GPIO开漏输出是同一套硬件电路,复用模式下控制权交给I2C外设

主机发送一个字节的完整内部流程

  1. CPU把数据写入I2C_DR,****TXE标志硬件清0
  2. 硬件自动把数据从TDR移入TSR移位寄存器
  3. TDR空出,TXE硬件置1,可以写下一个
  4. 移位寄存器在SCL时钟驱动下,逐位把数据移出到SDA引脚
  5. 8位全部移出,BTF标志置1,字节发送完成
  6. 硬件自动等待第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
    • 不匹配:忽略本次通信,继续等待下一次起始

主机接收一个字节的完整内部流程

  1. SDA总线数据经过滤波整形,送入接收移位寄存器
  2. 每个SCL时钟,采样1位,移入RSR
  3. 收完8位数据,硬件自动把RSR数据移入RDR
  4. 置位RXNE标志,通知CPU来读
  5. CPU读取I2C_DR,硬件自动清零RXNE
  6. 控制逻辑根据配置,自动输出ACK或NACK应答

五、模块4:控制与状态逻辑

I2C外设的"大脑",负责所有协议控制、状态检测、错误处理。

5.1 核心控制功能

  1. 起始/停止控制:根据CR1寄存器位,自动产生起始停止时序
  2. 应答控制:
    • 接收时,根据ACK位配置,自动输出ACK或NACK
    • 发送时,自动检测对方的ACK电平,置位AF(应答失败)标志
  3. 多主机仲裁:
    • 发送时边发边检测SDA总线电平
    • 自己发1却检测到0,判定仲裁失败,自动停止发送,置位ARLO标志
  4. 时钟拉伸控制:
    • 从机模式下,接收数据后如果CPU没及时读走,硬件自动拉低SCL
    • 延长时钟低电平时间,让主机等待,实现硬件流控
  5. 错误检测:
    • 总线错误、仲裁失败、应答失败、溢出错误,自动置位对应错误标志
    • 可配置中断,通知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指令到总线输出的完整内部过程:

  1. 配置阶段:CPU配置CR1、CR2、CCR寄存器,设置波特率、使能外设、开启应答
  2. 发起起始:CPU置位CR1的START位,硬件检测总线空闲后,自动产生起始信号
  3. 起始完成:起始发送完成,硬件置位SB标志,清零START位
  4. 写地址:CPU把地址字节写入DR寄存器,TXE清0;硬件移入移位寄存器,逐位输出
  5. 地址应答:8位地址发完,硬件释放SDA,检测从机ACK;收到ACK则继续
  6. 写数据:CPU写入数据字节,同样经过DR→移位寄存器→SDA总线
  7. 数据应答:检测从机ACK,确认发送成功
  8. 发送停止: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

🚩 高频面试题

  1. I2C外设内部主要有哪些模块?

    总线接口与寄存器组、时钟分频单元、发送数据通路(DR+移位寄存器)、接收数据通路(滤波+移位寄存器+地址匹配)、控制逻辑、引脚开漏输入输出电路。

  2. CPU写入I2C_DR后,数据经过哪些步骤发到SDA总线上?

    数据先写入发送数据寄存器TDR,硬件自动移入发送移位寄存器TSR;

    在SCL时钟驱动下,移位寄存器逐位把数据移出,经过开漏输出驱动到SDA引脚。

  3. I2C的地址匹配是怎么实现的?

    从机模式下,收到地址字节后,内部地址比较器把接收的高7位地址和自身OAR寄存器的地址对比;

    匹配则置位ADDR标志,硬件自动拉低SDA回复ACK;不匹配则忽略本次通信。

  4. I2C的SCL时钟是怎么产生的?

    由APB1总线时钟PCLK1,经过CCR寄存器配置的分频器分频得到;

    标准模式下高低电平均分。

  5. 什么是时钟拉伸?硬件怎么实现?

    从机处理不及时,硬件主动拉低SCL,延长时钟低电平时间,让主机等待。

    实现:从机接收数据后如果CPU没及时读走,接收控制逻辑导通SCL的开漏管,拉低总线SCL,直到数据被读取后释放。

  6. I2C发送和接收是共用数据寄存器吗?

    物理地址是同一个I2C_DR

    写操作对应发送数据寄存器TDR,读操作对应接收数据寄存器RDR,是两个独立的物理寄存器,共用一个地址。

  7. I2C为什么能实现多主机仲裁?

    开漏输出+线与特性,主机边发送边检测SDA电平;

    自己发1却检测到0,说明有其他主机拉低总线,判定自己仲裁失败,自动停止发送,过程不破坏数据。


相关推荐
老当益壮梁奶奶1 小时前
51单片机中断系统详解
c语言·单片机·嵌入式硬件·学习·51单片机
芒鸽1 小时前
把 Agent 运行时做成插件系统:agent-harness(openJiuwen Rust版) 的实践
开发语言·后端·rust
zhangzhangkeji1 小时前
stm32 笔记 - 3-1:GPIO 输出
stm32·arm
小七在进步1 小时前
类和对象(二)
java·开发语言
CHHH_HHH1 小时前
【Linux系统篇】进程间通信揭秘:从管道到共享内存
linux·服务器·c语言·开发语言·后端·ubuntu
写后端的胖头鱼1 小时前
【高频面试题】面试题里常出现的Bitmap是什么(附源码和多种场景)
java·开发语言·面试·位图·bitmap
学习智者1 小时前
《玄》IDE v3.8.2重磅发布:数据外置+全链路优化
开发语言·c++·ide·中文语言 玄
天空'之城9 小时前
单片机基础核心知识点汇总(三十五)
单片机·嵌入式硬件
secondyoung10 小时前
I2C通信协议详解
arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu·51单片机