硬汉嵌入式论坛

 找回密码
 立即注册
查看: 159|回复: 6
收起左侧

[技术讨论] st 的 I2C 问题

[复制链接]

32

主题

329

回帖

425

积分

高级会员

积分
425
QQ
发表于 前天 17:30 | 显示全部楼层 |阅读模式
我在初学者阶段对硬汉大佬的示例程序,使用硬件模拟 I2C 实现功能产生过疑惑。
后来实际上我从未硬件模拟过 I2C 通信,就不加太多前提条件了吧,反正我都是用的外设没出过问题。


今天刷知乎看到一篇“雄文”感觉很有道理,和坛友们交流一下,因为我实在经验浅薄,不知道坛友有没有遇到在其他情况外设 I2C 不可用的案例。
STM32 的 I2C 为什么会有 bug? - 知乎 pvfcd

反正核心提要是“只有 stm32f103 单片机有这个 I2C bug,且在官方勘误表中有明确说明"。


下面直接抄袭原作者的原文
大部分人说的bug应该指的是STM32F103单片机 IIC总线在运行中卡死这个问题。 具体的来说,是iic寄存器的busy位被置位,且无法通过正常手段被清零。 很遗憾,F103的iic确实有这个bug,而且st官方似乎也不知道什么情况下会触发这个bug。原因出在iic寄存器的模拟滤波器上:I2C 引脚内部集成的模拟滤波器可能会被锁死在低电平,而此时外部 SCL、SDA 线实际处于高电平状态。当外部iic引脚电平跳变即可消除此错误锁死状态,但是iic外设又需要iic主机发送起始位,才能发送信号。于是就引发了busy位被置位,总线卡死。
这个bug被记录在了勘误表中,而且,极其罕见的用了“这个故障会随机触发”(This issue occurs randomly)这个十分不严谨的表述。
抽象的是:触发这个bug后,需要极度繁琐的15步操作,才可以正确的复位此bug。(勘误表中其他bug基本六七步即可解决)简单说就是需要把iic对应的引脚重新配置成开漏输出模式,切换一次高低电平状态,然后读取是否正确输入了高电平,之后再复位iic外设即可。
官方勘误表中同样也明确说了,必须按照上述流程执行。
而网上所有号称iic无bug,或者说此bug可复位的人,竟然从来没有任何一个人完整的描述了这“正确的”步骤!
而更抽象的是:有些从业者,ai时代前他根本配置不明白硬件外设,只会软件实现。而那些认真的配置硬件寄存器的工程师却在这里卡住了,反而让技术力不行的人瞎猫撞上了死耗子。
真乃“程序靠碰运气执行”啊

以下是勘误表原文:
屏幕截图 2026-06-30 182311.png
ai翻译文本:
2.9.7 I2C模拟滤波器可能输出错误值,锁定BUSY标志并阻止进入主机模式
描述
I2C 引脚内部集成的模拟滤波器可能被锁死在低电平,而此时 SCL、SDA 总线实际处于高电平状态。该问题可能在 MCU 上电复位后,或是芯片遭受静电放电(ESD)冲击时出现。
这会导致 I2C 的 BUSY(忙)标志被置位,I2C 无法进入主机模式(无法产生起始条件)。
I2C 的 BUSY 标志既无法通过 SWRST 控制位清零,也无法通过外设复位或系统复位清除:复位过程中 BUSY 位会被临时清零,但复位解除后会立刻重新置高,因为模拟滤波器的输出仍停留在低电平。该问题为随机发生。
注意
在相同条件下,I2C 模拟滤波器也可能出现反向情况:SCL 和 SDA 总线保持低电平时,滤波器输出高电平。这种情况不会引发故障,因为下一次 SCL、SDA 电平跳变后,滤波器输出就会恢复正确。
规避方案
SCL 和 SDA 的模拟滤波器输出,会分别在对应引脚发生电平跳变后更新。
可通过软件将 I2C 对应的 GPIO 配置为输出模式,强制 SCL、SDA 产生电平跳变。当模拟滤波器解除锁定、输出与引脚实际电平一致后,就能通过软件复位清零 BUSY 标志,I2C 也就能正常进入主机模式。
必须按以下步骤执行:
1. 清零 I2Cx_CR1 寄存器的 PE 位,禁用 I2C 外设。
2. 将 SCL、SDA 对应的 GPIO 配置为通用开漏输出模式,输出高电平(向 GPIOx_ODR 对应位写 1)。
3. 读取 GPIOx_IDR 寄存器,确认 SCL、SDA 引脚为高电平。
4. 将 SDA 对应的 GPIO 配置为通用开漏输出模式,输出低电平(向 GPIOx_ODR 对应位写 0)。
5. 读取 GPIOx_IDR 寄存器,确认 SDA 引脚为低电平。
6. 将 SCL 对应的 GPIO 配置为通用开漏输出模式,输出低电平(向 GPIOx_ODR 对应位写 0)。
7. 读取 GPIOx_IDR 寄存器,确认 SCL 引脚为低电平。
8. 将 SCL 对应的 GPIO 配置为通用开漏输出模式,输出高电平(向 GPIOx_ODR 对应位写 1)。
9. 读取 GPIOx_IDR 寄存器,确认 SCL 引脚为高电平。
10. 将 SDA 对应的 GPIO 配置为通用开漏输出模式,输出高电平(向 GPIOx_ODR 对应位写 1)。
11. 读取 GPIOx_IDR 寄存器,确认 SDA 引脚为高电平。
12. 将 SCL、SDA 对应的 GPIO 切换为复用开漏功能模式。
13. 置位 I2Cx_CR1 寄存器的 SWRST 位。
14. 清零 I2Cx_CR1 寄存器的 SWRST 位。
15. 置位 I2Cx_CR1 寄存器的 PE 位,重新使能 I2C 外设。


