In addition to the I2C abritation problem, the NXP SDK function I2C_RTOS_Transfer() has a few bugs, for which I have added details and suggested solutions as below.
status_t I2C_RTOS_Transfer(i2c_rtos_handle_t *handle, i2c_master_transfer_t *transfer)
{
status_t status;
/* Lock resource mutex */
if (xSemaphoreTake(handle->mutex, portMAX_DELAY) != pdTRUE)
{
return kStatus_I2C_Busy;
}
status = I2C_MasterTransferNonBlocking(handle->base, &handle->drv_handle, transfer);
if (status != kStatus_Success)
{
xSemaphoreGive(handle->mutex);
return status;
}
/* Wait for transfer to finish */
(void)xSemaphoreTake(handle->semaphore, portMAX_DELAY);
BUG 1
Using the standard SDK code, the xSemaphoreTake delay will never expire after an I2C error occurs, so any task calling this will obviously hang forever!
/* Unlock resource mutex */
xSemaphoreGive(handle->mutex);
/* Return status captured by callback function */
return handle->async_status;
BUG 2
The async_status is read after the mutex has been released, if another I2C transfer is pending for the same interface the returned status will be invalid. May seem unlikely but it does happen!!
}
-----------------------------------------------------------
BUG 1 Suggested solution (limited testing but so far this has recovered from the arbitration error)
Use a fixed time delay when waiting for the semaphore, and if a timeout occurs (due to arbitration or other error) then:
1) Clear I2C status flags using I2C_MasterClearStatusFlags()
2) Reset the problem I2C interface using RESET_PeripheralReset()
3) Init the interface using I2C_MasterInit()
4) Reset the RTOS transfer state-machine
handle->drv_handle.state = (uint8_t)kIdleState;
-----------------------------------------------------------
BUG 2 Suggested solution (as per fsl_spi_freertos.c, SPI_RTOS_Transfer())
/* Retrieve status before releasing mutex */
status = handle->async_status;