Bootstrap

STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法

STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法

原文链接:https://blog.csdn.net/PP_hui/article/details/112229696

    在调试多用户表的时候,发现如果人为短接I2C的SDA或SLK脚后,I2C的SR2的Busy标志将会置1,并且试了很多种办法也无法清除该标志位,只能复位芯片后I2C才能恢复正常。
    导致这个问题的原因是STM32芯片的硬件I2C接口是支持多个主设备同时使用的,STM32的I2C接口会一直检查SDA和SLK的状态,当出现非自己发出的电平变化等情况后,STM32芯片则判定为是有其它I2C的主在操作总线,这样STM32的Busy(总线忙标志)则会置位,只有在检查到一次I2C协议的停止位后才会硬件清除该标志,如下说明:

       如果Busy标志为1,则STM32的硬件I2C将不会发送数据,也就导致了I2C异常。

    芯片手册上有说明CR1的SWRST(软件复位I2C)可以清除Busy标志,如下图:

但是经过测试发现并不能解决这个问题,虽然能够复位I2C的所有寄存器,但是在读一次任何寄存器后所有寄存器的值又会恢复原来的样子,Busy标志也没有清除:

设置SWRST后:,任意操作后:

    在网上搜索了很久最终还是解决了这个问题:

链接《https://blog.csdn.net/jatamatadada/article/details/40860619》

经过代码修改后,处理逻辑为:

a.在操作I2C前,检测SR2的Busy是否为1,如下:

//检查是否有Busy标志及配置是否正确

    if(READ_BIT(hi2c1.Instance->SR2,I2C_SR2_BUSY) || READ_BIT(hi2c1.Instance->CR1,I2C_CR1_PE) == 0){

             I2C_Busy_C();

             return 0;

         }

b.I2C_Busy_C()函数内容:

//===================================================

//清除总线忙标志

//Xcp:2021/1/5

void I2C_Busy_C(void)

{

    SET_BIT(hi2c1.Instance->CR1,I2C_CR1_SWRST);          

    CLEAR_BIT(hi2c1.Instance->CR1,I2C_CR1_SWRST);

    I2Cx_MspInit(&hi2c1);                      //自定义函数

    HAL_I2C_Init(&hi2c1);

}

c. I2Cx_MspInit()函数内容:

//---------------------------------------------------------

//复位I2c所有参数

static void I2Cx_MspInit(I2C_HandleTypeDef *hi2c)

{

  GPIO_InitTypeDef  GPIO_InitStruct;

  if (hi2c->Instance == I2C1)

  {

    /* Configure the GPIOs ---------------------------------------------------*/

    /* Enable GPIO clock */

             __I2C1_CLK_ENABLE();

    /* Configure I2C Tx as alternate function  */

    GPIO_InitStruct.Pin       = GPIO_PIN_6;

    GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;

    GPIO_InitStruct.Pull      = GPIO_NOPULL;

    GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;

    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Configure I2C Rx as alternate function  */

    GPIO_InitStruct.Pin = GPIO_PIN_7;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Configure the Discovery I2Cx peripheral -------------------------------*/

    /* Enable I2C1 clock */

    __I2C1_FORCE_RESET();                                   //通过总复位模块对I2C一次复位操作

      __I2C1_RELEASE_RESET();

   }

}

以上代码中,和库函数HAL_I2C_MspDeInit()不同的是增加了

__I2C1_FORCE_RESET();                                 //通过总复位模块对I2C一次复位操作

__I2C1_RELEASE_RESET();

这两句代码,它是通过系统的总复位对I2C进线一次复位操作,这样就能够清掉Busy的标志。
————————————————
版权声明:本文为CSDN博主「PP_hui」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/PP_hui/article/details/112229696

;