关于stm32f1系列手册中IIC部分的笔记,对应STM32F10xxx参考手册中第24章节。这是我个人在观看手册时的一些问题的汇总
在stm32f103的手册中,iic默认情况下,IIC接口总是工作在从模式。从从模式切换到主模式,需要产生一个起始条件
在STM32F103的I2C系统中,从模式切换到主模式并不是一个简单的软件配置切换,而是一个由"产生起始条件"这一硬件事件触发的状态机跃迁。这是STM32F1系列I2C模块一个非常核心且独特的设计。下面为你详细拆解这个过程。
STM32F103的I2C外设内部有一个硬连线状态机。它的默认状态(复位后或总线空闲时)确实是从模式(Slave) 。它始终在监听总线,等待自己被寻址。只有当它主动在总线上发起一个起始信号(START condition),其内部状态才会从"从机接收器/发送器"转变为"主机发送器/接收器"。

从机地址的从发送流程图
当主机要读取一个7位地址的从机时,过程最为直接。

7位地址核心特点 :寻址一步完成。主机发送的第一个字节就包含了完整的7位地址和读方向位,从机识别后即进入数据发送状态。
10位地址的读取过程较为复杂,它通过一个"重复起始条件 "来实现从"写地址"到"读数据"的切换,以确保与7位地址设备的兼容。

10位地址核心特点:
- 两段式寻址 :主机先以写方向(0) 发送10位地址(分两个字节)。这确保了总线上的7位地址设备会因首字节
11110不是自己的地址而保持静默。 - 重复起始条件 :在从机应答完整地址后,主机不发送停止信号,而是立即发送一个重复起始条件(Sr) ,然后再次发送地址头,但将方向位改为读(1)。这才是真正的"开始读取"指令。
- 从机视角 :对于STM32从机,在首次地址匹配(写方向)时,其
ADDR中断会被触发。在主机发起重复起始并发送读方向地址时,ADDR中断会再次触发。在HAL库中,这通常会在HAL_I2C_AddrCallback中发生两次,从机需要在合适的时机(例如第二次)启动发送流程(HAL_I2C_Slave_Transmit_IT)。
在stm32手册中的图:

注意:这里的帧头就是发送头字节(11110)与10位地址的头两位(A9,A8)+读写方向(1/0)
主机向一个7位地址的从机写入数据的过程最为直接。

7位地址核心特点 :寻址一步完成。主机发送的第一个字节(SLA+W)就同时宣告了"呼叫哪个从机"和"我要向你写数据"这两件事。
主机向一个10位地址的从机写入数据,寻址过程分两步,但方向自始至终是"写"。

10位地址核心特点:
- 两段式寻址 :主机先发送地址的高位部分(第一个字节,包含
11110前缀),再发送地址的低位部分(第二个字节)。两个字节的方向位都是"写"(0)。 - 无重复起始 :与10位地址的读操作 不同,写操作 在完成两段式寻址后,直接进入数据传送阶段,不需要重复起始条件。因为方向从一开始就是明确的(写)。
- 从机视角 :对于STM32从机,在收到第一个地址字节(
11110xx0)并应答后,ADDR中断可能不会被立即置位,硬件会等待完整地址匹配。通常,在完整地址(第二个字节)匹配并应答后,ADDR中断才会触发。在HAL库中,这会调用HAL_I2C_AddrCallback,从机应在其中启动接收流程(HAL_I2C_Slave_Receive_IT)。

主机向一个7位地址的从机写入数据,这是最常用的简单模式。

7位地址核心特点 :寻址一步完成。主机发送的第一个字节 SLA+W 同时指定了目标从机和"写"操作。
主机向一个10位地址的从机写入数据。关键在于使用保留的 11110 头字节进行寻址。

10位地址核心特点:
- 两段式寻址 :主机先发送一个特殊的头字节 (
11110 A9 A8 0),其中包含地址的最高两位(A9, A8)和写方向位(0)。这个字节会被所有支持I2C的设备检测到,但只有理解10位地址的从机才会继续处理。 - 无重复起始 :由于是写操作,在发送完完整的10位地址(两个字节)并收到ACK后,主机直接开始发送数据,不需要重复起始条件。
- 向后兼容 :
11110这个模式是I2C协议保留的,不属于任何7位地址范围。因此,总线上的7位地址设备在检测到这个头字节时,会认为不是呼叫自己,从而保持静默,不会干扰通信。

主机从一个7位地址的从机读取数据,过程相对直接。

