STMicroelectronics / STM32CubeG0

STM32Cube MCU Full Package for the STM32G0 series - (HAL + LL Drivers, CMSIS Core, CMSIS Device, MW libraries plus a set of Projects running on all boards provided by ST (Nucleo, Evaluation and Discovery Kits))
Other
164 stars 75 forks source link

HAL_I2C_Slave_Trasmit_IT always fails on second call #35

Closed etheory closed 2 years ago

etheory commented 2 years ago

Describe the set-up

Describe the bug

I have a Nucleo G474RE driving the G0 via I2C. The exact same setup works perfectly when driving an Nucleo G431KB so it appears limited to the G0 series. When I perform the following sequence the first time, everything works:

Slave -> HAL_I2C_Slave_Receive_IT Master -> HAL_I2C_Master_Transmit_IT Slave -> HAL_I2C_SlaveRxCpltCallback -> HAL_I2C_Slave_Transmit_IT Master -> HAL_I2C_MasterTxCpltCallback -> HAL_I2C_Master_Receive_IT Slave -> HAL_I2C_SlaveTxCpltCallback -> HAL_I2C_Slave_Receive_IT And repeat....

On the first loop through, I am sending 1 byte from the master to the slave, then the slave responds with two bytes, and the first time, this works:

image

But the second time, the last byte fails to send: image

And the slave holds the clock low forever.

Inside the HAL driver:

static HAL_StatusTypeDef I2C_Slave_ISR_IT(struct __I2C_HandleTypeDef *hi2c, uint32_t ITFlags,
                                          uint32_t ITSources)

On the second call of the sequence, it gets stuck forever, calling:

  else if ((I2C_CHECK_FLAG(tmpITFlags, I2C_FLAG_TXIS) != RESET) && \
           (I2C_CHECK_IT_SOURCE(ITSources, I2C_IT_TXI) != RESET))
  {
    /* Write data to TXDR only if XferCount not reach "0" */
    /* A TXIS flag can be set, during STOP treatment      */
    /* Check if all Data have already been sent */
    /* If it is the case, this last write in TXDR is not sent, correspond to a dummy TXIS event */
    if (hi2c->XferCount > 0U)
    {
      /* Write data to TXDR */
      hi2c->Instance->TXDR = *hi2c->pBuffPtr;

      /* Increment Buffer pointer */
      hi2c->pBuffPtr++;

      hi2c->XferCount--;
      hi2c->XferSize--;
    }
    else
    {
      if ((tmpoptions == I2C_NEXT_FRAME) || (tmpoptions == I2C_FIRST_FRAME))
      {
        /* Last Byte is Transmitted */
        /* Call I2C Slave Sequential complete process */
        I2C_ITSlaveSeqCplt(hi2c);
      }
    }
  }

indefinitely.

The issue is that hi2c->XferCount and hi2c->XferSize are both zero, but tmpoptions is set to I2C_NO_OPTION_FRAME so it never completes. Also, TXIS and TXE are set to 1, so the transfer thinks it's incomplete, and continues to run indefinitely, holding the clock line low.

Additional context I have been unable to figure out the cause of this, but I've spent a LONG time looking in to it. It appears to be an issue with HAL.

etheory commented 2 years ago

I've since fixed this by switching to the HAL_I2C_EnableListen_IT, HAL_I2C_Slave_Seq_Receive_IT, HAL_I2C_Slave_Seq_Transmit_IT API's, which do not suffer from the above issue.

JRASTM commented 2 years ago

Hello,

This workaround seems correct and compliant with the SW design. But in your first comment on 5 July, did not find the Enable Listen IT notion.

So what is finally the workaround versus function call stack that you have done before ?

Regards