关于stm32f1系列手册中IIC部分的笔记,对应STM32F10xxx参考手册中第24章节。这是我个人在观看手册时的一些问题的汇总
在hal库iic中,控制ACK、NACK的场景
软件控制的ACK、NACK场景
作为主机接收器,接收最后一个字节前:
- 这是最重要的软件控制场景! 主机在接收一串数据的最后一个字节之前 ,必须先将
CR1.ACK位清零 ,以指示硬件:"下一个字节我不再需要了,收到后请回复NACK"。
需要主动终止接收时: - 如果主机在任何时刻想强制终止接收(例如从机发送了意外数据),软件可以立即清零
ACK位。硬件将在接收完当前字节后回复NACK,随后主机可发送停止条件。
一切围绕控制寄存器1的ACK位 (I2C_CR1.ACK) 展开:
ACK = 1:使能应答。硬件在成功接收字节后自动发送ACK。ACK = 0:禁用应答。硬件在接收字节后自动发送NACK。
ACK/NACK 的硬件自动回复场景
当STM32的I2C硬件处于以下状态时,ACK/NACK的生成和SDA线的驱动完全由硬件自动处理,无需软件干预:
- 作为从机,地址匹配时 :
- 自动ACK :当STM32检测到总线上呼叫的地址与自身
OAR寄存器中的地址匹配时,硬件会在第9个SCL时钟周期自动拉低SDA线,发出ACK信号。
- 自动ACK :当STM32检测到总线上呼叫的地址与自身
- 作为接收方(无论主机或从机),成功接收一个数据字节后 :
- 默认自动ACK:每当成功接收一个非结束字节后,硬件默认会在第9个SCL周期自动回复ACK,告诉发送方"请继续发送下一个字节"。
- 启用PEC且校验失败时 :
- 自动NACK :当启用PEC (
ENPEC=1) 且硬件计算出的CRC值与接收到的PEC字节不匹配时,硬件会自动回复NACK(在第9个SCL周期保持SDA高电平)。这是硬件保证数据完整性的重要机制。
- 自动NACK :当启用PEC (
在hal库iic中,普通主发送与存储器模式发送
在STM32F1的I2C主发送模式中,确实有普通主发送 和存储器模式发送 两种方式,它们的主要区别在于寻址方式和用途。
普通主发送(Standard Master Transmitter)
特点:
- 用于通用I2C设备通信
- 7位或10位从设备地址
- 适用于大多数标准的I2C外设(传感器、EEPROM等)
- 发送流程相对简单
txt
START → 发送从设备地址+写位 → 等待ACK → 发送数据字节 → 等待ACK → ... → STOP
存储器模式发送(Memory Mode Transmitter)
特点:
- 专为EEPROM类存储器设计
- 使用双地址模式:先发送设备地址,再发送存储器内部地址
- 支持页写入操作
- 通常用于24Cxx系列EEPROM等存储器
txt
START → 发送设备地址+写位 → 等待ACK → 发送存储器内部地址(高字节) → 等待ACK→ 发送存储器内部地址(低字节) → 等待ACK → 发送数据字节 → 等待ACK → ... → STOP
在hal库iic中,什么的广播呼叫模式
广播呼叫(General Call)是I2C总线协议中的一个特殊功能,允许主机同时向总线上所有从机设备发送数据或命令,而无需指定具体的从机地址。以下是对广播呼叫模式的详细解释:
- 广播呼叫地址 :
- I2C协议规定了一个特殊的保留地址作为广播呼叫地址,即0x00(7位地址格式下,通常与读写位组合后成为8位的0x00或0x01,但一般说广播地址是0x00)。
- 当主机发送起始条件后,紧接着发送广播呼叫地址(0x00)时,总线上所有能够识别广播呼叫的从机都应该响应这个地址。
- 工作方式 :
- 主机发送广播呼叫地址后,总线上所有配置为支持广播呼叫的从机都会拉低SDA线以应答ACK,表示它们已准备好接收后续数据。
- 随后,主机可以发送一个或多个数据字节,这些数据会被所有支持广播呼叫且已应答的从机接收。
- 广播呼叫通常用于同时配置多个相同的从机设备(例如,初始化一组传感器),或向所有从机发送同步命令(如复位、全局启动/停止等)。
- 使能与识别 :
- 并非所有从机都必须支持广播呼叫。从机设备通常通过配置寄存器或硬件设置来决定是否响应广播呼叫。
- 在STM32的I2C外设中,可以通过配置I2C_CR1寄存器的相应位(例如,在标准外设库中为I2C_CR1_ENGC位)来使能或禁用广播呼叫功能。
- 在HAL库中,通常可以在初始化I2C时通过配置参数来启用广播呼叫模式。
- 数据方向 :
- 广播呼叫总是由主机向从机写数据,即数据方向位(R/W)为写(0)。因此,广播呼叫地址通常被视为写地址。
- 应用场景 :
- 系统初始化:同时配置多个相同从机设备的参数,如采样率、量程等。
- 同步命令:发送全局复位、开始测量、停止测量等命令。
- 时钟同步:在需要严格同步的多设备系统中,广播呼叫可用于发送同步时间戳。
- STM32 HAL库中的使用 :
- 在STM32上使用HAL库时,如果需要从机响应广播呼叫,需要在初始化I2C从机时启用广播呼叫功能。例如,在
I2C_InitTypeDef结构中,有一个GeneralCallMode字段,可以设置为I2C_GENERALCALL_ENABLE。 - 当启用广播呼叫后,从机在监听模式下也会对地址0x00产生地址匹配中断。在地址回调函数中,可以通过检查匹配的地址来判断是普通寻址还是广播呼叫。
- 在STM32上使用HAL库时,如果需要从机响应广播呼叫,需要在初始化I2C从机时启用广播呼叫功能。例如,在
- 注意事项 :
- 广播呼叫地址是一个保留地址,因此普通从机设备不应将0x00作为自己的地址。
- 广播呼叫可能会增加总线负载,因为所有使能了广播呼叫的从机都会响应并接收数据,因此需谨慎使用以避免不必要的总线冲突。
- 在广播呼叫序列中,从机通常根据接收到的第一个数据字节(命令字节)来决定后续动作,因此协议设计时需考虑如何区分广播数据与普通数据。
总结:广播呼叫是I2C总线中一种一对多的通信机制,通过特殊地址0x00寻址,使得主机能够同时向多个从机发送数据或命令。在STM32的HAL库中,可以通过配置I2C初始化参数来启用该功能,并在地址匹配回调中区分广播呼叫和普通寻址。
注意事项
- 选择性响应:并非所有I2C从设备都支持或需要响应广播呼叫。许多设备的默认设置是忽略广播地址。是否需要启用,取决于你的具体应用和设备手册。
- 地址冲突 :广播地址
0x00是协议保留地址,严禁 将任何从机设备的独立地址设置为0x00,否则会造成寻址混乱。 - 协议定义 :广播呼叫的具体数据格式(尤其是第一个命令字节的含义)没有统一的国际标准,通常由系统设计者或设备制造商自行定义。在设计和实现时,需要查阅相关设备的数据手册或制定统一的系统通信协议。
注意:在I2C协议中,从机设备无法在广播呼叫后主动向主机发送数据来"回应" 。这是由I2C的通信根本原则决定的:任何时候,总线只能由一个设备(主机)控制通信的发起和时序。
可行的回应方案:既然不能主动回应,就需要设计一套由主机主导的协调协议。以下是三种最主流的方案:
| 方案 | 核心思路 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 1. 主机轮询查询 | 广播后,主机依次 呼叫各从机地址,单独询问状态或数据。 | 实现简单、可靠、无冲突。 | 效率较低,响应速度慢。 | 从机数量不多,或对实时性要求不高的系统。 |
| 2. 异步中断上报 | 从机通过独立的硬件中断线(如GPIO)通知主机"我有数据"。主机收到中断后,再轮询中断源。 | 响应及时,总线效率高。 | 需占用额外GPIO引脚,硬件连线复杂。 | 对事件响应速度要求高的系统。 |
| 3. 静默处理,无需回应 | 从机默默执行广播命令(如复位、配置),不向主机做任何确认。 | 最简单,最符合广播"命令"的初衷。 | 主机无法得知命令执行结果。 | 全局复位、广播配置等不要求反馈的场景。 |
注意:
- 广播呼叫不是"问答" :它本质是一个单向的、由主机发起的"命令下达"或"事件通知" 机制。设计系统时,必须放弃"群发群回"的想法。
- 标准模式是"发令+轮询" :广播呼叫 + 主机依次轮询是解决多设备回应问题的最标准、最可靠的软件协议。绝大多数I2C多设备系统都采用此模式。
- 根据场景选择方案 :
- 需要确认结果 -> 采用 方案一(轮询)。
- 需要极速响应 -> 考虑 方案二(中断线)。
- 只需执行,无需反馈 -> 采用 方案三(静默执行)。
在hal库iic中,什么是时钟拉伸、什么是"不拉伸"模式
"不拉伸"(No-stretch)模式,在STM32的I2C上下文中,特指 "时钟不拉伸"(Clock Stretching Disable) 模式。它是针对I2C从机(Slave)行为的一个关键配置选项。
什么是"时钟拉伸"?
这是I2C协议允许从机控制总线时钟的一种合法机制。
- 目的 :当从机需要更多时间来处理数据(如写入Flash、处理中断)而无法立即响应主机时,可以通过主动拉低SCL(串行时钟)线来强制主机进入等待状态。
- 过程 :主机在释放SCL线(准备产生下一个时钟脉冲)后,会检测SCL电平。如果从机将其拉低,主机就暂停发送后续时钟和数据,直到从机释放SCL线,主机才继续。
- 本质 :从机对主机说:"等等,我还没准备好。"
时钟拉伸是I2C协议保证数据可靠传输的重要特性,尤其适用于处理能力较弱或需要执行耗时操作的从机。
什么是"不拉伸"(No-stretch)模式?
当你在STM32的I2C从机配置中启用"不拉伸"模式时,即明确禁止了该STM32从机使用时钟拉伸功能 。
在这种模式下:
- STM32从机承诺 :在任何情况下,绝不主动拉低SCL线来拖延主机。
- STM32从机必须:在硬件规定的极短时间窗口内,完成对主机请求的响应(如产生ACK/NACK、读取或输出数据位)。
- 后果 :如果STM32的CPU或软件来不及在规定时间内处理数据(例如,数据未就绪或未及时读取接收寄存器),将可能导致数据丢失、NACK或通信错误。
核心总结
- "不拉伸"模式 就是禁用STM32作为I2C从机时的"时钟拉伸"功能。
- 它用通信的可靠性 (允许从机拖延)换取了总线的确定性和可控性(主机完全掌握时序)。
- 这是一个高级功能 ,除非你的应用场景明确需要(如多主机系统、特殊主机兼容性),否则建议保持默认的允许拉伸状态,这样最安全可靠。
- 如果启用,你必须投入精力优化代码(使用DMA、提高中断优先级、降低时钟速度),以确保STM32能"跟得上节奏"。
在hal库iic中,什么是监听模式
在STM32的HAL库中,I2C的监听模式是一种特殊的工作状态,它让设备能够在I2C总线上"侦听"与自己地址匹配的数据帧,而自身不主动发起通信 [。
简单来说,当你的STM32作为I2C从设备时,启用监听模式后,它会安静地等待主机呼叫自己的地址,一旦地址匹配成功,便会触发中断,你的程序即可知道有主机在呼叫自己,从而准备进行数据收发。
为了让这个概念更清晰,下表将监听模式与I2C的其他两种主要工作模式进行了对比:
| 模式 | 角色 | 行为特点 | 主要目的 |
|---|---|---|---|
| 主机模式 | 通信的发起者 | 主动产生起始信号、发送从机地址、控制时钟 | 向其他I2C设备读写数据 |
| 从机模式 | 通信的响应者 | 被动等待主机寻址,根据指令收发数据 | 响应主机的数据请求或命令 |
| 监听模式 | 总线的侦听者 | 被动监听总线地址,自身不主动收发数据 | 检测是否有主机在呼叫自己,是实现从机中断接收的基础。 |
它本质上是一种地址匹配中断机制:
- 你将STM32的I2C配置为从机,并设置自己的7位或10位从机地址。
- 调用
HAL_I2C_EnableListen_IT()函数开启监听模式。 - I2C外设开始监测总线上的所有地址帧。
- 当主机发送的从机地址与你的地址一致时,STM32的I2C硬件会自动设置标志位并触发中断。
- 在中断服务程序中,你可以判断事件类型(例如,是主机要发送数据给你,还是向你请求数据),然后调用对应的HAL函数(如
HAL_I2C_Slave_Receive_IT())来接收或发送数据。
在HAL库的状态机中,当设备进入监听模式时,其状态变量(例如hi2c->State)的特定标志位会被置位(官方文档常提到其状态位图的位3表示监听状态,以区别于普通的"就绪"或"忙碌"状态。
注意:监听模式 ≠ 数据收发:它只负责"监听地址"。真正的数据收发是在地址匹配中断后,由你主动调用从机收发函数来启动的

可以简单的理解为:监听模式其实就是一个从机地址检测中断触发,触发之后的步骤与普通的从机模式一样。

总结:
- 监听模式是触发器 :它只负责"监听地址"并"产生中断"。它不参与任何实际的数据位传输。
- 从机模式是执行体 :真正的数据发送和接收,是由
HAL_I2C_Slave_Transmit_IT或HAL_I2C_Slave_Receive_IT函数启动的标准从机通信过程。 - HAL库的设计逻辑:HAL库通过监听模式将"地址识别"这个动作事件化、中断化,从而让CPU在等待呼叫时得以解放。这是一种更高效、更事件驱动的编程模型。
所以监听模式是实现中断式从机通信 所必需的一个前置配置步骤。它打开了"地址匹配中断"这个开关,之后的道路就和普通的从机通信没有区别了。
在hal库iic中,什么是序列发送
序列发送(Sequential Transfer) 是I2C协议中的一种高级通信模式,指主设备在一次"占用总线"期间,连续发送(或接收)多个数据包,而只在最后产生一个停止信号 。这是一种高效、无间断的连续通信方式。
与常规发送的核心区别在于停止条件(STOP)的位置:
| 特性 | 常规发送 (如 Master_Transmit) |
序列发送 (如 Master_Seq_Transmit) |
|---|---|---|
| 停止条件 | 每次传输后都有STOP | 只在序列完全结束后有一个STOP |
| 时序 | S + 数据包1 + P, S + 数据包2 + P |
S + 数据包1 + Sr + 数据包2 + P |
| 总线释放 | 每包后都释放总线 | 整个序列期间独占总线 |
| 效率 | 较低,有额外开销 | 高,无间歇开销 |
| 目的 | 简单、独立的操作 | 需要原子性或高速的连续操作 |

关键点 :序列发送使用的是 "重复起始条件"(Repeated Start, Sr) 来连接多个数据包。Sr在电气特性和功能上与起始信号S相同,但它不释放总线,从而保证了操作的连续性。
在hal库iic中,什么是仲裁
什么是I2C仲裁?仲裁是I2C协议内置的、用于防止多个主设备同时控制总线造成数据混乱的硬件竞争解决机制 。当两个或多个主设备同时尝试开始通信时,仲裁过程会自动决定哪一个主设备获得总线控制权,而其他主设备则"礼貌地"退出,整个过程对数据没有破坏。
物理基础:I2C总线的SDA数据线是"线与"逻辑。这意味着:
- 0(低电平) 是"强"信号。任何一个设备拉低SDA,整条线就是低电平。
- 1(高电平) 是"弱"信号。只有当所有设备都释放SDA(输出高)时,总线才呈现高电平。
仲裁过程:仲裁发生在SDA线上,由硬件自动完成。我们通过一个简单的例子来看:
假设主设备1 想发送二进制位 1011...,主设备2 想发送 1001...。
- 两个主设备同时启动,并各自在总线上驱动起始条件
S,然后是地址/数据位。 - 它们驱动的前两位
1和0完全相同,总线状态与它们驱动的一致,没有冲突。 - 当发送第3位时,主设备1 驱动
1(释放SDA,期望高电平),而主设备2 驱动0(拉低SDA)。 - 根据"线与"特性,总线实际电平为
0(低电平)。 - 主设备1 检测到自己输出
1,但总线却是0,便知道自己"输掉"了仲裁。它立即关闭自己的输出驱动器,转为从机接收模式,并开始监听总线,看赢得仲裁的主设备(主设备2)要做什么。 - 主设备2对仲裁过程毫无察觉,它看到总线电平与自己的输出一致,便继续完成自己的通信。
整个仲裁过程对所有从设备是透明的,它们只会响应最终赢得仲裁的那个主设备的寻址。
注意 :
虽然只有主设备参与仲裁,但仲裁的发生与设备角色动态变化相关:
- 一个设备在主发送(Master Transmitter) 模式下,才可能与其他主发送设备发生仲裁。
- 一个设备在从接收(Slave Receiver) 模式下,不可能发起仲裁。
- 一个设备可以在一次通信中从主设备变为从设备(输掉仲裁后),反之亦然。这是I2C协议支持多主机的精妙之处。
与"仲裁"易混淆的概念:地址冲突
虽然严格来说不是"仲裁",但有一种类似情况:如果总线上有两个从设备被意外配置了相同的I2C地址 ,当主机寻址这个地址时,两个从机都会应答,在SDA上造成冲突(一个想拉低,一个想释放),最终可能导致主机收到的ACK信号畸变,通信失败。
这虽然也体现了"线与"的竞争,但它不是协议定义的多主仲裁,而是一种错误配置。解决方法是确保每个从设备地址唯一。
总结
- 仲裁 是I2C协议为解决多主竞争 而设计的硬件自动过程。
- 只有处于"主发送"模式的设备才会参与仲裁。从设备不发起通信,故无仲裁。
- 仲裁的核心原理是利用SDA的"线与"特性,通过逐位比较,让发送"低电平0"的设备胜出,发送"高电平1"的设备优雅退出。
- 在HAL库编程中,你无需主动管理仲裁过程,但必须在错误处理中识别并妥善处理仲裁丢失(
HAL_I2C_ERROR_ARLO)的情况,通常实现为带延迟的重试机制。
在hal库iic中,什么是OTHER
在OTHER是一个状态标志**
在SR2寄存器中有一个OTHER_F位(bit 15),它表示:
- 值为1:从设备模式下,时钟延展正在进行中
- 值为0:无时钟延展活动
关键特点:
- 时钟延展使能
- 从设备可以通过拉低SCL线来暂停通信
- 用于处理数据处理延迟
工作原理:
- 正常通信:主设备控制SCL时钟
- 需要延时:从设备处理数据时,拉低SCL
- 恢复通信:从设备准备就绪后,释放SCL
- 继续传输:主设备检测到SCL释放后继续通信
时钟延展(Clock Stretching) 是I2C协议中的一个重要特性,它允许从设备 在需要更多时间处理数据时,通过拉低SCL时钟线 来暂停通信,直到从设备准备好继续传输。
txt
主设备:释放SCL,等待从设备
从设备:拉低SCL,暂停通信
↓
从设备:处理数据
↓
从设备:释放SCL
↓
主设备:检测SCL变高,继续通信
在hal库iic中,什么是SMBus
SMBus和I²C在物理连接和基础帧结构上高度相似,但SMBus在电气规格、协议扩展和可靠性方面进行了严格标准化,可以理解为 I²C的一个严格子集和专业化分支。两者的关系类似"通用语言"(I²C)与"行业标准用语"(SMBus)。
在hal库iic中,主机模式、从机模式、监听模式对比
1. 主机模式
场景 :你(主机)用遥控器(发起通信)指挥电视机(从机)换台、调音量。
特点 :你是主动的发起者。遥控器(主机)握有绝对控制权,决定何时开始、和谁说话、说什么、何时结束。
2. 从机模式
场景 :电视机(从机)加电后就处于"随时待命 "的状态。
特点 :它是被动的响应者。只要电源接通(初始化完成),它就准备好接受指令,但它自己不能主动找遥控器说话。
3. 监听模式(关键区别在这里)
场景 :一个普通的"随时待命"的从机,其CPU必须时刻"睁大眼睛"盯着总线,看有没有自己的消息(轮询 ,效率低)。而带监听模式 的从机,就像一个配备了"智能门铃"的电视机。
核心区别 :它让电视机(从机)的CPU可以去"打盹"或处理其他任务。当遥控器(主机)按下属于这台电视的按键(地址匹配 )时,"智能门铃"(监听模式)会"叮咚"一声(触发中断 )把CPU叫醒。CPU醒来后,才开始正式接听指令(进入从机收发流程)。
本质 :监听模式是一个 "地址匹配中断使能器" 。它本身不负责 数据的实际收发,只负责检测是否有主机在呼叫自己 ,并在检测到时通知CPU。真正的数据接收或发送,是在CPU被唤醒后,再启动标准的从机接收(HAL_I2C_Slave_Receive_IT) 或从机发送(HAL_I2C_Slave_Transmit_IT) 模式来完成的。
在hal库iic中,主模式下的SCL频率计算公式
SCL频率计算公式
主模式下的SCL频率计算公式 :
f S C L = f P C L K 1 ( C C R + 1 ) × 2 f_{SCL}=\frac{f_{PCLK1}}{(CCR+1)\times{2}} fSCL=(CCR+1)×2fPCLK1
CCR值的分情况计算
CCR(时钟控制寄存器)的计算根据模式和占空比不同而不同:
标准模式(100kHz) :
C C R = P C L K 1 ( 2 × f S C L ) CCR=\frac{PCLK1}{(2\times{f_{SCL}})} CCR=(2×fSCL)PCLK1
快速模式(400kHz):
- 占空比 2:1(I2C_DUTYCYCLE_2) :
C C R = P C L K 1 ( 3 × f S C L ) CCR=\frac{PCLK1}{(3\times{f_{SCL}})} CCR=(3×fSCL)PCLK1 - 占空比 16:9(I2C_DUTYCYCLE_16_9) :
C C R = P C L K 1 ( 25 × f S C L ) 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%
结论:完美配置
在hal库iic中,BTF、TxE、RxNE的区别
TxE(Transmit Data Register Empty)
- 位置:SR1寄存器的第7位
- 含义:当数据寄存器(DR)为空时,该位由硬件置1。这意味着CPU可以写入下一个要发送的数据到DR寄存器。
- 清除条件:写入数据到DR寄存器将清除该位(在写入数据后,DR非空,所以TxE清零)。
RxNE(Receive Data Register Not Empty)
- 位置:SR1寄存器的第6位
- 含义:当数据寄存器(DR)非空(即接收到的数据已经可以从DR读取)时,该位由硬件置1。
- 清除条件:读取DR寄存器将清除该位(读取后DR为空,所以RxNE清零)。
BTF(Byte Transfer Finished)
- 位置:SR1寄存器的第2位
- 含义:当一个字节传输完成(包括发送和接收)时,该位由硬件置1。具体来说,在发送模式下,它表示一个字节已经从移位寄存器完全发送到总线上;在接收模式下,它表示一个字节已经完整地从总线接收到移位寄存器并已传输到DR寄存器(如果DR寄存器为空)或者已经在DR寄存器中(如果之前的数据未被读取)。
- 清除条件:在发送模式下,写入下一个数据到DR寄存器会清除BTF(同时也会清除TxE,因为写入数据后DR非空);在接收模式下,读取DR寄存器会清除BTF(同时也会清除RxNE,因为读取后DR为空)。
在发送模式(主发送或从发送)下:
- TxE:表示DR寄存器为空,可以写入下一个要发送的数据。但是,此时上一个数据可能还在移位寄存器中发送(即还没有完全发送到总线上)。
- BTF :表示移位寄存器也已经空了(即上一个数据已经完全发送到总线),并且DR寄存器也为空(即TxE也为1)。因此,当BTF=1时,一定满足TxE=1,但反之不一定。
注意:在发送模式下,BTF=1意味着一个完整的字节(包括8位数据和应答位)已经完成传输。此时,如果DR寄存器中还没有被写入新的数据,那么移位寄存器和DR都为空,可以写入下一个数据。如果此时不写入新数据,那么SCL时钟线将会被拉低(如果时钟延长使能),直到写入新数据为止。
在接收模式(主接收或从接收)下:
- RxNE:表示DR寄存器中有新接收到的数据可以读取。但是,此时下一个字节可能已经开始接收(移位寄存器中正在接收下一个字节)。
- BTF:表示一个完整的字节已经接收完成,并且数据已经存储在DR寄存器中(如果DR寄存器中之前的数据已经被读取,那么新接收的数据会从移位寄存器转移到DR,此时DR非空,RxNE=1;如果之前的数据没有被读取,那么新接收的数据会留在移位寄存器中,直到DR被读取,此时DR非空,RxNE=1,但移位寄存器满,所以BTF=0)。因此,在接收模式下,BTF=1表示当前字节已经接收完成,并且下一个字节的接收还没有开始(因为移位寄存器已经将数据转移到DR,且DR未被读取,所以移位寄存器空,等待下一个字节的接收)。实际上,在接收模式下,BTF=1通常出现在DR寄存器中已经有数据(RxNE=1)且移位寄存器也已经空(即一个字节已经完整接收)的情况下。
发送模式:
假设我们要发送两个字节:Data1和Data2。
- 写入Data1到DR寄存器,此时TxE清零(因为DR非空),BTF=0(因为Data1还没有开始发送)。
- 当Data1从DR寄存器转移到移位寄存器时,TxE置1(因为DR空了),此时可以写入Data2到DR寄存器。
- 当Data1的最后一个位(包括应答位)从移位寄存器发送到总线上时,BTF置1。此时,如果已经写入了Data2到DR寄存器,那么Data2会立即从DR寄存器转移到移位寄存器,然后开始发送Data2,同时BTF清零(因为移位寄存器非空,正在发送Data2)。如果没有写入Data2,那么BTF保持为1,SCL被拉低(时钟延长),直到写入Data2。
接收模式:
假设我们要接收两个字节:Data1和Data2。
- 当接收到Data1的最后一个位(包括应答位)时,Data1从移位寄存器转移到DR寄存器,此时RxNE置1(DR非空),同时BTF=0(因为移位寄存器空,但DR有数据,所以不满足BTF条件?注意:在接收模式下,BTF=1的条件是DR寄存器非空(RxNE=1)且移位寄存器也空(即一个字节已经完成接收并转移到DR,且没有新字节正在接收)。实际上,在接收模式下,当接收完一个字节后,如果DR寄存器中还有数据(即上一次的数据没有被读取),那么新接收的数据会留在移位寄存器中,直到DR被读取。所以,BTF=1出现在DR有数据(RxNE=1)且移位寄存器空(即没有新数据正在接收)的情况下。
- 如果我们在RxNE=1时读取了DR寄存器(得到Data1),那么RxNE清零。此时,如果正在接收Data2(移位寄存器非空),那么BTF=0。当Data2接收完成时,Data2从移位寄存器转移到DR,RxNE再次置1,同时移位寄存器空,如果此时DR中已经有数据(即Data2),那么BTF=1(因为DR非空且移位寄存器空)。
关键点:
- BTF标志位表示一个完整的字节传输(包括应答位)已经完成,并且数据寄存器(DR)和移位寄存器都处于"就绪"状态(即发送模式下两个寄存器都为空,接收模式下DR有数据且移位寄存器为空)。
- TxE和RxNE只关注数据寄存器(DR)的状态,而不关心移位寄存器的状态。
- 在发送模式下,BTF=1意味着TxE=1;在接收模式下,BTF=1意味着RxNE=1。
BTF(Byte Transfer Finished)标志位置1表示一个字节的传输已经完成,这包括8位数据加上1位应答(ACK)或非应答(NACK)的完整过程
- 在发送模式(主机或从机发送器)下:
- 当一个字节的数据(8位)从移位寄存器中发送出去,并且收到了来自接收方的应答位(ACK或NACK)后,BTF标志位会置1。
- 所以,此时BTF=1表示一个字节(包括应答)已经完成传输。
- 在接收模式(主机或从机接收器)下:
- 当接收完一个字节的数据(8位)并发送了应答位(ACK或NACK)后,BTF标志位会置1。
- 同样,这也表示一个完整的字节传输过程(包括应答)已经结束。
然而,需要注意的是,在发送模式下,BTF=1的条件是:
- 数据寄存器DR为空(即TxE=1)并且移位寄存器也为空(即当前没有数据正在移位输出)。
- 同时,应答位已经被接收(对于发送方来说是收到应答,对于接收方来说是发出应答)。
在接收模式下,BTF=1的条件是:
- 数据寄存器DR为满(即RxNE=1)并且移位寄存器为空(即已经接收到的数据已经转移到DR,并且没有新的数据正在移位接收)。
- 同时,应答位已经发送(对于接收方来说是发出应答,对于发送方来说是收到应答)。
在hal库iic中,ITBUFEN(缓冲器中断使能)、ITEVTEN(事件中断使能)、ITERREN(出错中断使能)
ITBUFEN(缓冲器中断使能)
- 控制对象 :I2C 自身的 TXE(发送空)和 RXNE(接收非空)事件的中断
- 作用 :决定是否在
TXE=1或RXNE=1时产生 CPU 中断 - 位置:CR2 寄存器的 bit 10
- 关系 :与 CPU 中断相关
ITEVTEN(事件中断使能):
- 控制对象 :I2C 自身的 SB(起始位)、ADDR(地址匹配)、ADD10(10位地址头序列)、STOPF(停止位检测)、BTF(字节传输完成)事件的中断
- 作用 :决定是否在
SB=1、ADDR=1、ADD10=1、STOPF=1或BTF=1时产生 CPU 中断 - 位置:CR2 寄存器的 bit 9
- 关系 :与 CPU 中断相关,用于控制通信流程中的关键事件
ITERREN(出错中断使能):
- 控制对象 :I2C 自身的 BERR(总线错误)、ARLO(仲裁丢失)、AF(应答失败)、OVR(过载/欠载)事件的中断
- 作用 :决定是否在
BERR=1、ARLO=1、AF=1或OVR=1时产生 CPU 中断 - 位置:CR2 寄存器的 bit 8
- 关系 :与 CPU 中断相关,用于错误处理
补充说明:
- 这三个中断使能位(ITEVTEN、ITBUFEN、ITERREN)分别控制不同类型的事件中断,它们都位于CR2寄存器中,且都是控制是否产生CPU中断。
- 其中,ITEVTEN和ITBUFEN控制的事件中断共享同一个中断向量(I2Cx_EV_IRQn),而ITERREN控制的中断则使用另一个中断向量(I2Cx_ER_IRQn)。
- 在配置中断时,需要同时配置相应的NVIC中断通道。
I2C的中断设计:
- I2C协议比UART复杂,因为它是一种多主从、双向、半双工的同步串行总线。I2C的通信过程需要严格的状态机控制,因此将中断分为事件中断、缓冲中断和错误中断可以更精细地控制中断响应,提高效率。
- 事件中断(ITEVTEN)处理的是协议状态事件,如起始位、地址匹配、停止位等。这些事件标志着通信阶段的变化,需要及时处理以推进状态机。
- 缓冲中断(ITBUFEN)专门处理数据缓冲区的空满状态(TXE和RXNE)。这样设计允许用户在处理数据时不必关心协议状态,可以分开处理。
- 错误中断(ITERREN)则专门处理通信中可能出现的错误,如仲裁丢失、应答失败等。
在hal库iic中,TRISE计算
为什么需要这个设置?
这完全是由 I2C总线的物理特性(RC电路) 决定的。I2C总线是开源漏(Open-Drain) 结构,依赖外部上拉电阻将信号拉到高电平。
- 物理现实 :SDA和SCL线并非理想的数字信号。它们与上拉电阻(R)、总线上的引脚电容、走线寄生电容(C)共同构成一个 RC充电电路 。当信号从低电平(0V)跳变到高电平(Vdd)时,电压是缓慢指数上升 的,而非垂直跳变。这个从低到高所需的时间就是 "上升时间(Rise Time)"。
- 采样风险 :如果STM32的I2C模块在总线电平还未稳定到逻辑高电平
VIH(通常为0.7×Vdd)之前就去采样数据,就会采样到一个中间电平 ,导致误判为逻辑0,从而产生通信错误。 - 协议要求 :I2C协议规范(如NXP的UM10204)明确规定了不同模式下允许的最大上升时间,这是为了保证所有设备都能在电平稳定后进行可靠采样。
TRISE 的计算公式与作用
STM32通过 TRISE 寄存器来满足协议的上升时间要求。其计算公式为:
T R I S E = t r ( m a s ) t P C L K 1 + 1 TRISE=\frac{t_{r(mas)}}{t_{PCLK1}}+1 TRISE=tPCLK1tr(mas)+1
- t r ( m a x ) t_{r(max)} tr(max):是I2C协议规定的最大上升时间。
- t P C L K 1 t_{PCLK1} tPCLK1:是APB1总线的时钟周期(例如,PCLK1=36MHz时,周期约为27.78ns)。
- +1:STM32硬件设计的余量因子,确保时间足够。
| 模式 | 最大上升时间 t r ( m a x ) t_{r(max)} tr(max) | 计算公式 (假设 PCLK1 = 36MHz) |
|---|---|---|
| 标准模式 (100kHz) | 1000 ns | TRISE=1000ns/27.78ns+1≈36+1=37 |
| 快速模式 (400kHz) | 300 ns | TRISE=300ns/27.78ns+1≈10.8+1=12 |
HAL库的自动处理 :当你调用 HAL_I2C_Init() 并设置 ClockSpeed 和 DutyCycle 时,HAL库会自动根据当前PCLK1的频率和所选模式,计算出正确的TRISE值并写入寄存器。你通常无需手动计算