cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_I2C_Slave_Transmit_IT hangs forever

etheory
Senior

Hi there,

On some MCUs, I've noticed that the second call to HAL_I2C_Slave_Transmit_IT hangs forever.

An example I have is on an STM32G030K6 (which doesn't seem to happen on a STM32G474RE).

I have set all pins to fast to ensure that's not the issue either.

The general paradigm is as follows:

The slave calls:

HAL_I2C_Slave_Receive_IT

The master calls:

HAL_I2C_Master_Transmit_IT

The slave callback:

HAL_I2C_SlaveRxCpltCallback

runs, then calls:

HAL_I2C_Slave_Transmit_IT

The master callback:

HAL_I2C_MasterRxCpltCallback

then completes the first "transfer".

At this point, everything seems OK.

One the next transfer, HAL_I2C_Slave_Transmit_IT never completes, because it sends 1 less of the buffer than requested, then the I2C_Slave_ISR_IT routine becomes stuck in an infinite loop. I checked this in a debugger. The issue seems to be that the i2c XferOptions is set to I2C_NO_OPTION_FRAME, but the I2C_Slave_ISR_IT function keeps not calling I2C_ITSlaveSeqCplt because the guards around calling this function won't fire when tmpoptions are set to I2C_NO_OPTION_FRAME.

There appears to even be a ticket about this here: https://github.com/STMicroelectronics/STM32CubeF4/issues/76 from June 2021, that has had zero progress. This ticket describes the issue quite nicely.

ST, is there any progress on this? Since it would appear that the I2C IT is, currently, for some of your MCUs, very very broken with HAL, and needs to be fixed.

Any suggestions/answers here would be much appreciated.

Thanks.

3 REPLIES 3
etheory
Senior

My code on the slave is, more or less, as follows:

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef* hi2c)
{
  if (hi2c != &hi2c2) return;
 
  // what is the command we are being asked to respond to?
  switch (i2c_data.in)
  {
    case 0u: // a status update was requested
    {
      i2c_data.out.i = encoder_state[0].value;
      HAL_I2C_Slave_Transmit_IT(&hi2c2, i2c_data.out.bytes, 2);
      break;
    }
    default:
      break;
  }
}
 
// handle request for data
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef* hi2c)
{
  if (hi2c != &hi2c2) return;
 
  // set up next receive of data
  HAL_I2C_Slave_Receive_IT(&hi2c2, &i2c_data.in, 1);
 
  // lower EXT pin as data transmit is complete
  led_state &= ~1;
  HAL_GPIO_WritePin(EXT_GPIO_Port, EXT_Pin, GPIO_PIN_RESET);
}

Just in case it's useful. Having said that, the issue does seem pretty easy to reproduce.

etheory
Senior

The code on the Master is, more or less:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef* hi2c)
{
  if (hi2c != &hi2c1) return;
 
  switch (i2c_transfer_type)
  {
    case kI2CTransferType_Data:
    {
      HAL_I2C_Master_Receive_IT(&hi2c1, i2c_input_address << 1u, i2c_data.in.bytes, 2);
      break;
    }
    case kI2CTransferType_LCD:
    {
      I2C_Manager_MasterTxCpltCallback(&lcd_i2c_manager);
      break;
    }
    case kI2CTransferType_None:
    default:
      break;
  }
  //DEBUG_LOG(&huart3, "HAL_I2C_MasterTxCpltCallback - %d", lcd_i2c_manager.transmitIndex);
  DEBUG_LOG(&huart3, "HAL_I2C_MasterTxCpltCallback - i2c_transfer_type: %d", i2c_transfer_type);
}
 
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef* hi2c)
{
  if (hi2c != &hi2c1) return;
 
  uint16_t receivedData = i2c_data.in.i;
  i2c_transfer_type = kI2CTransferType_None;
 
  DEBUG_LOG(&huart3, "HAL_I2C_MasterRxCpltCallback - data: %d", receivedData);
  //received_data = i2c_input_data.i;
  //data_dirty = 1u;
}

With:

        case kI2CTransferType_None:
        {
          i2c_transfer_type = kI2CTransferType_Data;
 
          DEBUG_LOG(&huart3, "HAL_GPIO_EXTI_Callback - EXT_Pin - kI2CTransferType_None");
 
          // EXT is raised, so receive data from the control board
          i2c_data.out = 0u; // ask device for status update
          HAL_I2C_Master_Transmit_IT(&hi2c1, i2c_input_address << 1, &i2c_data.out, 1);
 
          break;
        }

Sitting in an EXTI for a pin that the slave pulls high.

Everything except the second HAL_I2C_Slave_Transmit_IT works perfectly, and, as I said, all setup up to that point is perfect. There is no brown out, the i2c pins are correctly pulled high with 4.7K resistors, and there are no other setup issues. Just the second transmit call sends one less byte of data, then gets stuck forever in the interrupt.

etheory
Senior

I have a little bit more info here.

On the first cycle, the following code is here the transfer successfully completes:

static HAL_StatusTypeDef I2C_Slave_ISR_IT(struct __I2C_HandleTypeDef *hi2c, uint32_t ITFlags,
                                             uint32_t ITSources)
{
  uint32_t tmpoptions = hi2c->XferOptions;
  uint32_t tmpITFlags = ITFlags;
 
  /* Process locked */
  __HAL_LOCK(hi2c);
 
  /* Check if STOPF is set */
  if ((I2C_CHECK_FLAG(tmpITFlags, I2C_FLAG_STOPF) != RESET) && \
    (I2C_CHECK_IT_SOURCE(ITSources, I2C_IT_STOPI) != RESET))
  {
    /* Call I2C Slave complete process <- HERE IS WHERE IT COMPLETES */
    I2C_ITSlaveCplt(hi2c, tmpITFlags);
  }

But on the second transfer, this code never even runs, which means that the I2C_FLAG_STOPF and I2C_IT_STOPI flags never get set.