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

Hi,

 

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().

 

Nicolas

 

EDIT :

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)
    {
        xSemaphoreGive(handle->mutex);
        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 */
    xSemaphoreGive(handle->mutex);

    /* Return status captured by callback function */
    return handle->async_status;
}

Outcomes