• 浅谈STM32L071硬件I2C挂死


    STM32的IIC问题一直存在,在网上也被很多人吐槽,然而FAE告诉我,硬件IIC的问题在F1,F3,F4系列单片机存在,而在L0上已经解决了,然而这几天调试加密芯片和显示芯片,都是IIC芯片,却再一次证明,L0系列的IIC一样存在问题,

    FAE告诉我是我的配置问题,故写这篇文章,如果是我配置问题,希望得到高人指点,再次感激不尽。

    配置代码:

    void MX_I2C1_Init(void)
    {
    
      hi2c1.Instance = I2C1;
      hi2c1.Init.Timing = 0x00707CBB;
      hi2c1.Init.OwnAddress1 = 0;
      hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
      hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
      hi2c1.Init.OwnAddress2 = 0;
      hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
      hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
      hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
      if (HAL_I2C_Init(&hi2c1) != HAL_OK)
      {
        _Error_Handler(__FILE__, __LINE__);
      }
    
        /**Configure Analogue filter 
        */
      if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
      {
        _Error_Handler(__FILE__, __LINE__);
      }
    
        /**Configure Digital filter 
        */
      if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
      {
        _Error_Handler(__FILE__, __LINE__);
      }
    
    }

    void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
    {
      unsigned char i;
      
      GPIO_InitTypeDef GPIO_InitStruct;
      if(i2cHandle->Instance==I2C1)
      {
          __HAL_RCC_I2C1_CLK_ENABLE();
          __HAL_RCC_GPIOB_CLK_ENABLE();
          
          /*I2C1 GPIO Configuration    
              PB8     ------> I2C1_SCL
              PB9     ------> I2C1_SDA 
           */
          GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin;
          GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
          GPIO_InitStruct.Pull = GPIO_PULLUP;
          GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
          GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
          HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
      }
    }
    
    void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
    {
    
      if(i2cHandle->Instance==I2C1)
      {
          __HAL_RCC_I2C1_CLK_DISABLE();
          /*I2C1 GPIO Configuration    
              PB8     ------> I2C1_SCL
              PB9     ------> I2C1_SDA 
            */
          HAL_GPIO_DeInit(GPIOB, I2C_SCL_Pin|I2C_SDA_Pin);
      }
    } 

    现象:程序调用HAL_I2C_Master_Transmit(),如果代码中有其它中断,就很容易传输失败:

    status = HAL_I2C_Master_Transmit(&hi2c1, SLAVE_IIC_ADDR, i2c_data, 2, 2)
    error_status = HAL_I2C_GetState(&hi2c1); printf("err1:%02x -- err2:%02x ",status,error_status);

    查看到的是status基本就是HAL_BUSY,error_status基本就是HAL_I2C_STATE_BUSY_TX,用逻辑分析仪查看I2C,即使重新调用HAL_I2C_Master_Transmit(),也没看到有波形出来,用万用表量出来SDA是高电平,SCL是低电平。

    在网上搜索STM32硬件I2C锁死,能搜到几篇有参考性的文章,其中下面这篇参考性意义很大,在此非常感谢:

    http://blog.csdn.net/dldw8816/article/details/51579781

    由于芯片不一样,所以方法也不一样,但思路是一样的,我贴上我的解决方法:

    查看datasheet上寄存器的描述,清零PE,就可以清除I2C的所有错误状态。

    void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
    {
      unsigned char i;
      
      GPIO_InitTypeDef GPIO_InitStruct;
      if(i2cHandle->Instance==I2C1)
      {
          __HAL_RCC_I2C1_CLK_ENABLE();
          __HAL_RCC_GPIOB_CLK_ENABLE();
    
          GPIO_InitStruct.Pin    = I2C_SCL_Pin|I2C_SDA_Pin;
          GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
          GPIO_InitStruct.Pull  = GPIO_NOPULL;
          GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
          HAL_GPIO_Init(I2C_SCL_GPIO_Port, &GPIO_InitStruct);
    
          HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_SET);
          HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_SET);   /*force to release I2C bus*/
    
          __HAL_I2C_DISABLE(i2cHandle);
          for(i=0;i<100;i++);              /*delay awhile*/  
          __HAL_I2C_ENABLE(i2cHandle);
          
          /*I2C1 GPIO Configuration    
              PB8     ------> I2C1_SCL
              PB9     ------> I2C1_SDA 
           */
          GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin;
          GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
          GPIO_InitStruct.Pull = GPIO_PULLUP;
          GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
          GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
          HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
      }
    }
    void I2C_Reset(void)
    {
        HAL_I2C_MspDeInit(&hi2c1);
        HAL_I2C_MspInit(&hi2c1);
    }

    如果单单这么做,然后在I2C传输返回值不是HAL_OK的时候调用I2C_Reset,也不能解决问题,仍然返回HAL_BUSY,于是查看发送源码,发现STM32的库函数里面软件实现了一个状态机,需要将这个状态机恢复到初始状态,代码如下:

            do{
                 status = HAL_I2C_Master_Transmit(&hi2c1, SLAVE_IIC_ADDR, i2c_data, 2, 2);
                 if(status != HAL_OK)
                 {
                     I2C_Reset();
    #if 1             
                     error_status = HAL_I2C_GetState(&hi2c1);
                     printf("err1:%02x -- err2:%02x
    ",status,error_status);
    #endif
                     __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_STOPF); /* Clear STOP Flag */
                     I2C_RESET_CR2(&hi2c1);                        /* Clear Configuration Register 2 */
                     hi2c1.State = HAL_I2C_STATE_READY;
                     hi2c1.Mode  = HAL_I2C_MODE_NONE;
                     __HAL_UNLOCK(&hi2c1);                              /* Process Unlocked */
                 }
                 
            }while(status != HAL_OK);

    这样测下来I2C发生错误之后,就可以重新初始化总线了,测试了一上午,查看串口打印出来的log,能看到printf("err1:%02x -- err2:%02x ",status,error_status);这条消息,但我的代码可以修复I2C。

    下面两篇文章,一篇是ST官方的解决方案,一篇是网上没有署名的大神写的文档,再次非常感谢!

    STM32-I2C-接口进入-Busy-状态不能退出.pdf

    解决STM32-I2C接口死锁在BUSY状态的问题.pdf

  • 相关阅读:
    Shenzhen Wanze Technology Co., Ltd.技术支持
    Shenzhen Wanze Technology Co., Ltd.隐私协议
    123457123456#1#----com.MC.CarWashKidsGames234----前拼后广--洗车游戏mc-mc1111
    123457123457#0#----com.MC.konglongtianse222----前拼后广--恐龙填色mc-mc1111
    123457123456#5#----com.MC.HuaHuaGame866----前拼后广--babyDrawGame-mc555
    ios开发注意事项小总结
    ios开发将截图保存到相册
    iOS评分功能实现
    123457123456#2#----com.MC.DishuGame368----前拼后广--儿童打地鼠Game-mc2222222
    123457123457#0#----com.MC.3or1KongLongPT867----前拼后广--3or1恐龙PtGame-mc
  • 原文地址:https://www.cnblogs.com/kent-hu/p/8250099.html
Copyright © 2020-2023  润新知