2. STM32 I2C外设详解:模块架构、时钟控制、数据寄存器、状态标志
各位同学,今天我们来啃一块硬骨头——STM32的I2C外设。说实话,I2C这个外设看起来简单,但坑不少。我早期做项目时,就因为没搞懂它的状态标志,折腾了整整两天。所以这一章,咱们把它的里里外外都扒清楚。
2.1 I2C模块整体架构
先看一张整体结构图,心里有个谱。
从图上能看出来,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的经验浓缩成几条:
- 先查时钟:用示波器看SCL波形,频率对不对?占空比正常吗?
- 再看起始条件:SDA在SCL高电平时从高变低,这个时序对不对?
- 地址发送后查ADDR:ADDR没置位,说明从机没响应。检查地址对不对,上拉电阻有没有。
- 数据收发看TXE/RXNE:这两个标志是数据流动的"心跳",丢了任何一个都会卡死。
- 最后查STOPF:通信结束要确认STOP条件完成,否则下次START发不出去。
嗯,这一章的内容就到这儿。I2C外设的架构、时钟、数据寄存器和状态标志,是写驱动的基础。把这些搞透了,后面写传感器驱动就是水到渠成的事。