AnsweredAssumed Answered

CubeMx HAL F3 version 1.4 I2C slave limitations

Question asked by le_devehat.loik on Mar 8, 2016
Latest reply on Mar 8, 2016 by FTITI.Walid
Hello,

For my application I am writing an i2c slave that uses polling mode.
I noticed that the slave_receive function implementation is not practical.

HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout)

You must pass the Size parameter, however there is no way to receive less than Size bytes.
If the master sends less than expected number of bytes and then a stop flag. The Hal will just return an HAL_ERROR status.

So i'm proposing a modification that uses existing mechanisms by using the XFerCount variable.

Here is the modified code.

HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  if(hi2c->State == HAL_I2C_STATE_READY)
  {  
    if((pData == NULL ) || (Size == 0))
    {
      return  HAL_ERROR;                                    
    }
    
    /* Process Locked */
    __HAL_LOCK(hi2c);
    hi2c->XferCount = 0;
    hi2c->State = HAL_I2C_STATE_SLAVE_BUSY_RX;
    hi2c->ErrorCode   = HAL_I2C_ERROR_NONE;
    
    /* Enable Address Acknowledge */
    hi2c->Instance->CR2 &= ~I2C_CR2_NACK;

    /* Wait until ADDR flag is set */
    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, RESET, Timeout) != HAL_OK)      
    {
      /* Disable Address Acknowledge */
      hi2c->Instance->CR2 |= I2C_CR2_NACK;
      return HAL_TIMEOUT;
    }

    /* Clear ADDR flag */
    __HAL_I2C_CLEAR_FLAG(hi2c,I2C_FLAG_ADDR);
    
    /* Wait until DIR flag is reset Receiver mode */
    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_DIR, SET, Timeout) != HAL_OK)      
    {
      /* Disable Address Acknowledge */
      hi2c->Instance->CR2 |= I2C_CR2_NACK;
      return HAL_TIMEOUT;
    }

    while(Size > 0)
    {
      /* Wait until RXNE flag is set */
      if(I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout) != HAL_OK)      
      {
        /* Disable Address Acknowledge */
        hi2c->Instance->CR2 |= I2C_CR2_NACK;
        
        /* Store Last receive data if any */
        if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXNE) == SET)
        {
          /* Read data from RXDR */
          (*pData++) = hi2c->Instance->RXDR;
          hi2c->XferCount++;
        }
        
        if(hi2c->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
        {
          return HAL_TIMEOUT;
        // A stop condition occured, consider it's a timeout
        }else if (hi2c->ErrorCode == HAL_I2C_ERROR_NONE) {
          return HAL_TIMEOUT;
        } else {
          return HAL_ERROR;
        }

      }
      
      /* Read data from RXDR */
      (*pData++) = hi2c->Instance->RXDR;
      hi2c->XferCount++;
      Size--;
    }
    
    /* Wait until STOP flag is set */
    if(I2C_WaitOnSTOPFlagUntilTimeout(hi2c, I2C_TIMEOUT_STOPF) != HAL_OK)
    {
      /* Disable Address Acknowledge */
      hi2c->Instance->CR2 |= I2C_CR2_NACK;

      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);
    
    /* Wait until BUSY flag is reset */
    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, Timeout) != HAL_OK)      
    {
      /* Disable Address Acknowledge */
      hi2c->Instance->CR2 |= I2C_CR2_NACK;
      return HAL_TIMEOUT;
    }

    
    /* Disable Address Acknowledge */
    hi2c->Instance->CR2 |= I2C_CR2_NACK;
    
    hi2c->State = HAL_I2C_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hi2c);
    
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

And then when using this function you can know how many bytes were received by checking the hi2c.XFferCount value if a HAL_TIMEOUT status is returned

Outcomes