关于STM32的I2C接口死锁在BUSY状态无法恢复的现象,网上已有很多讨论,看早几年比较老的贴子,有人提到复位MCU也无法恢复、只有断电才行的状况,那可是相当严重的问题。类似复位也无法恢复的情况是存在的,技术支持矢口否认问题存在,并不是正确面对问题的态度。比如我用这款F439芯片的SDRAM控制器,在错误操作后进入HardFault状态,复位无法恢复,JTAG也无法联机,只能断电重来,官方的Erratasheet里也提到了。
如果I2C接口无法可靠工作,那么所做的设计将存在严重隐患,不可能要求用户用断电的方法恢复系统。如果像某些网友提到弃用硬件I2C,转为GPIO模拟I2C时序,那么首先I2C时钟频率不易确定,因为STM32的时钟频率可以动态调节;此外不用硬件I2C,无法用中断、DMA等高级模式,会严重降低ARM内核效率。所以务须确认和解决这个问题。

一.问题存在
我用STM32F439IGT,为了确定问题存在,让I2C控制器作Master,先人为产生I2C总线故障。产生I2C总线故障的方法简单而粗暴:在I2C总线工作过程中,用镊子把SCL和SDA两个信号短路一下,很容易进入BUSY死锁状态。长时间短路也可能产生超时。HAL_I2C_Init()、HAL_I2C_Master_Transmit()、HAL_I2C_Master_Receive()等函数返回值分别为HAL_BUSY(0x02)、HAL_TIMEOUT(0x03)。
试着用MCU复位,是可以恢复的,说明硬件没死穴。又测试不用MCU复位,而是在程序中依次调用STM32Cube_FW_F4_V1.5.0固件库提供的如下两个初始化函数:HAL_I2C_DeInit(&hi2c1)、HAL_I2C_Init(&hi2c1),并不能保证一定恢复正常。
BUSY死锁时,用万用表测试I2C信号电压,SCL、SDA均为低电平。如果调用函数:HAL_I2C_DeInit(&hi2c1),会函数释放IO口回到GPIO的默认状态(Input),此时再测SCL、SDA电压,均为高电平。这说明总线是被MCU这边的Master拉低的,而不是被Slave拉低的。当然也存在Slave刚好输出低电平拉低SDA的可能。

二.出错代码位置跟踪
单步运行,可以看到进入stm32f4xx_hal_i2c.c程序中I2C读写函数不远处(如图阴影所在行),读BUSY位,总会得到SET的结果,无法继续执行后续程序而返回。

三.参考文献
读了网上很多解决方案,其中比较有启发意义的有这几篇:
1. 百度文库,这个好像是ST官方客服提供的,关于死锁的可能机理和解决方案做了说明:
http://wenku.baidu.com/link?url=KB9p-TYrQcmVu1azHG66BXAcG6Pe6Bm2kWF_9ERSU35EOA8obiTVTDrZ6fZ3IOjfVAb71RCvJIiAODo4p4Sr0fUPDy0kQyyqWWJgxjfYHzO
2. STM社区,这个提到了初始化I2C引脚前应该先置为OUT及高电平。这在上电初始化时无虞,因为MCU复位后IO口为输入,并由外部上拉电阻拉为高电平。但在做故障恢复时很重要,因为此时IO口可能正被Master或Slave拉成低电平。 http://www.stmcu.org/module/forum/thread-518463-1-1.html
3. 这个解决方案和上面思想两个相仿,但是写了太多代码,又有放置位置的要求,看起来头大。仅作参考:http://bbs.ednchina.com/BLOG_ARTICLE_2154168.HTM
4. 最重要的说明,在ST官方提供的STM32F4xx用户指南:RM0090 Reference manual Rev9,第845页,关于I2C_CR1,SWRST位的Note,提到解决BUSY死锁问题:
 
意思是说SWRST位可以在出错或死锁时,用于复位I2C控制器,例如众所周知的BUSY位问题。我没有看其它老STM型号的手册,至少STM32F4xx有SWRST位,STM32L0xx用户指南提到可以用PE位复位。

