4. STM32的I2C外设详解:模块框图、工作模式与寄存器

说实话,很多初学者用STM32的I2C外设时,都是直接抄库函数。一旦出问题,就完全抓瞎了。我个人觉得,要想真正搞定I2C通信,必须把外设的骨架摸清楚——模块框图、工作模式、关键寄存器。这就像修车,你得知道发动机长什么样,才能判断哪里漏油。

这一章,我们就来拆解STM32F103的I2C外设。我会结合自己调试时的经验,把那些容易踩坑的地方指出来。

4.1 I2C模块框图:看看里面到底有什么

先看整体结构。下图是I2C模块的内部框架,我把它简化成了几个核心部分。

STM32F103 I2C模块内部框图 时钟控制 CCR / TRISE 数据寄存器 (DR) 发送/接收缓冲区 控制逻辑 CR1 / CR2 状态标志 SR1 / SR2 地址比较器 OAR1 / OAR2 SCL/SDA控制 GPIO复用 核心数据流 1. 主机通过CR1/CR2配置模式,产生起始条件 2. 地址比较器匹配从机地址,确认通信对象 3. DR寄存器负责数据收发,状态标志反馈进度 4. SCL/SDA控制引脚输出时序,完成物理层传输

从框图可以看出,I2C外设主要由这几大块组成:时钟控制决定通信速率,数据寄存器是收发数据的缓冲区,控制逻辑负责配置模式,状态标志告诉我们当前通信进行到哪一步了,地址比较器用于从机模式下的地址匹配,SCL/SDA控制则管理引脚输出。

我的经验:调试I2C时,我习惯先看状态标志寄存器。它能直接告诉我总线是否忙、数据是否发送完成、是否有应答信号。比瞎猜快多了。

4.2 I2C工作模式:主机与从机

STM32的I2C外设支持两种工作模式:主机模式和从机模式。说白了,主机是发号施令的,从机是听指挥的。

4.2.1 主机模式

主机模式下,外设负责产生时钟信号(SCL)、发起通信、发送地址和数据。我们驱动OLED屏幕时,STM32就是作为主机。

主机模式的关键流程:

  • 产生起始条件:SCL高电平时,SDA从高变低
  • 发送从机地址:7位地址 + 读写位
  • 等待应答:检查从机是否回应ACK
  • 发送/接收数据:每字节后等待应答
  • 产生停止条件:SCL高电平时,SDA从低变高
注意:我曾经在主机模式下忘记配置时钟频率,结果SCL跑到了400kHz以上,从机直接不响应。后来查了手册才发现,标准模式最高100kHz,快速模式才到400kHz。别超频!

4.2.2 从机模式

从机模式下,外设不产生时钟,而是监听总线上的地址。当地址匹配时,自动参与通信。这个模式在项目中用得少一些,但做多机通信时很关键。

从机模式的关键点:

  • 地址配置:通过OAR1和OAR2寄存器设置本机地址
  • 地址匹配:硬件自动比较,匹配后产生中断
  • 数据收发:根据读写位决定方向

4.3 I2C寄存器功能解析

寄存器是操作外设的直接入口。下面我挑几个最常用的,结合实战经验讲清楚。

4.3.1 控制寄存器CR1

CR1是I2C外设的总开关。关键位如下:

位 名称 功能
0 PE 外设使能。必须置1才能工作
1 SMBus SMBus模式选择,一般I2C用不到
8 START 产生起始条件。发送完自动清零
9 STOP 产生停止条件。发送完自动清零
10 ACK 应答使能。接收模式下必须置1
15 SWRST 软件复位。总线卡死时救命用的
避坑指南:我曾经在调试时发现总线一直忙,查了半天才发现是PE位没置1。记住:配置完所有参数后,最后再使能PE。顺序反了可能导致外设状态异常。

4.3.2 控制寄存器CR2