回复

使用道具 举报

32

主题

466

回帖

562

积分

金牌会员

积分
562
发表于 昨天 08:59 | 显示全部楼层
本帖最后由 tovinz 于 2026-7-1 09:00 编辑

我用过 at32,ch32 的,和 st 的 i2c 控制器差不多,寄存器布局基本上一样。
我用着没什么问题,oled 和 at24cxx 通过 1MHz 访问无压力。
确实会有锁死的问题,解决办法就是 发现 busy 超时直接把两个io配置成推挽输出拉高一下强制释放总线就行了。
回复

使用道具 举报

32

主题

329

回帖

425

积分

高级会员

积分
425
QQ
 楼主| 发表于 昨天 09:53 | 显示全部楼层
tovinz 发表于 2026-7-1 08:59
我用过 at32,ch32 的,和 st 的 i2c 控制器差不多,寄存器布局基本上一样。
我用着没什么问题,oled 和 a ...

佬,具体是啥型号呢?stm32f103 替换吗?还是说和原文说的不一样,其他型号 I2C 外设也有问题。

因为我只在 st32H7 gd32H7 用过 I2C,而且只操作 eeprom ,没遇到过问题所以甚至没有错误处理。就是说我这种情况要不要加 BUSY 锁死的检测和恢复呢?
回复

使用道具 举报

10

主题

116

回帖

151

积分

初级会员

积分
151
发表于 昨天 10:20 | 显示全部楼层
我在v7开发板上用的也是硬件i2c,感觉没啥问题啊
回复

使用道具 举报

3

主题

41

回帖

50

积分

初级会员

积分
50
发表于 昨天 11:02 | 显示全部楼层
硬件是功能 软件才是模拟吧
回复

使用道具 举报

32

主题

466

回帖

562

积分

金牌会员

积分
562
发表于 昨天 15:34 | 显示全部楼层
yono 发表于 2026-7-1 09:53
佬,具体是啥型号呢?stm32f103 替换吗?还是说和原文说的不一样,其他型号 I2C 外设也有问题。

因为 ...

当时我在测试我自己编写的 at32f403a 硬件 i2c 驱动
我在 i2c 初始化之后,会有一个从 0x00 ~ 0xFF 地址的扫描过程,查看挂载了多少设备
我测试在扫描过程中按下单片机复位按钮,多次测试,发现时不时存在总线锁死的情况:
    硬件 i2c 主机控制器卡死在 busy,
    软件复位 i2c 外设也无法退出 busy,
    通过复位按键复位整个单片机也无法退出 busy。
    发现只有硬件上完全断开 i2c 设备才会退出卡死状态。
我就在网上找解决办法,测试发现当总线 busy 超时的时候把sda和scl设置为推完输出并且拉高之后,就能解锁。

今天我移植到 stm32f103 上发现也存在这个现象。

我仔细看过stm32f1、ch32h417、ch583、at32f403a 的手册,可以说这几个国产使用的 i2c 控制器和 st 的是一样的。
回复

使用道具 举报

2

主题

26

回帖

32

积分

新手上路

积分
32
发表于 昨天 19:28 | 显示全部楼层
实际是st的IP设计bug,初代版本会存在这个随机的问题,具体原因估计只有查看RTL代码才能知道问题,国产的IIC有些替代型号实际是使用了stm32f4系列后面这些优化后的版本的,优化后的版本基本不太会有这个问题,说白了就是IP设计bug有可能通过外部脉冲干扰导致SCL和SDA死锁进入busy状态。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2026-7-2 05:42 , Processed in 1.622157 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表