7位地址核心特点 :寻址一步完成。主机发送的第一个字节 SLA+R 就明确表示"我要从你那里读数据"。
主机从一个10位地址的从机读取数据。这是I2C协议中最复杂的标准序列,因为它需要结合"两段式寻址"和"重复起始条件"。

10位地址核心特点:
- 先写后读的"迂回"策略 :主机首先以写方向(0) 发送完整的10位地址。这一步的目的不是 要写数据,而是纯粹为了在总线上安全地宣告"我要与地址为0xXX的从机对话",同时让所有7位设备保持静默。
- 重复起始条件(Sr) :在从机应答后,主机不发送停止信号,而是立即产生一个重复起始条件 。这会将总线重置到起始状态,但不释放总线控制权,允许主机紧接着发起一次新的通信。
- 发送真实意图 :在重复起始后,主机再次发送地址头字节,但这次将方向位改为读(1),这才表明了本次操作的真实目的------读取数据。从机识别到此地址和读方向后,开始发送数据。
- 设计精髓 :这种"虚写实读"的复杂流程,是I2C协议为了在确保与7位设备100%向后兼容的前提下,实现地址扩展而付出的必要代价。

仲裁检测的核心硬件机制
STM32的I2C模块内部有一个 "数据匹配"电路 。它会将自己试图发送到SDA线上的电平 (即TX数据寄存器的值)与 "实际从SDA引脚读取到的电平" 进行实时比较。
仲裁发生的具体时刻 :在发送地址或数据的每一位时,硬件都会在SCL高电平期间(数据有效时段)进行这种比较。
仲裁失败(丢失)的判定条件:
- 当STM32试图输出'1'(释放SDA,期望高电平) ,但检测到SDA线实际为 '0'(低电平) 时。
- 这表明总线上有另一个主机正在驱动'0',根据"线与"规则,该主机拥有更高优先级。
- 硬件立即判定自己输掉仲裁,并自动采取后续行动。
硬件仲裁失败后的自动行为
一旦检测到仲裁失败,STM32的I2C硬件会按固定流程处理:
- 立即切换角色 :自动从 "主发送器" 模式切换到 "从接收器" 模式。
- 释放SDA线:停止驱动SDA线(输出改为高阻态),以避免干扰赢得仲裁的主机继续通信。
- 设置标志位 :将状态寄存器中的 仲裁丢失标志位
ARLO置1。 - 产生中断:如果相关中断使能,会触发I2C错误中断。
- 清除控制位 :自动清除
CR1寄存器中的START位 和STOP位 ,并清空DR数据寄存器。
| 错误标志位 | 所属寄存器 | 触发条件 | 含义 |
|---|---|---|---|
ARLO |
I2C_SR1 |
仲裁丢失。在主机模式下,检测到自己发送的电平与总线实际电平冲突。 | 多主机竞争失败 |
AF |
I2C_SR1 |
应答失败。主机发送地址或数据后,未在第九个时钟周期检测到从机的ACK。 | 从机无响应或地址错误 |
BERR |
I2C_SR1 |
总线错误。检测到非法的起始或停止条件(如总线被外部干扰)。 | 总线格式异常 |
OVR |
I2C_SR1 |
数据溢出。在接收到新数据之前,软件未读取旧数据。 | 软件处理过慢 |
TIMEOUT |
I2C_SR1 |
超时。SCL低电平持续时间超过设定值。 | 时钟线被意外拉低 |
SMBus和IIC对比
| 特性 | I²C (Inter-Integrated Circuit) | SMBus (System Management Bus) | 差异影响 |
|---|---|---|---|
| 设计目标 | 通用、灵活,用于板内IC间通信。 | 专为智能电池、电源管理、传感器 等系统管理任务设计,强调可靠性与互操作性。 | SMBus协议更严格,容错性更强。 |
| 电气规格 | 电压电平跟随I/O电源(如3.3V/5V)。上拉电阻值较灵活。 | 电平固定 :高电平为2.1V-5V,低电平≤0.8V。 上拉电阻严格:通常在1kΩ至10kΩ之间。 | SMBus设备不能直接与I²C的1.8V低电压系统混用。SMBus的强上拉可实现更快的边沿和更低的总线电容容忍度。 |
| 时钟速度 | 标准模式 :100 kHz 快速模式 :400 kHz 高速模式:3.4 MHz | 固定 :10 kHz 至 100 kHz。不支持高速模式。 | SMBus速度较低,旨在保证高噪声环境下的可靠性。 |
| 超时机制 | 无强制超时。时钟拉伸理论上可无限长。 | 强制超时 : - 时钟低超时 :35 ms - 总线空闲超时:主机复位后,若总线忙超时(如35 ms),可强制接管。 | SMBus超时防止总线因设备故障(如卡死拉低SCL)而永久锁死,是关键可靠性特性。 |
| 协议扩展 | 基础协议较简单,扩展功能(如时钟拉伸、广播呼叫)由厂商自定义。 | 定义了标准命令集 :如 Send Byte, Write Word, Read Block 等。 强制使用PEC:部分命令要求包错误校验(Packet Error Checking)。 |
SMBus设备交互更规范,软件驱动通用性更强。PEC增强了数据完整性。 |
| 地址空间 | 7位地址(0x00-0x7F)。保留地址(如0x00广播地址)外均可使用。 | 保留特定地址:如主机默认地址为0x08,SMBus警报响应地址为0x0C。 | SMBus地址分配更规范,避免了设备间的地址冲突。 |
| VDD 容差 | 无特别规定。 | 设备必须能承受 +6V 的电压而不损坏(耐压保护)。 | 增强了系统的鲁棒性,防止因电源异常损坏设备。 |
| 工作温度 | 无特殊要求。 | 通常定义更宽的工作温度范围,适应工业环境。 | 可靠性更高。 |
重要提示 :即使硬件配置了SMBus模式,要实现完整的SMBus协议(如标准命令、PEC校验),通常仍需在软件层实现。
主模式与从模式下,DMA发送与接收的控制流程图(大概):

