关于STM32 HAL库 (I2C/IIC)问题的解决方法
1.情况介绍:
因为某个项目想要颜色识别,去识别球的颜色,但是又不想多来个摄像头,所以想尝试一下颜色传感器的方案,简化流程。然后在淘宝上买了个TCS34725来试试,随后就遇到了问题:HAL库 I2C通信了几次就死锁了 。我之前也接触过I2C,OLED屏,tft屏,我那时候是通过GPIO口模拟I2C(因为淘宝的和网上的例程大多是这个)来实现的。还有就是九轴陀螺仪的磁力计数据读取,ist8310的例程是HAL库的,但是我那时候的问题是我用我的cubeMX去修改配置,更新之后,I2C就死锁了,但是那时候也没有太过深入,因为没有使用九轴陀螺仪的必要,就改成了六轴陀螺仪,间接没有使用I2C了。现在这个问题又给我撞上了,正好解决一下当年没有完成的事情。
以下是我环境配置:
【STM32cubeMX Vision】:6.0.1
【FreeRTOS Vision】 :CMSIS_1
是的,我是用FreeRTOS跑的。
具体的情况是:我移植了例程之后,开始跑调试看数据
数据开头全零,意料之中。
将断点打到里面去,是能执行的,就是卡在了HAL_I2C_Master_Transmit返回值有问题上。一路追查下去,卡在了if (I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK),再追下去卡在了if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, Timeout, Tickstart) != HAL_OK)。超时了!看了一眼官方注释:Wait until ADDR flag is set。再追下去,__HAL_I2C_GET_FLAG(I2C_FLAG_ADDR)和 __HAL_I2C_GET_FLAG(I2C_FLAG_AF)
属于是这两个标志位没有到位报错。
参考:I2C_GetFlagStatus()函数返回值说明,IIC的标志位-CSDN博客
I2C_FLAG_ADDR:表示I2C地址发送完成的标志位。
- SET:表示I2C地址发送完成。
- RESET:表示I2C地址未发送完成。
I2C_FLAG_AF:表示I2C Acknowledge失败的标志位。
- SET:表示I2C Acknowledge失败。
- RESET:表示I2C Acknowledge成功。
额额额~,没有发送,也没有应答。这就开始难办了。
结构体里面的ErrorCode 也是这个问题。
没办法,只能先求助网上了。
********************************************************************************************************
2.网上问题描述集合
1. HAL_I2C_Master_Transmit 或者 HAL_I2C_Master_Receive 一直返回 BUSY 或 TIMEOUT
参考:STM32 I2C 死锁问题_if (i2c_waitonflaguntiltimeout(hi2c, i2c_flag_busy-CSDN博客
他这一篇已经比较完备的介绍了问题,提出了几个解决方法:
1.当I2C报错时,对其写入一个stop信号,将管脚配置为普通输出管脚后,实现电平的反转,以达到解除死锁,再将其恢复为 I2C 配置。这个方法是很简单的,我也试过了这个方法,问题依旧,然后就发现一直在给我重置,我就知道我的 I2C 一直在超时,一直在报错了,对我遇到的问题没有作用。这个方法仅适用于间接性报错,如果是有明确错误原因的不适用,不然会一直重置。
2.他发现当出现 TIMEOUT 或 ERROR 时,STM32 Master 并不会产生 STOP 信号。这应该是老版cubeMX的遗漏了,我V6.0.1 问题就被修补了,SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);添加了停止位。因为这篇文章已经是18年的老文章了,有些情况已经不适用了。
/* Wait until TXE flag is set */
if (I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
}
return HAL_ERROR;
}
***********************************************************************************************************
2.I2C_WaitOnFlagUntilTimeout return BUSY,运气好的话只有第一次成功访问,但后续将无法读取数据
参考:STM32 HAL 库 I2C_WaitOnFlagUntilTimeout BUSY 解决办法-CSDN博客
解决STM32 I2C接口死锁在BUSY状态的方法讨论_stm32 i2c总线复位-CSDN博客
他的解决方法:
1.外接上拉电阻,稳定信号,确保信号到位。我看了一下我手头上的板子,确实没有上拉电阻,但是手头上没有4K7,就没试了。
2.STM32IIC引脚 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP,改为推挽输出。这个就有意思了,这里把开漏输出改为推挽输出,他的考量是什么我不得而知。我们先来看一下,开漏输出有一个明显的优势就是可以很方便的调节输出的电平,因为输出电平完全由上拉电阻连接的电源电平决定。所以在需要进行电平转换的地方,非常适合使用开漏输出。而推挽输出拥有驱动大负载的能力。事实上 I2C 并不需要拉什么大负载,I2C 更加适合用开漏输出,而有人更是提到“线与”,我倒是觉得,“线与”对于 I2C 来说没什么影响。还有我的cubeMX好像根本不让我有选择推挽输出的可能【笑哭】。
3.通过将 CR1 的 SWRST 置‘1’然后清‘0'来复位 I2C 接口,以达到清除 Busy 标志回到空闲状态目的。这种操作是建立在 I2C 在发生总线错误之后,自己会发生一个 stop 信号并重新初始化。
***********************************************************************************************************
3.I2C_WaitOnTXISFlagUntilTimeout返回HAL_ERROR
参考:I2C_WaitOnTXISFlagUntilTimeout返回HAL_ERROR,无法与传感器通信怎么解决?
这个是比较经典的传输地址出错导致无应答超时。大家一定要检查设备地址与硬件接线。
*********************************************************************************************************
4.也是busy的错误
参考:STM32 IIC问题_while (__hal_i2c_get_flag(hi2c, flag) == status)-CSDN博客
这里的方法是,先GPIO时钟使能,再I2C时钟使能,然后才配置IIC。其实还有其他更直观的文章的,找不到了,给大家截个图看吧,我记得有几篇文章的,但是顺序好像都不一样。
***********************************************************************************************************
5.使用FreeRTOS 并使用HAL库函数,卡奇怪错误的
参考:解决STM32 I2C接口死锁在BUSY状态的另一种原因和方法_i2c时钟使能后busy-CSDN博客
这里提到了一嘴FreeRTOS 对 HAL I2C库函数的影响,但是我按照他的方法来还是解决不了就先暂时没管了。到了后面我才知道,真的是FreeRTOS搞的鬼!!还是下面这位老哥的文章点醒我的,HAL_I2C_Master_Transmit本身是在一个task中调用的,而这个延迟的判定是通过while循环轮询的。I2C的标志位是用while来判定的,并且没有任何释放操作,在SPI,SDMMC等函数中涉及到标志位的一些判定基本都是一个死循环,像I2C的超时时间预设的还不是特别长,我印象里有的函数的超时时间预设的是0xFFFFFFF,相当于死等,进去只要错过了标志位那么系统必然卡死,所以用的时候还是要多注意一下具体库的内部细节,否则这种问题查起来真的是要命。
参考:STM32 I2C 在FreeRTOS中造成的死锁 - elmagnifico's blog
***********************************************************************************************************
6.还有ST论坛上,Github和其他上的一些文章,感兴趣可以看看:
已解决:I2C_WaitOnFlagUntilTimeout返回HAL_ERROR - STMicroelectronics 社区
关于 HAL_I2C_ERROR_AF :
Solved: I2C always falls into HAL_I2C_ERROR_AF - STMicroelectronics Community
*********************************************************************************************************
7.建议对 I2C 没有太高的性能要求的朋友换成模拟 I2C
其实网上有很多模拟 I2C 了,这其实只需要通过了解一点 I2C 的工作原理就能够写出来。已知硬件 I2C 存在很多问题(HAL库),如果对 I2C 没有太高的性能要求的朋友可以换成模拟 I2C ,因为这会友好很多,网上也有很多可以参考:
STM32 HAL I2C(IIC)通信的序列传输(restart condition)_hal_i2c_slave_seq_receive_it
【STM32F4系列】【HAL库】【自制库】模拟IIC主机_hal库模拟iic-CSDN博客
基于STM32的I2C协议,STM32CubeMX生成_stm32 i2c cube-CSDN博客
虽然模拟 I2C 的性能 比 硬件 I2C 会差,比如说接口与稳定性,传输方式等。首先I2C时钟频率不易确定,因为STM32的时钟频率可以动态调节;此外不用硬件I2C,无法用中断、DMA等高级模式,会严重降低ARM内核效率。但是硬件 I2C 确实很多问题。
*********************************************************************************************************
3.结语
这是我新建的工程,没有加 FreeRTOS ,直接能跑能读取,正常得不行。想要在 FreeRTOS 上跑硬件 I2C 还是任重而道远啊。继续尝试加delay解决。
希望以上内容对你有帮助,有什么不对的地方还请多多指点。