cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_I2C_Mem_Read_IT() blocks MCU!

HTD
Senior III

The function is supposed to read the I2C in non-blocking mode.

However - quite often - it blocks the MCU for like 2 seconds!

As I need to read an I2C in time critical part of my program, this breaks everything.

I investigated the function source and I found THIS:

static HAL_StatusTypeDef I2C_RequestMemoryRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
                                               uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout,
                                               uint32_t Tickstart)
{
  I2C_TransferConfig(hi2c, DevAddress, (uint8_t)MemAddSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
 
  /* Wait until TXIS flag is set */
  if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
  {
    return HAL_ERROR;
  }
 
  /* If Memory address size is 8Bit */
  if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
  {
    /* Send Memory Address */
    hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
  }
  /* If Memory address size is 16Bit */
  else
  {
    /* Send MSB of Memory Address */
    hi2c->Instance->TXDR = I2C_MEM_ADD_MSB(MemAddress);
 
    /* Wait until TXIS flag is set */
    if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
    {
      return HAL_ERROR;
    }
 
    /* Send LSB of Memory Address */
    hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
  }
 
  /* Wait until TC flag is set */
  if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, RESET, Timeout, Tickstart) != HAL_OK)
  {
    return HAL_ERROR;
  }
 
  return HAL_OK;
}

WHAT THE HECK?! What's the point of IT function ("non-blocking") if it WAITS?

No matter if it's executed from a timer interrupt or my GUI thread - it freezes the application!

Is it a bug, because for me it really, really looks like a bug. It defeats the point of the interrupt mode call.

And what can I do to save the day? Maybe if I set timeout to zero...

The point is - if the I2C is not ready, I want the operation retried after let's say 1ms, 10ms, 100ms. But I'm positive I don't want to WAIT until it's ready blocking the entire MCU.

In my application I2C is used to read PCA9555 GPIO expander. My application must immediately (like within milliseconds) react to changes on any of the PCA input. The PCA9555 gives INT signal, I get it, I read the chip and get what's changed.

However, sometimes either the reading or the interrupt fails - that leaves me with an invalid state, because my program doesn't know the input signal has changed. To solve that issue I made reading called from a timer interrupt every 100ms. Before the call is made I check the I2C busy flag. If it's set, I just skip the call.

As I tested my code - failed calls practically don't occur, so my checks are almost always hit, hardly ever missed.

Now the terrible lags - they don't occur when __HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) == SET.

They occur on normal reading operation, when I2C_FLAG_BUSY is not set.

This is an absolute show stopper for me. I consider using a different chip using like SPI instead of I2C, but I have a deadline, and this will mean a week or more of delay. Please help.

All I need just really non-blocking reading of the I2C. I use one of the inputs to measure a time that is circa 1 second. It doesn't have to be super precise, I can accept a measurement error, but not hanging entire application for more than a second, this would just not work at all.

1 REPLY 1
HTD
Senior III

Here's my solution to the problem:

https://gist.github.com/HTD/e36fb68488742f27a737a5d096170623

I hacked the original driver. I think this should be a part of the official driver, not a hack, change my mind 😉

BTW, now it works. No problem with readings triggered with interrupts.