- 主模式路径(结束或启动新事务)
- 状态 :作为通信的发起者,主机在完成预定数据块的收发后,职责已经履行完毕。总线通常已由硬件发送停止条件(P)释放。
- 后续动作 :回调函数里,你可以选择:
- 结束:什么都不做,通信结束。
- 启动新事务 :基于业务逻辑,立即或延时后发起下一轮对相同或不同从机的通信(再次调用
Master_xxx_DMA)。
- 不需要:重新启用监听或进行其他模式切换。
- 从模式路径(必须重新监听)
- 状态 :作为被动的响应者,从机在一次请求-响应周期结束后,必须立即回到"待命"状态,否则将错过主机的下一次呼叫。
- 强制动作 :在任何一个从机传输完成回调函数(
SlaveTxCpltCallback或SlaveRxCpltCallback)的最后,必须 重新调用HAL_I2C_EnableListen_IT()。这是从机DMA编程的铁律。 - 目的:这将I2C外设再次置于"地址匹配监听"状态,等待下一次总线上的呼叫。
当STM32作为主机,使用DMA向从机写数据时,硬件自动化的程度最高。

关键点:主模式DMA发送由一次函数调用启动,整个数据块的搬移和发送完全由DMA和I2C硬件协同完成,CPU仅在最后收到一次完成中断。
主机使用DMA从从机读取数据,流程对称但方向相反。

STM32作为从机,在收到主机读请求后,使用DMA向主机发送数据。

STM32作为从机,使用DMA接收主机写来的数据。

