2. STM32 I2C外设详解:模块架构、时钟控制、数据寄存器、状态标志

各位同学,今天我们来啃一块硬骨头——STM32的I2C外设。说实话,I2C这个外设看起来简单,但坑不少。我早期做项目时,就因为没搞懂它的状态标志,折腾了整整两天。所以这一章,咱们把它的里里外外都扒清楚。

2.1 I2C模块整体架构

先看一张整体结构图,心里有个谱。

STM32 I2C外设模块架构 时钟控制 CCR / TRISE 数据寄存器 DR / TXE / RXNE 控制逻辑 CR1 / CR2 / OAR1 状态标志 SR1 / SR2 中断 / DMA IT / DMA 请求 引脚控制 SCL / SDA I2C 总线(SCL + SDA) 上拉电阻 → 开漏输出 → 线与逻辑 核心要点:I2C外设 = 时钟控制 + 数据通路 + 状态机 + 中断/DMA 所有操作都围绕状态标志展开,读标志 → 判断 → 动作

从图上能看出来,I2C外设主要分几大块:时钟控制、数据寄存器、控制逻辑、状态标志,还有中断/DMA和引脚控制。说白了,你写驱动就是在跟这些寄存器打交道。

2.2 时钟控制——决定通信速度

时钟控制这块,我见过太多人栽跟头。STM32的I2C时钟由两个寄存器控制:CCR(时钟控制寄存器)和TRISE(上升时间寄存器)。

关键公式:

标准模式(100kHz):SCL频率 = PCLK1 / (2 × (CCR + 1))

快速模式(400kHz):SCL频率 = PCLK1 / (2 × CCR) (Duty = 0时)

举个例子,假设PCLK1 = 36MHz,想要100kHz:

CCR = (PCLK1 / (2 × 100000)) - 1
    = (36000000 / 200000) - 1
    = 180 - 1
    = 179

我曾经踩过的坑:TRISE寄存器没配对。标准模式下TRISE必须设为PCLK1周期数 + 1,快速模式设为PCLK1周期数 × 300ns + 1。忘了配这个,总线时序就乱了,传感器死活不响应。

2.3 数据寄存器——收发数据的核心

数据寄存器DR是I2C通信的"收发室"。你往DR写数据,就是发送;读DR,就是接收。但这里有个细节——DR是双缓冲的。

什么意思?发送时,你写DR,数据先进发送缓冲器,然后移位寄存器一位一位往外送。接收时,移位寄存器收完一个字节,自动转到接收缓冲器,你读DR就能拿到。这样CPU不用等每个位,效率高不少。

寄存器 位宽 功能 注意事项
DR 8位 发送/接收数据 写=发送,读=接收
TXE标志 1位 发送缓冲器空 可写下一个字节
RXNE标志 1位 接收缓冲器非空 可读数据

我的习惯:发送数据前先检查TXE,接收数据后先检查RXNE。别图省事直接读写DR,否则数据容易错位。

2.4 状态标志——通信的"红绿灯"

状态标志是I2C调试的重中之重。我当年调一个温湿度传感器,死活读不到数据,最后发现是SB标志没处理好。来,咱们把几个关键标志过一遍。

2.4.1 SR1寄存器(状态寄存器1)

标志位 含义 触发条件 清除方式
SB 起始位已发送 发送START条件后 读SR1后写DR
ADDR 地址已发送/匹配 发送地址后或地址匹配 读SR1后读SR2
BTF 字节发送完成 移位寄存器空且DR空 读/写DR或STOP
TXE 发送缓冲器空 DR数据移入移位寄存器 写DR
RXNE 接收缓冲器非空 移位寄存器数据移入DR 读DR
AF 应答失败 未收到ACK 软件写0
STOPF 停止条件完成 STOP条件发送后 读SR1后写CR1

2.4.2 SR2寄存器(状态寄存器2)

SR2主要包含总线状态和从机地址信息:

  • BUSY:总线忙标志。发送START前必须检查BUSY=0,否则会冲突。
  • MSL:主从模式标志。1=主机模式,0=从机模式。
  • GEN CALL:通用呼叫标志。收到0x00地址时置位。

我曾经犯过的错:ADDR标志的清除顺序搞反了。必须先读SR1,再读SR2,顺序不能乱。我一开始先读SR2再读SR1,结果ADDR一直不消,卡死在地址发送阶段。

2.5 中断与DMA——解放CPU

轮询方式简单,但浪费CPU。实际项目中,我更喜欢用中断或DMA。

方式 优点 缺点 适用场景
轮询 代码简单,调试方便 占用CPU 低速、少量数据
中断 响应及时,CPU利用率高 中断服务程序不能太长 中速、中等数据量
DMA 几乎不占CPU 配置复杂,调试困难 高速、大量数据(如OLED刷屏)

中断使能位在CR2寄存器里:

  • ITEVTEN:事件中断使能(SB、ADDR、BTF等)
  • ITBUFEN:缓冲中断使能(TXE、RXNE)
  • ITERREN:错误中断使能(AF、ARLO等)

我的建议:刚开始学I2C驱动,先用轮询把流程跑通。等逻辑没问题了,再换成中断或DMA。一步到位用DMA,出了问题你都不知道是硬件还是软件的事。

2.6 调试要点总结

最后,把我这些年调I2C的经验浓缩成几条:

  1. 先查时钟:用示波器看SCL波形,频率对不对?占空比正常吗?
  2. 再看起始条件:SDA在SCL高电平时从高变低,这个时序对不对?
  3. 地址发送后查ADDR:ADDR没置位,说明从机没响应。检查地址对不对,上拉电阻有没有。
  4. 数据收发看TXE/RXNE:这两个标志是数据流动的"心跳",丢了任何一个都会卡死。
  5. 最后查STOPF:通信结束要确认STOP条件完成,否则下次START发不出去。

嗯,这一章的内容就到这儿。I2C外设的架构、时钟、数据寄存器和状态标志,是写驱动的基础。把这些搞透了,后面写传感器驱动就是水到渠成的事。