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 :
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;
}
NicolasP, thank you for raising the issue.
I have some comments to your situation:
Here is the scenario :
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}
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
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
Thanks, I will do some tests to verify this issue.
Regards
Daniel
Daniel,
If this can be of any help, on my design I use the following hardware :
Regards,
Nicolas