注意事项
- 内存缓冲区管理 :DMA直接操作内存,必须确保缓冲区在传输期间有效且内存地址对齐(通常要求32位对齐)。
- DMA与I2C中断优先级:DMA传输完成中断和I2C事件中断(如ADDR、NACK)的优先级需合理设置,避免相互阻塞。
- 从模式的特殊性 :从机的DMA传输必须在地址匹配回调(AddrCallback)中启动,而不是在主循环中随意启动。
- 错误处理 :务必实现
HAL_I2C_ErrorCallback,以处理DMA传输中可能出现的总线错误、仲裁丢失或NACK错误。 - 双缓冲技巧:对于连续数据流,可以配置DMA双缓冲模式。当一个缓冲区用于传输时,CPU可处理另一个已满的缓冲区,实现无缝衔接。
时钟控制寄存器(I2C_CCR)
SCL频率计算公式
主模式下的SCL频率计算公式 :
fSCL=fPCLK1(CCR+1)×2f_{SCL}=\frac{f_{PCLK1}}{(CCR+1)\times{2}}fSCL=(CCR+1)×2fPCLK1
CCR值的分情况计算
CCR(时钟控制寄存器)的计算根据模式和占空比不同而不同:
标准模式(100kHz) :
CCR=PCLK1(2×fSCL)CCR=\frac{PCLK1}{(2\times{f_{SCL}})}CCR=(2×fSCL)PCLK1
快速模式(400kHz):
- 占空比 2:1(I2C_DUTYCYCLE_2) :
CCR=PCLK1(3×fSCL)CCR=\frac{PCLK1}{(3\times{f_{SCL}})}CCR=(3×fSCL)PCLK1 - 占空比 16:9(I2C_DUTYCYCLE_16_9) :
CCR=PCLK1(25×fSCL)CCR=\frac{PCLK1}{(25\times{f_{SCL}})}CCR=(25×fSCL)PCLK1
注意:PCLK1是指的当前IIC总线的时钟频率
实际参数约束
- CCR必须是整数,通常向下取整
- 最小CCR值 :
- 标准模式:≥ 4
- 快速模式:≥ 1
- 实际频率计算:用取整后的CCR代入主公式计算实际频率
CCR值的三种模式推导
标准模式(Standard Mode)推导
在标准模式下,I2C协议要求高电平和低电平时间相等 (占空比1:1)。
硬件工作方式:
- 每个SCL周期由 2个时间单位 组成
- 1个单位用于高电平(Thigh)
- 1个单位用于低电平(Tlow)
- 每个时间单位的长度 = CCR × PCLK1周期
txt
1个SCL周期 = 2个时间单位
1个时间单位 = CCR × (1 / PCLK1) 秒
所以:
T_SCL = 2 × CCR × (1 / PCLK1)
f_SCL = 1 / T_SCL = PCLK1 / (2 × CCR)
反过来:
CCR = PCLK1 / (2 × f_SCL)
快速模式,占空比2:1 推导
在快速模式下,当选择占空比2:1时:
- 高电平时间(Thigh) = 2个时间单位
- 低电平时间(Tlow) = 1个时间单位
- 总周期 = 3个时间单位
硬件工作方式: - 配置为快速模式+2:1占空比时,硬件自动按照2:1的比例分配高/低电平
txt
1个SCL周期 = 3个时间单位 (因为2+1=3)
1个时间单位 = CCR × (1 / PCLK1) 秒
所以:
T_SCL = 3 × CCR × (1 / PCLK1)
f_SCL = 1 / T_SCL = PCLK1 / (3 × CCR)
反过来:
CCR = PCLK1 / (3 × f_SCL)
注意:手册中占空比的表示方式(1:2, 9:16)与我们通常说的(2:1, 16:9)是相反的,但实质相同。
快速模式,占空比16:9 推导
在快速模式下,当选择占空比16:9时:
- 高电平时间(Thigh) = 16个时间单位
- 低电平时间(Tlow) = 9个时间单位
- 总周期 = 25个时间单位
硬件工作方式: - 配置为快速模式+16:9占空比时,硬件自动按照16:9的比例分配高/低电平
txt
1个时间单位 = CCR × (1 / PCLK1) 秒
Thigh = 16 × 时间单位
Tlow = 9 × 时间单位
T_SCL = Thigh + Tlow = (16 + 9) × 时间单位 = 25 × 时间单位
所以:
T_SCL = 25 × CCR × (1 / PCLK1)
f_SCL = 1 / T_SCL = PCLK1 / (25 × CCR)
反过来:
CCR = PCLK1 / (25 × f_SCL)
注意:手册中占空比的表示方式(1:2, 9:16)与我们通常说的(2:1, 16:9)是相反的,但实质相同。
示例1:标准模式100kHz,PCLK1=8MHz
已知参数
- PCLK1 = 8,000,000 Hz
- 目标频率 f_SCL = 100,000 Hz
- 模式:标准模式(Sm,≤100kHz)
计算过程
txt
1. 使用标准模式公式:CCR = PCLK1 / (2 × f_SCL)
2. 代入数值:CCR = 8,000,000 / (2 × 100,000)
3. 计算:CCR = 8,000,000 / 200,000 = 40
4. CCR是整数,满足要求(标准模式要求CCR≥4)
5. 计算实际频率:f_实际 = PCLK1 / (CCR × 2) = 8,000,000 / (40 × 2) = 8,000,000 / 80 = 100,000 Hz
6. 误差:0%(完美匹配)
结果
txt
标准模式,100kHz配置:
理论CCR = 40
实际CCR = 40
实际频率 = 100,000 Hz
误差 = 0%
结论:完美配置
示例2:快速模式400kHz,占空比2:1,PCLK1=36MHz
已知参数
- PCLK1 = 36,000,000 Hz
- 目标频率 f_SCL = 400,000 Hz
- 模式:快速模式,占空比2:1
计算过程
txt
1. 使用快速模式2:1公式:CCR = PCLK1 / (3 × f_SCL)
2. 代入数值:CCR = 36,000,000 / (3 × 400,000)
3. 计算:CCR = 36,000,000 / 1,200,000 = 30
4. CCR是整数,满足要求(快速模式要求CCR≥1)
5. 计算实际频率:f_实际 = PCLK1 / (CCR × 3) = 36,000,000 / (30 × 3) = 36,000,000 / 90 = 400,000 Hz
6. 误差:0%(完美匹配)
结果
txt
快速模式400kHz,占空比2:1配置:
理论CCR = 30
实际CCR = 30
实际频率 = 400,000 Hz
误差 = 0%
结论:完美配置
示例3:快速模式400kHz,占空比16:9,PCLK1=36MHz
已知参数
- PCLK1 = 36,000,000 Hz
- 目标频率 f_SCL = 400,000 Hz
- 模式:快速模式,占空比16:9
计算过程
txt
1. 使用快速模式16:9公式:CCR = PCLK1 / (25 × f_SCL)
2. 代入数值:CCR = 36,000,000 / (25 × 400,000)
3. 计算:CCR = 36,000,000 / 10,000,000 = 3.6
4. CCR取整:向下取整为3,但需要检查最小值要求
5. 验证:快速模式最小CCR=1,3≥1,所以CCR=3
6. 计算实际频率:f_实际 = PCLK1 / (CCR × 25) = 36,000,000 / (3 × 25) = 36,000,000 / 75 = 480,000 Hz
7. 误差:(480,000 - 400,000)/400,000 × 100% = 20%(误差过大)
重新考虑向上取整:
8. CCR向上取整:3.6向上取整为4
9. 计算实际频率:f_实际 = 36,000,000 / (4 × 25) = 36,000,000 / 100 = 360,000 Hz
10. 误差:(360,000 - 400,000)/400,000 × 100% = -10%
11. I2C协议允许误差±10%,因此CCR=4是可接受的
结果
txt
快速模式400kHz,占空比16:9配置:
理论CCR = 3.6
CCR取整选项:
1. 向下取整:CCR=3 → 实际频率=480,000Hz → 误差=+20%(超出允许范围)
2. 向上取整:CCR=4 → 实际频率=360,000Hz → 误差=-10%(在允许范围内)
选择CCR=4
实际频率 = 360,000 Hz
误差 = -10%
结论:可接受配置
示例4:标准模式100kHz,PCLK1=72MHz
已知参数
- PCLK1 = 72,000,000 Hz
- 目标频率 f_SCL = 100,000 Hz
- 模式:标准模式
计算过程
txt
1. 使用标准模式公式:CCR = PCLK1 / (2 × f_SCL)
2. 代入数值:CCR = 72,000,000 / (2 × 100,000)
3. 计算:CCR = 72,000,000 / 200,000 = 360
4. CCR是整数,满足要求
5. 计算实际频率:f_实际 = PCLK1 / (CCR × 2) = 72,000,000 / (360 × 2) = 72,000,000 / 720 = 100,000 Hz
6. 误差:0%(完美匹配)
结果
txt
标准模式,100kHz配置:
理论CCR = 360
实际CCR = 360
实际频率 = 100,000 Hz
误差 = 0%
结论:完美配置
示例5:快速模式400kHz,占空比2:1,PCLK1=72MHz
已知参数
- PCLK1 = 72,000,000 Hz
- 目标频率 f_SCL = 400,000 Hz
- 模式:快速模式,占空比2:1
计算过程
txt
1. 使用快速模式2:1公式:CCR = PCLK1 / (3 × f_SCL)
2. 代入数值:CCR = 72,000,000 / (3 × 400,000)
3. 计算:CCR = 72,000,000 / 1,200,000 = 60
4. CCR是整数,满足要求
5. 计算实际频率:f_实际 = PCLK1 / (CCR × 3) = 72,000,000 / (60 × 3) = 72,000,000 / 180 = 400,000 Hz
6. 误差:0%(完美匹配)
结果
txt
快速模式400kHz,占空比2:1配置:
理论CCR = 60
实际CCR = 60
实际频率 = 400,000 Hz
误差 = 0%
结论:完美配置