四.问题的解决方案
按照ST手册的提示,经过各种尝试,本着尽量少改动代码、尽量不改动固件库里只读文件的原则,我的解决方案如下所述。假设主程序里有如下的代码,返回值ret不等于0表示出错,按stm32f4xx_hal_def.h头文件中的错误代码定义,返回值为0x02是HAL_BUSY,0x03是HAL_TIMEOUT,这两个返回值都可能得到。下面程序里红色的两行是错误处理必须的:

4.1 主程序改动,加错误处理代码2行:
unsigned char ret = Sensor_ReadData(uint8* buf);   // I2C读写函数
    if (ret != 0)  {                   //I2C故障处理
      HAL_I2C_DeInit(&hi2c1);        //释放IO口为GPIO,复位句柄状态标志
      HAL_I2C_Init(&hi2c1);          //这句重新初始化I2C控制器
    }
    else  {
      // 。。。。I2C无错误时的正常程序
    }

4.2 子程序的改动,加7行代码:
上面HAL_I2C_Init(&hi2c1)函数会调用HAL_I2C_MspInit(hi2c)函数,这个函数在stm32f4xx_hal_msp.c文件中实现,主要是初始化IO口以及外设,由STM32CubeMX工具生成或用户自行编写,非只读文件。以下节选该函数第一段,其中I2C端口用哪个pin,是由用户自己设定的,我这里用的PB6、PB7。红、绿底色的几行是为了处理BUSY死锁问题专门插入的。

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  if (hi2c->Instance==I2C1)
  {
    __I2C1_CLK_ENABLE();
    // PB6    ---->   I2C1_SCL
    // PB7    ---->   I2C1_SDA
    // strong pull-up high to recover from locking in BUSY state
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;      //此行原有
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;   //GPIO配置为输出
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;         //强上拉
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

HAL_GPIO_WritePin(GPIOB, 6, GPIO_PIN_SET);       //拉高SCL
    HAL_GPIO_WritePin(GPIOB, 7, GPIO_PIN_SET);       //拉高SDA

hi2c->Instance->CR1 = I2C_CR1_SWRST;          //复位I2C控制器
    hi2c->Instance->CR1 = 0;              //解除复位(不会自动清除)
// 以下是原有代码
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
//。。。
}

上面程序中,把I2C端口配置成GPIO-OUTPUT,并强制拉高,是必需的。注意到手册里关于SWRST位说明的第一句:“When set, the I2C is under reset state. Before resetting this bit, make sure the I2C lines are released and the bus is free.” 意思就是置位SWRST,会使I2C控制器保持在复位状态。解除复位前,确保I2C总线已经释放到空闲状态,即SCL、SDA均为高电平,再恢复I2C控制器。所以解除复位是用户来做的,硬件不会自动清除该位。

五.结论
我用这款STM32F439IGT单片机,I2C部分没有出现断电才能解除BUSY死锁的严重问题,看来STM已经意识到这个硬BUG,并在后期产品里逐步进行了改进。
在没有硬件死穴的情况下,我这里仅增加10行程序,就可以用软件恢复故障。多次尝试,触发I2C故障时,一次就可以恢复,无需加延时等语句,也未改动现有固件库代码。

以上部分内容参考了别人的博客,以下为我的解决方式,测试有效:

我是将scl SDA配置为推挽输出,将两个管脚置高后关闭IIC,然后再重新初始化IIC达到复位IIC总线的目的,修复总线死锁问题。

