Skip to main content
TDJ
Senior III
September 20, 2021
Question

STM32 I2C Slave mode HAL bug

  • September 20, 2021
  • 2 replies
  • 1774 views

Recently I have tried to implement a solution employing I2C interface in slave mode.

The solution follows the pattern presented in the official I2C_TwoBoards_RestartAdvComIT example available in CubeMX packages and another solution presented here: STM32F072 I2C slave Receive callback.

After fighting with incorrect timings I2C generated by CubeMX (see Possible CubeMX I2C timing config bug (STM32L0x and other)) I stumbled upon another problem: the last received frame is written twice at two consecutive buffer offsets which may result in buffer overrun and unpredictable results.

I found that the problem is caused by imperfect HAL driver code. The solution: before calling HAL_I2C_Slave_Seq_Receive_IT make sure there is more data available:  

if (I2C_CHECK_FLAG(hi2c->Instance->ISR, I2C_FLAG_RXNE) == SET) ...

The problem occurs with STM32L041 and probably all recent MCUs using new I2C architecture/control registry layout. Please find details and sequence of calls in response to this post. (for any reason I am unable to add code snippets to this initial post)

Package: STM32Cube_FW_L0_V1.12.1

#I2C​ 

This topic has been closed for replies.

2 replies

TDJ
TDJAuthor
Senior III
September 20, 2021

Detailed call sequence (pseudocode)

-> HAL_I2C_EV_IRQHandler()
 -> I2C_Slave_ISR_IT()
 -> I2C_ITSlaveCplt()
 // problem 1: I2C_FLAG_RXNE flag is cleared but on local/temp flags copy only
 tmpITFlags &= ~I2C_FLAG_RXNE;
 *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->RXDR;
 pBuffPtr++;
 -> I2C_ITSlaveSeqCplt()
 -> HAL_I2C_SlaveRxCpltCallback()
 // problem 2: HAL_I2C_Slave_Seq_Receive_IT is called when it is already known that there is no more data 
 -> HAL_I2C_Slave_Seq_Receive_IT(Size=1)
 hi2c->XferCount = Size; // here XferCount is set to 1 again while receiving of the current frame is not finished
	 <- HAL_I2C_SlaveRxCpltCallback()
 <- I2C_ITSlaveSeqCplt()
 <- I2C_ITSlaveCplt()
 <- I2C_Slave_ISR_IT()
 // problem 3: data from RXDR register is read again without checking that I2C_FLAG_RXNE flag is still set
 if (hi2c->XferCount > 0U) *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->RXDR;
<- HAL_I2C_EV_IRQHandler()

Possible solution:

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) {
 if (_isFirstFrame) {
 // start address/offset received
 _isFirstFrame = false;
 } else {
 // process _rxBuffer[_rxBufferOffset] value here
 _rxBufferOffset++;
 }
 
 if (I2C_CHECK_FLAG(hi2c->Instance->ISR, I2C_FLAG_RXNE) == SET) { // check more data is available
 HAL_StatusTypeDef ret = HAL_I2C_Slave_Seq_Receive_IT(hi2c, &_rxBuffer[_rxBufferOffset], 1, I2C_NEXT_FRAME);
 }
}

Note: I2C_NEXT_FRAME and I2C_FIRST_FRAME flags appear to be equivalent - I found no difference in HAL code, both values are checked everywhere.

TDJ
TDJAuthor
Senior III
September 23, 2021