On my system, I have two I2C masters : a Kinetis (K22FN512VLH12) and a smart battery.
I use MCUXpresso SDK V2.5.0.
I use the FreeRTOS I2C driver for the Kinetis to communicate with the smart battery.
I am facing 2 bugs where the I2C driver either returns BUSY infinitely or stalls.
First bug :
Here is the scenario :
- Application calls I2C_RTOS_Transfer()
- I2C_RTOS_Transfer() calls I2C_MasterTransferNonBlocking()
- I2C_MasterTransferNonBlocking() checks for handle->state to be equal to kIdleState
- I2C_MasterTransferNonBlocking() calls I2C_InitTransferStateMachine()
- I2C_InitTransferStateMachine() modifies handle->state
- I2C_InitTransferStateMachine() calls I2C_MasterStart()
- I2C_MasterStart() calls I2C_MasterGetStatusFlags()
- I2C_MasterGetStatusFlags() returns kStatus_I2C_Busy
- I2C_MasterStart() returns kStatus_I2C_Busy
- I2C_InitTransferStateMachine() returns kStatus_I2C_Busy
- I2C_MasterTransferNonBlocking() returns kStatus_I2C_Busy
- I2C_RTOS_Transfer() returns kStatus_I2C_Busy
I2C_RTOS_Transfer() exits with a BUSY error but handle->state is not reverted to kIdleState. So, next time the application calls I2C_RTOS_Transfer(), the process stops at step 3. This repeats infinitely.
My solution to this problem is to revert handle->state to kIdleState at the end of I2C_InitTransferStateMachine() function like this :
static status_t I2C_InitTransferStateMachine(I2C_Type *base, i2c_master_handle_t *handle, i2c_master_transfer_t *xfer) //TODO
status_t result = kStatus_Success;
i2c_direction_t direction = xfer->direction;
else /* For normal transfer, send start. */
result = I2C_MasterStart(base, handle->transfer.slaveAddress, direction);
if (result == kStatus_I2C_Busy) // Added
handle->state = kIdleState; // Added
Second bug :
There is a second bug which, sometimes, happens when the smart battery is plugged or unplugged.
In this scenario, I2C_RTOS_Transfer() never exits. It is stalled waiting for xSemaphoreTake() to return.
I suppose there is a race condition between a busy bus state or arbitration lost state and I2C driver sequencer.
I have not found a good fix for this bug.
My (temporary) solution is to specify a timeout to xSemaphoreTake().
The first bug (BUSY) arises only with a multi-master system. In my case, the smart battery acts has a slave/master.
The second bug is, I believe, much more complex to understand. However, it arises quite often when plugging-in/plugging-out the smart battery. Here, the problem is related to the SCL and SDA signals that are randomly driven in a high/low/undefined state. There might not be any other solution than managing the timeout. It might request a big amount of time to fix it. I don't have this time, so I'll keep my solution for now. Here it is :
status_t I2C_RTOS_Transfer(i2c_rtos_handle_t *handle, i2c_master_transfer_t *transfer)
int err; // Added
/* Lock resource mutex */
if (xSemaphoreTake(handle->mutex, portMAX_DELAY) != pdTRUE)
status = I2C_MasterTransferNonBlocking(handle->base, &handle->drv_handle, transfer);
if (status != kStatus_Success)
/* Wait for transfer to finish */
err = xSemaphoreTake(handle->semaphore, 1000/portTICK_PERIOD_MS); // Modfied
if (err != pdTRUE) // Added
//printf("!!! I2C_RTOS_Transfer timeout !!!\n");
handle->async_status = kStatus_I2C_Timeout; // Added
I2C_MasterTransferAbort(handle->base, &handle->drv_handle); // Added
/* Unlock resource mutex */
/* Return status captured by callback function */