大部分人说的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时代前他根本配置不明白硬件外设,只会软件实现。而那些认真的配置硬件寄存器的工程师却在这里卡住了,反而让技术力不行的人瞎猫撞上了死耗子。
真乃“程序靠碰运气执行”啊
以下是勘误表原文:
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 外设。