2022-04-07 04:41 PM
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.
2022-04-07 04:43 PM
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.
2022-04-07 04:51 PM
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.
2022-04-08 02:01 AM
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.