2. STM32 I2C外设概述:模块框图、时钟树、主要特性、工作模式

好,我们正式开始啃I2C外设。很多初学者上来就翻寄存器手册,结果越看越晕。我的习惯是——先看框图,再看时钟,最后看特性。脑子里有了整体画面,寄存器才有意义。

2.1 I2C模块框图:先看骨架

STM32的I2C外设,说白了就是一套硬件状态机。它帮你处理SCL时钟生成、数据移位、起始/停止条件检测、应答位检查这些繁琐的活儿。你只需要往数据寄存器里丢字节,或者从里面取字节就行。

下面这张图是我自己画的I2C模块核心框图,你看一眼就能明白数据是怎么流动的:

STM32 I2C 外设核心框图 CPU / 总线 控制逻辑 (CR1/CR2) 数据寄存器 (DR) 移位寄存器 SCL / SDA 引脚 噪声滤波器 时钟控制 (CCR) 状态寄存器 (SR1/SR2) 数据流向:CPU → 数据寄存器 → 移位寄存器 → SDA引脚(发送) 状态反馈:SCL/SDA → 状态寄存器 → 控制逻辑 → 中断/事件

你看,整个数据通路其实很清晰。CPU把数据写到DR寄存器,硬件自动移位到SDA线上。反过来,接收到的数据从SDA进来,经过移位寄存器,最终出现在DR里等你读取。

关键点:I2C硬件帮你做了90%的位级工作。你只需要关注DR、SR1、SR2、CR1、CR2这五个核心寄存器组。别被手册里几十个寄存器吓到,大部分你根本用不上。

2.2 时钟树:I2C的脉搏从哪来

I2C的SCL时钟不是凭空产生的。它来自APB总线时钟,经过一个分频器,最终生成你想要的SCL频率。比如标准模式100kHz,快速模式400kHz。

时钟计算公式其实很简单:

SCL频率 = APB时钟频率 / (CCR值 × 2)
// 以STM32F103为例,APB1=36MHz,想得到400kHz:
// CCR = 36MHz / (400kHz × 2) = 45
// 所以CCR寄存器写45,就能得到400kHz

我的经验:实际项目中,我习惯把CCR值算出来后再加1~2。为什么?因为总线负载、引脚电容都会让实际频率偏低。加一点余量,测出来刚好是目标频率。我曾经在一个传感器项目里没加余量,结果SCL只有380kHz,传感器偶尔超时——折腾了两天才找到原因。

不同STM32系列的时钟源略有差异,但原理一样:

系列 APB时钟 最大SCL 备注
STM32F1 APB1 (最高36MHz) 400kHz 标准I2C外设
STM32F4 APB1 (最高42MHz) 400kHz 支持FM+模式
STM32L4 APB1 (最高80MHz) 1MHz 支持FM+,低功耗优化
STM32H7 APB1/APB4 1MHz 双I2C外设,更灵活

2.3 主要特性:硬件到底帮你做了啥

我列一下I2C外设的核心特性,你心里有个数:

  • 多主机功能:总线上可以有多个主机,硬件自动处理仲裁。说白了,两个主机同时发数据,硬件会判断谁赢了。
  • 7位/10位地址:大部分传感器用7位地址。10位地址我几乎没用过,但硬件支持。
  • 双缓冲数据寄存器:发送时你可以提前写下一字节,接收时你有时间取走当前字节。不会丢数据。
  • 可编程的SCL时钟:通过CCR寄存器,你可以设100kHz、400kHz甚至1MHz。
  • 事件中断和错误中断:发送完成、接收完成、仲裁丢失、应答失败……硬件都会给你发中断信号。
  • DMA支持:大批量数据传输时,DMA可以帮你自动搬运数据,CPU只管睡觉。

注意:不是所有STM32系列都支持1MHz FM+模式。F1系列最高只有400kHz。如果你需要高速I2C,选型时一定要看数据手册的I2C特性表。我见过有人用F1去驱动1MHz的OLED屏,结果死活点不亮——查了半天才发现是频率上限问题。

2.4 工作模式:主机、从机、多主机

I2C外设支持三种工作模式。大部分时候我们用主机模式,但理解从机模式也很重要:

2.4.1 主机模式

你主动发起通信。流程是:

  1. 配置CR1的START位,硬件自动发送起始条件
  2. 发送从机地址 + 读写位
  3. 等待从机应答(检查SR1的ADDR位)
  4. 发送或接收数据
  5. 发送停止条件(配置STOP位)

说白了,主机就是那个「说了算」的角色。你想读哪个寄存器,想写什么数据,都由你决定。

2.4.2 从机模式

你被动响应。别人发地址给你,你匹配了就应答。这种模式在I2C多机通信中很常见,比如一个STM32作为主控,另一个STM32作为从设备。

从机模式下,你需要配置自己的地址(OAR1/OAR2寄存器),然后等待主机来访问。硬件会自动比较地址,匹配了就给你发中断。

2.4.3 多主机模式

多个主机共享一条I2C总线。硬件通过仲裁机制解决冲突。说白了,谁先拉低SDA谁就赢。输的那个自动退出,不会破坏总线数据。

我的建议:初学者先别碰多主机模式。老老实实一主一从,把中断和DMA玩熟了再说。多主机模式在工业现场总线里用得多,普通产品开发几乎用不到。

2.5 中断事件:硬件什么时候叫你

I2C外设的中断事件分为两类:

事件类型 触发条件 对应标志位
发送事件 数据寄存器空,可以写下一字节 TXE
接收事件 数据寄存器非空,可以读取 RXNE
地址匹配 从机模式下,主机地址匹配成功 ADDR
停止条件 检测到总线停止条件 STOPF
错误事件 应答失败、仲裁丢失、总线错误 AFE/ARLO/BERR

嗯,这里要注意:中断标志位在进入中断后必须手动清除。尤其是ADDR位,很多人忘了清,导致中断一直触发,CPU卡死在中断里。我早期做项目时就踩过这个坑——程序跑着跑着就死机了,后来发现是ADDR没清,中断嵌套把自己搞死了。

2.6 小结:你该记住什么

这一章内容不少,但核心就三点:

  • 框图:数据从DR到移位寄存器再到SDA,状态从引脚回到SR1/SR2
  • 时钟:SCL频率 = APB时钟 / (CCR × 2),记得加余量
  • 模式:主机模式最常用,从机模式做从设备,多主机模式先别碰