CR2主要配置时钟频率和中断。关键位:

  • FREQ[5:0]:设置APB时钟频率,范围2-36MHz。这个必须配准,否则SCL时序全乱
  • ITBUFEN:缓冲区中断使能
  • ITEVTEN:事件中断使能
  • ITERREN:错误中断使能

我个人习惯在调试阶段把所有中断都打开,方便定位问题。量产时再关掉不必要的,减少中断开销。

4.3.3 状态寄存器SR1

SR1是调试时最常用的寄存器。它实时反映外设的工作状态。

位 名称 含义
0 SB 起始条件已发送(主机模式)
1 ADDR 地址已发送(主机)或地址匹配(从机)
2 BTF 字节发送完成,DR已空
6 RxNE 数据寄存器非空,有数据可读
7 TxE 数据寄存器空,可以写入新数据
8 BUSY 总线忙。1表示总线上有通信
9 MSL 主从标志。1为主机,0为从机
10 AF 应答失败。从机没回应
14 TIMEOUT 超时错误。SCL被拉低太久
调试技巧:我每次写I2C驱动,都会在关键步骤后读SR1寄存器,打印出来看。比如发送地址后检查ADDR位是否置1,发送数据后检查BTF位。这样能快速定位是主机没发出去,还是从机没应答。

4.3.4 状态寄存器SR2

SR2主要包含从机模式下的状态信息,比如:

  • DUALF:双地址标志,表示是否匹配了第二个地址
  • GENCALL:广播呼叫标志,收到0x00地址时置位
  • TRA:数据传输方向,1表示发送,0表示接收
  • BUSY:总线忙标志(和SR1中的BUSY是同一个)

主机模式下,SR2用得不多。但如果你做从机,这个寄存器就很重要了。

4.3.5 数据寄存器DR

DR是收发数据的通道。写DR就是发送数据,读DR就是接收数据。注意:DR只有8位,一次只能处理一个字节。

操作DR时要注意:

  • 发送时:先检查TxE位,确保DR为空,再写入
  • 接收时:先检查RxNE位,确保DR有数据,再读取
  • 连续收发:每字节后都要等待状态标志,不能一股脑全塞进去
血的教训:我曾经在发送多字节时,没等TxE位就连续写DR,结果数据全乱套了。后来老老实实加了个while循环等待标志位,问题就解决了。别嫌麻烦,硬件时序不能省。

4.4 寄存器配置实战流程

下面是一个典型的主机发送配置流程,我把它整理成了步骤列表:

  1. 使能GPIO时钟和I2C时钟:RCC_APB1PeriphClockCmd()
  2. 配置GPIO为开漏输出:SCL和SDA都要配成AF_OD模式
  3. 复位I2C外设:设置CR1的SWRST位,再清零
  4. 配置CR2的FREQ位:设置APB时钟频率
  5. 配置CCR寄存器:设置SCL时钟频率(标准/快速模式)
  6. 配置TRISE寄存器:设置SCL上升时间
  7. 使能I2C外设:设置CR1的PE位
  8. 产生起始条件:设置CR1的START位
  9. 等待SB位置1:确认起始条件已发送
  10. 写入从机地址:写DR寄存器
  11. 等待ADDR位置1:确认地址已发送并收到应答
  12. 发送数据:写DR,等待TxE,重复直到数据发完
  13. 产生停止条件:设置CR1的STOP位

这个流程我用了很多年,基本没出过问题。你写驱动时,可以照着这个顺序来。

4.5 常见寄存器配置错误

最后,我总结几个自己踩过的坑:

  • FREQ位没配准:SCL频率会乱,从机不响应。一定要根据APB时钟算好
  • PE位使能过早:还没配完参数就开外设,状态可能异常。建议最后再使能
  • 没等标志位就操作DR:数据丢失或错位。每个字节都要等
  • ACK位没置1:接收模式下,从机收不到应答信号,通信会中断
  • SWRST用完后没清零:外设一直处于复位状态,无法工作