Kinetis SDK I2C driver BUSY bugs

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Kinetis SDK I2C driver BUSY bugs

1,412 Views
NicolasP
Contributor IV

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;
}
0 Kudos
5 Replies

1,062 Views
susansu
NXP Employee
NXP Employee

NicolasP‌, thank you for raising the issue.

I have some comments to your situation:

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

May I know why the bus is busy? Is that because the bus is occupied by another master?  In the design thoughts of the low-level I2C driver, the I2C handle should be a global variable and multi-masters should uses the same handle variable when call the I2C transfer APIs. So, it's definitely not allowed to revert the state variable to idle when the call returns to busy state in I2C_MasterTransferNonBlocking()  API.

I would suggest to call I2C_MasterTranferAbort API inside I2C_RTOS_Transfer API when the call to I2C_MasterTransferNonBlocking does not return kStatus_Success.

{code}

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)
{

/* NEW !!! Add call to transfer abort to abort the transfer. */

I2C_MasterTransferAbort((handle->base, &handle->drv_handle)


xSemaphoreGive(handle->mutex);
return status;
}

/* Wait for transfer to finish */
xSemaphoreTake(handle->semaphore, portMAX_DELAY);

/* Unlock resource mutex */
xSemaphoreGive(handle->mutex);

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

{code}

0 Kudos

1,062 Views
danielchen
NXP TechSupport
NXP TechSupport

Hi Nicolas:

Thank you very much for your feedback.

For your first issue, I will report it to the develop team.

For your second issue, could you please let me know how to reproduce it? it also happens  with a multi-master system?

Regards

Daniel

0 Kudos

1,062 Views
NicolasP
Contributor IV

Hi Daniel,

Thanks for your interest in my report.

As I said in my EDIT post, the second issue randomly happens when connecting/de-connecting the smart battery.

This is not a normal situation. I believe this happens only because the SDA and SCL lines are erroneously badly driven to an undefined state during the connection/de-connection of the battery.

Best regards,

Nicolas

0 Kudos

1,062 Views
danielchen
NXP TechSupport
NXP TechSupport

Thanks, I will do some tests to verify this issue.

Regards

Daniel

0 Kudos

1,062 Views
NicolasP
Contributor IV

Daniel,

If this can be of any help, on my design I use the following hardware :

- RRC battery cable

- RRC battery

Regards,

Nicolas

0 Kudos