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 :
- 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)
{
status_t result = kStatus_Success;
i2c_direction_t direction = xfer->direction;
...
else
{
result = I2C_MasterStart(base, handle->transfer.slaveAddress, direction);
}
if (result == kStatus_I2C_Busy)
{
handle->state = kIdleState;
}
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;
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;
}
err = xSemaphoreTake(handle->semaphore, 1000/portTICK_PERIOD_MS);
if (err != pdTRUE)
{
handle->async_status = kStatus_I2C_Timeout;
I2C_MasterTransferAbort(handle->base, &handle->drv_handle);
}
xSemaphoreGive(handle->mutex);
return handle->async_status;
}