• GD32 单片机 硬件I2C死锁解决方法


    死锁的复现方式

    • 在I2C恢复函数下个断点(检测到I2C多次超时之后,应该能跳转到I2C恢复函数)
    • 使用镊子,将SCL与SDA短接,很快就能看到程序停到恢复函数的断点上,此时再执行恢复函数,看能否正常走出(可在回复函数中写个死循环,只有I2C正常才跳出,检测I2C正常的办法,可以读从设备的ID)
      1. void HAL_I2C_MspInit(I2C_HandleTypeDef *i2cHandle)
      2. {
      3. GPIO_InitTypeDef GPIO_InitStruct = {0};
      4. if (i2cHandle->Instance == I2C1)
      5. {
      6. /* USER CODE BEGIN I2C1_MspInit 0 */
      7. /* USER CODE END I2C1_MspInit 0 */
      8. __HAL_RCC_GPIOB_CLK_ENABLE();
      9. GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
      10. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
      11. GPIO_InitStruct.Pull = GPIO_NOPULL;
      12. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
      13. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
      14. for (int i = 0; i < 10; ++i)
      15. {
      16. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
      17. HAL_Delay(1);
      18. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
      19. HAL_Delay(1);
      20. }
      21. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
      22. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
      23. HAL_Delay(1);
      24. i2cHandle->Instance->CR1 |= I2C_CR1_SWRST; //复位I2C控制器
      25. HAL_Delay(1);
      26. i2cHandle->Instance->CR1 = 0; //解除复位(不会自动清除)
      27. /**I2C1 GPIO Configuration
      28. PB6 ------> I2C1_SCL
      29. PB7 ------> I2C1_SDA
      30. */
      31. GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
      32. GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
      33. GPIO_InitStruct.Pull = GPIO_PULLUP;
      34. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      35. GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
      36. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
      37. /* I2C1 clock enable */
      38. __HAL_RCC_I2C1_CLK_ENABLE();
      39. /* USER CODE BEGIN I2C1_MspInit 1 */
      40. /* USER CODE END I2C1_MspInit 1 */
      41. }
      42. }
      43. /* USER CODE BEGIN 1 */
      44. void I2C_Reset()
      45. {
      46. HAL_I2C_MspDeInit(&hi2c1);
      47. hi2c1.State = HAL_I2C_STATE_RESET;
      48. MX_I2C1_Init();
      49. // 硬件i2c会出现死锁,当超时次数达到一定数量,即很有可能是发生了死锁
      50. // 所谓死锁是指主机与从机互相等待,主机以为总线在从机手上控制,从机以为总线在主机手上控制,一直再等待对方释放总线
      51. }

      死锁的解决方法

    1. 释放IO口为GPIO,复位句柄状态标志,改为IO方式
    2. 将SDA改为高电平 
    3. 将SCK发送9个时钟  为高电平时,测试SDA是否为低电平完成死锁。

    通过模拟几种情形来实际体会一下(从机对SDA的操作红色表示):

    如果在地址字节第9个CLK拉高后主机复位。在模拟的第一个时钟低电平期间就可以看到SDA的释放,随后主机先拉低SDA,再模拟一个STOP结束条件。

    在数据字节第2个CLK拉高后主机复位,在第二个模拟的时钟低电平期间才看到SDA释放


    在数据字节第6个CLK拉高后主机复位,在第三个模拟的时钟低电平期间才看到SDA释放

    通过以上三种情况的分析,想必你已经非常清楚改如何处理了,最后附上一个程序处理流程图

    SCL挂死

    I2C从机主动拉低SCL线在规范中是一个合法的行为,称之为Clock Stretching(时钟扩展,我一般叫他时钟同步)。通常是主机请求数据( 收或者发)后从机需要一些时间处理,且没有多余Buffer可以接收接或者提供接下来的数据的时候从机则会拉低SCL一段时间直到有新的数据准备好。

    SCL挂死(也就是前面所说一直拉低SCL)这种情况在标准I2C从器件上基本不会出现,因为只要芯片还在正常工作buffer总算有准备好的时候,自然就就释放SCL了。往往是使用用户使用MCU作为I2C从机时,程序设计上的问题导致MCU无法读取&填充buffer而导致,重点分析MCU I2C中断服务程序。

    1. I2C中断服务程序被意外屏蔽
    2. 中断服务程序中陷入了一些标志位查询的while(flag != xxx)死循环
    3. I2C功能系统被意外禁止
  • 相关阅读:
    力扣(LeetCode)60. 排列序列(C++)
    科学技术创新杂志科学技术创新杂志社科学技术创新编辑部2022年第24期目录
    【业务功能107】微服务-springcloud-springboot-Sentinel容器安装-熔断降级限流
    今天起,Windows可以一键召唤GPT-4了
    鼠标灵敏度怎么调?4个方法提高使用体验感!
    LeetCode 1704. 判断字符串的两半是否相似:小难懂的代码
    Mybatis实现分页查询
    vue3 中使用 jsx 开发(语法差异)
    Spring Boot 中是使用 JDK Proxy 动态代理还是 CGLib ?
    随手笔记(四十二)——关于Stack部分原理分析
  • 原文地址:https://blog.csdn.net/lvmingzhou/article/details/134194753