2021-09-20 08:54 AM
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
2021-09-20 08:56 AM
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.
2021-09-23 03:10 AM
Bug has been registered here
https://github.com/STMicroelectronics/stm32l0xx_hal_driver/issues/5