解决STM32 硬件IIC死锁在BUSY状态的方法讨论相关推荐

  1. 使用STM32+硬件IIC+DMA驱动GT系列触摸屏(GT911)

    使用STM32+硬件IIC+DMA驱动GT系列触摸屏(GT911) 初始化代码 /** @brief GT911 初始化程序* @param None* @retval None*/ void GT9 ...

  2. 12. STM32——硬件IIC驱动OLED屏幕显示

    STM32--硬件IIC驱动OLED屏幕显示 OLED屏幕 OLED屏幕特点 OLED屏幕接线说明 OLED屏幕显存 OLED屏幕原理 OLED屏幕常用指令 OLED屏幕字模软件的使用 写命令 写数据 ...

  3. STM32硬件IIC的BUG问题

    问题描述 大概是这样,使用STM32硬件IIC作为主机通讯,只要不发送错误的从机地址, 通讯一直都是没问题的,因为验证程序的关系,所以在程序中修改了一下从机地址, 这时候就出现了问题,下面直接看图把 ...

  4. stm32 硬件IIC使用方法说明与示例---LIS3DH的IIC通信

    有时我们可能需要MCU进行速率较高的IIC总线通信,一般stm32的IIC默认传输速率是100kpbs,最大为400kpbs.现在大部分项目会使用程序IO模拟的IIC,使用方便,具体网上例子很多. 这 ...

  5. 基于stm32硬件IIC的oled显示

    一 stm32硬件IIC 硬件IIC特性架构 ①通讯引脚 查看对应开发板芯片的原理图可以查看对应IIC外设引脚的位置 这里代码采用的是stm32f103c8t6,硬件IIC的外设引脚为PB6 PB7, ...

  6. STM32硬件IIC读写EEPROM

    前面一篇写了软件模拟IIC读写EEPROM. 本篇介绍硬件IIC读写EEPROM.平台是STM32F103+AT24C04N.SDA和SCL接5K上拉电阻到3.3v. 首先介绍AT24C04N的基本特 ...

  7. STM32硬件IIC 主从(一、CubeMx快速实现)

    目的 通过HAL库实现STM32F103C8T6两块板之间的IIC主从相互通信,串口打印接收数据. 环境搭建 /**I2C1 GPIO Configuration PB6 ------> I2C ...

  8. stm32硬件SPI驱动3线SPI-LCD的方法

    1.基本控制原理 三线SPI LCD, 顾名思义,最少只需要3个IO控制LCD显示,如果采用硬件控制上电时序和背光,最少只需要接SCK,CS,MOSI三个引脚即可控制LCD,并且不管接不接其他引脚,控 ...

  9. STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、图像(硬件IIC)

    参考:基于STM32-Oled(IIC)的使用 作者:奋斗的小殷 发布时间: 2021-05-07 13:09:26 网址:https://blog.csdn.net/boybs/article/de ...

最新文章

  1. 如何区分“Invoice代码”和“Invoice号码”?
  2. linux基本项目环境搭建
  3. Linux 下搭建 php 开发环境完整教程
  4. 面试官问:JS的继承
  5. socket 编程入门教程(一)TCP server 端:2、socket与文件描述符
  6. *【牛客 - 301哈尔滨理工大学软件与微电子学院第八届程序设计竞赛同步赛(高年级)】小乐乐打游戏(bfs,双元bfs,思维)
  7. 完成css的切图 图片任意,css切图是什么意思
  8. 观察者模式,从公众号群发说起
  9. Resx 文件无效。未能加载 .RESX 文件中使用的类型 System.Collections.Generic.List`1请确保已在项目中添加了必需的引用。
  10. 启动白屏处理_App启动优化一顿操作猛如虎
  11. Html5简单描述(优点与缺点)
  12. PAIP.自定义SELECT BOX COMMBO BOX展现.txt
  13. java c3p0 jar包_c3p0 jar包下载-c3p0-0.9.1.2.jar包下载 --pc6下载站
  14. 开源BI工具对比(三) DataEase
  15. Black-Scholes期权定价公式
  16. 关于数字万用表你需要知道的知识
  17. 转:著名的100个管理定律点评8 - 竞争决胜的智慧与策略
  18. 计算机的cup颗数、核数、线程数
  19. 2023 抖音表情包小程序变现项目 详细玩法视频课程
  20. IOS 蓝牙相关-BabyBluetooth蓝牙库介绍(4)

热门文章

  1. mac Os升级系统
  2. PHP:Maze迷宫寻址算法(附完整源码)
  3. PHP microtime 返回当前 Unix 时间戳和微秒数
  4. 周界报警系统服务器,周界报警系统
  5. 【区块链】读懂拜占庭将军问题
  6. c语言内存越界例子,内存越界的可能情况分析,C语言内存越界详解
  7. Collection集合
  8. 【C语言】小学数学练习
  9. 如何优雅地弄好PCB丝印
  10. Codeforces Round #776 (Div. 3)-D. Twist the Permutation