[应用相关] I2C 配置顺序引发的异常案例

[复制链接]
1471|12
 楼主| 我想看大海 发表于 2021-11-10 09:32 | 显示全部楼层 |阅读模式
前言
在参考 Cube 软件包中 I2C 例程后, 根据应用需要新增了一路 I2C 接口,结果新增 I2C 无法收发数据。 本文主要对问题
进行描述, 分析产生原因,提供解决方法。  

 楼主| 我想看大海 发表于 2021-11-10 09:33 | 显示全部楼层
一 问题描述
如前言所述,现象表现为 I2C 无法收发数据。 得无法理解之处,在于之前已经完成了 I2C 的移植工作,并且运行正常。
现在遵照正确的方式,新添一路 I2C 接口, 只是更改了对应的 I2C 接口及引脚,为什么无法收发数据。 简化测试程序如下。  

  1. GPIO_InitTypeDef GPIO_InitStruct;
  2. /* Enable GPIO TX/RX clock */
  3. I2Cx_SCL_GPIO_CLK_ENABLE();
  4. I2Cx_SDA_GPIO_CLK_ENABLE();
  5. /* Enable I2Cx clock */
  6. I2Cx_CLK_ENABLE();
  7. /*##-2- Configure peripheral GPIO ##########################################*/
  8. /* I2C TX GPIO pin configuration */
  9. GPIO_InitStruct.Pin = I2Cx_SDA_PIN;
  10. GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  11. GPIO_InitStruct.Pull = GPIO_PULLUP;
  12. GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  13. GPIO_InitStruct.Alternate = I2Cx_SCL_SDA_AF;
  14. HAL_GPIO_Init(I2Cx_SDA_GPIO_PORT, &GPIO_InitStruct);
  15. GPIO_InitStruct.Pin = I2Cx_SCL_PIN;
  16. HAL_GPIO_Init(I2Cx_SCL_GPIO_PORT, &GPIO_InitStruct);
  17. I2cHandle.Instance = I2Cx;
  18. I2cHandle.Init.ClockSpeed = 100000;
  19. I2cHandle.Init.DutyCycle = I2C_DUTYCYCLE;
  20. I2cHandle.Init.OwnAddress1 = 0;
  21. I2cHandle.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  22. I2cHandle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  23. I2cHandle.Init.OwnAddress2 = 0;
  24. I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  25. I2cHandle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  26. if(HAL_I2C_Init(&I2cHandle) != HAL_OK)
  27. {
  28. /* Initialization Error */
  29. Error_Handler();
  30. }
  31. while(1)
  32. {
  33. HAL_I2C_Master_Transmit(&I2cHandle, (uint16_t)I2C_ADDRESS, (uint8_t*)aTxBuffer,
  34. TXBUFFERSIZE, 10000);
  35. }


 楼主| 我想看大海 发表于 2021-11-10 09:33 | 显示全部楼层
二 现象分析
将上述程序在 STM32F469i-Disco 板上实现, 复现现象并寻找规律。发现规律如下:  

56214618b216e26718.png
 楼主| 我想看大海 发表于 2021-11-10 09:34 | 显示全部楼层
分析上表可以发现问题的产生与硬件设计有一定关系。 观察发现, 如果 I2C 没有外部上拉时,会导致问题产生。
通过单步调试,定位于 HAL_I2C_Master_Transmit(), 在这个函数中调用的 I2C_WaitOnFlagUntilTimeout()无法执行异
常,返回 HAL_BUSY,导致了 I2C 写功能失败。在 I2C_WaitOnFlagUntilTimeout 函数内部,是对忙标志位
BUSY@I2Cx_SR2 的检测。 通过对参考手册的阅读(如下截图所述) , 如果在未占用 I2C 总线时, SDA 或 SCL 引脚存在低
电平,则意味着总线处于忙状态。这种检测机制在 I2C 接口失能时依然工作。   1766618b2195e17b4.png

结合程序中调用顺序,在 I2C3 时钟使能时,虽然 I2C3 没有使能,但是忙状态检测已经开始。 由于对应的 SCL 引脚上无
上拉电阻, 并且由于还未对 I2C3 的 SCL 引脚进行配置。此时 SCL 引脚为浮空输入状态, 实际测量发现为低电平, BUSY 标
志被置位。


 楼主| 我想看大海 发表于 2021-11-10 09:35 | 显示全部楼层
三 解决方法
通过现象及分析, 可了解到问题可通过硬件或者软件解决。
硬件上, 为 SDA、 SCL 引脚提供外部的 I2C 上拉电阻, 问题不在出现。
软件上,能够发现对于 SCL、 SDA 引脚配置中,会启用内部上拉。 通过将 I2C 时钟使能放于 I2C 引脚配置语句后面, 问
题不再出现。
需要注意, I2C 的 SDA、 SCL 引脚内部上拉电阻, 为弱上拉。 使用者可以通过对应型号 STM32 的数据手册, 查看对应
引脚的上拉电阻,以便判断是否能够满足应用需要。 如下为 STM32F469 上拉电阻信息截图。  

87386618b21c22750d.png
sadicy 发表于 2021-11-11 09:57 | 显示全部楼层
就是需要提供上拉电阻的啊~
kiwis66 发表于 2021-11-13 13:39 | 显示全部楼层
上拉电阻,对I2C来说,不是标准电路设计么?
木木guainv 发表于 2021-12-6 14:01 | 显示全部楼层
极少用这个iic
wakayi 发表于 2021-12-6 14:02 | 显示全部楼层
要是cube默认的配置顺序呢
wowu 发表于 2021-12-6 14:03 | 显示全部楼层
有和楼上一样的感觉
xiaoqizi 发表于 2021-12-6 14:06 | 显示全部楼层
这样的现象普遍吗
tpgf 发表于 2021-12-6 14:08 | 显示全部楼层
看来是个案啊
磨砂 发表于 2021-12-6 14:20 | 显示全部楼层
上拉电阻是必须的吧
您需要登录后才可以回帖 登录 | 注册

本版积分规则

36

主题

282

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部