你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F767IGTB使用IIC的过程中遇到读超时以及总线忙的问题

[复制链接]
yaoyefengling 提问时间:2019-5-29 14:47 /
这两天测试过程中发现读取eeprom偶尔会报错,定位过程发现IIC总线上经常会有时钟总线为低的情况,开始不解,经查阅资料发现这是正常现象,从MCU手册上得知,当移位寄存器里8位数据接收完成后,如果这时接收寄存器里有未取走的数据,那么IIC外设会在本次发送ACK前拉低总线,防止从设备继续回数据导致数据丢失。因此在读取EEPROM的过程中如果被中断或其它线程打断,就会出现这种情况,如果被打断的持续时间超过了一次II2读取的超时时间就会报错。根据这个,修改了每次读取EEPROM的超时时间,再测试发现故障率降低了,但是又偶发过一次,继续定位,突发奇想,如果在一次读取EEPROM的过程中我在读取接收寄存器前加个断点,总线会是什么表现。奇怪的事情发生了,如下图,总线上竟然传回了3个字节的数据,紧跟着写地址读操作后是2个字节的数据,之后时钟总线一直为低,但是过了150ms后总线上竟然又传回一个字节的数据。理论上分析当第一个字节传输完成时接收寄存器为空,数据从移位寄存器送到接收寄存器,紧接着第二字节数据到达,因为此时第一个字节还占着接收寄存器,所以第二个字节存在移位寄存器里,在ACK前时钟总线被IIC外设拉低,现象和手册吻合,但是150MS后总线上又传输了一个字节,这期间MCU一直是停在断点( 下图if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_RXNE, RESET, Timeout, tickstart) != HAL_OK)处),于是第一个字节被成功覆盖了,最终第一个字节丢失,其它数据往前窜一个字节,最后一个字节直接超时,因为早在上一次总线就传输完了。设想,如果一次读取EEPROM的过程被打断超过150ms是不是就有可能出现这种错误。

HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint32_t tickstart = 0U;

  /* Check the parameters */
  assert_param(IS_I2C_MEMADD_SIZE(MemAddSize));

  if(hi2c->State == HAL_I2C_STATE_READY)
  {
    if((pData == NULL) || (Size == 0U))
    {
      return  HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(hi2c);

    /* Init tickstart for timeout management*/
    tickstart = HAL_GetTick();

    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK)
    {
      return HAL_TIMEOUT;
    }

    hi2c->State     = HAL_I2C_STATE_BUSY_RX;
    hi2c->Mode      = HAL_I2C_MODE_MEM;
    hi2c->ErrorCode = HAL_I2C_ERROR_NONE;

    /* Prepare transfer parameters */
    hi2c->pBuffPtr  = pData;
    hi2c->XferCount = Size;
    hi2c->XferISR   = NULL;

    /* Send Slave Address and Memory Address */
    if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, Timeout, tickstart) != HAL_OK)
    {
      if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
      {
        /* Process Unlocked */
        __HAL_UNLOCK(hi2c);
        return HAL_ERROR;
      }
      else
      {
        /* Process Unlocked */
        __HAL_UNLOCK(hi2c);
        return HAL_TIMEOUT;
      }
    }

    /* Send Slave Address */
    /* Set NBYTES to write and reload if hi2c->XferCount > MAX_NBYTE_SIZE and generate RESTART */
    if(hi2c->XferCount > MAX_NBYTE_SIZE)
    {
      hi2c->XferSize = MAX_NBYTE_SIZE;
      I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_READ);
    }
    else
    {
      hi2c->XferSize = hi2c->XferCount;
      I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_READ);
    }

    do
    {
      /* Wait until RXNE flag is set */
      if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_RXNE, RESET, Timeout, tickstart) != HAL_OK)
      {
        return HAL_TIMEOUT;
      }

      /* Read data from RXDR */
      (*hi2c->pBuffPtr++) = hi2c->Instance->RXDR;
      hi2c->XferSize--;
      hi2c->XferCount--;

      if((hi2c->XferSize == 0U) && (hi2c->XferCount != 0U))
      {
        /* Wait until TCR flag is set */
        if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }

        if(hi2c->XferCount > MAX_NBYTE_SIZE)
        {
          hi2c->XferSize = MAX_NBYTE_SIZE;
          I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP);
        }
        else
        {
          hi2c->XferSize = hi2c->XferCount;
          I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP);
        }
      }
    }while(hi2c->XferCount > 0U);

    /* No need to Check TC flag, with AUTOEND mode the stop is automatically generated */
    /* Wait until STOPF flag is reset */
    if(I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
    {
      if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
      {
        return HAL_ERROR;
      }
      else
      {
        return HAL_TIMEOUT;
      }
    }

    /* Clear STOP Flag */
    __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);

    /* Clear Configuration Register 2 */
    I2C_RESET_CR2(hi2c);

    hi2c->State = HAL_I2C_STATE_READY;
    hi2c->Mode  = HAL_I2C_MODE_NONE;

    /* Process Unlocked */
    __HAL_UNLOCK(hi2c);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

20190529141812.jpg
收藏 评论7 发布时间:2019-5-29 14:47

举报

7个回答
废鱼 回答时间:2019-5-29 15:05:45
楼主问一下,I2C总线上是否有上拉电阻?建议增加上拉电阻。当发生异常后,重新初始化一下I2C是否可以解决该问题?

评分

参与人数 1蝴蝶豆 +2 收起 理由
STMCU + 2

查看全部评分

yaoyefengling 回答时间:2019-5-29 15:06:57
安 发表于 2019-5-29 15:05
楼主问一下,I2C总线上是否有上拉电阻?

有外部上拉,是正常的
废鱼 回答时间:2019-5-29 15:13:31
楼主是否有跑系统,如果有系统使用,I2C操作建议增加互斥,保证I2C在通讯过程中是独占的。
yaoyefengling 回答时间:2019-5-29 15:22:02
安 发表于 2019-5-29 15:13
楼主是否有跑系统,如果有系统使用,I2C操作建议增加互斥,保证I2C在通讯过程中是独占的。 ...

有 系统,也有互斥锁,互斥锁只能保证IIC不能同时被多个线程使用,但是无法保证不被其它线程和中断打断,所以单纯加互斥对上述问题没有效果。
edmundlee 回答时间:2019-5-29 16:06:23
用I2C, 首选中断的方式。因为你其它的中断一旦占用了CPU超过一定的时间, I2C就会出错。

评分

参与人数 1蝴蝶豆 +2 收起 理由
STMCU + 2

查看全部评分

yaoyefengling 回答时间:2019-5-30 09:38:30
edmundlee 发表于 2019-5-29 16:06
用I2C, 首选中断的方式。因为你其它的中断一旦占用了CPU超过一定的时间, I2C就会出错。
...

现在读操作改成中断了,正在测试
edmundlee 回答时间:2019-5-30 12:19:06
yaoyefengling 发表于 2019-5-30 09:38
现在读操作改成中断了,正在测试

要把I2C中断优先级别设为系统不管理的级别
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版