Nicolas Pinault

Kinetis SDK I2C driver BUSY bugs

Discussion created by Nicolas Pinault on Jan 25, 2019
Latest reply on Mar 11, 2019 by Susan Su



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 :

  1. Application calls I2C_RTOS_Transfer()
  2. I2C_RTOS_Transfer() calls I2C_MasterTransferNonBlocking()
  3. I2C_MasterTransferNonBlocking() checks for handle->state to be equal to kIdleState
  4. I2C_MasterTransferNonBlocking() calls I2C_InitTransferStateMachine()
  5. I2C_InitTransferStateMachine() modifies handle->state
  6. I2C_InitTransferStateMachine() calls I2C_MasterStart()
  7. I2C_MasterStart() calls I2C_MasterGetStatusFlags()
  8. I2C_MasterGetStatusFlags() returns kStatus_I2C_Busy
  9. I2C_MasterStart() returns kStatus_I2C_Busy
  10. I2C_InitTransferStateMachine() returns kStatus_I2C_Busy
  11. I2C_MasterTransferNonBlocking() returns kStatus_I2C_Busy
  12. 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

    return result;


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)
    status_t status;
    int err;             // Added

    /* 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)
        return status;

    /* 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 */
    return handle->async_status;