I2C: repeated Start condition using fsl_i2c_freertos

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

I2C: repeated Start condition using fsl_i2c_freertos

3,998 Views
danielholala
Senior Contributor II

Hello all.

Reading data from an EEPROM involves an I2C transaction that uses a combined i2c-write and i2c-read data transfer: Following the write of the memory address, data can be read. The transaction looks like this:

danielholala_3-1644400366988.png

This transaction involves a repeated Start condition.

To realize a repeated Start condition using the now obsolete LPCOpen API a single call to Chip_I2C_MasterTransfer() was sufficient as this function comprised a write with a following read. 

However, the current SDK provides I2C_MasterTransferNonBlocking()which (to my understanding) provides either an i2c-read or an i2c-write transfer. It is more low level (and unwieldier) than the LPCOpen API. To generate the above transadtion, one has to call I2C_MasterTransferNonBlocking()twice and set the flags correctly to create the repeated Start condition.

With that said, let's look at the relevant function I2C_RTOS_Transfer()  in  fsl_i2c_freertos.c:

 

/*!
 * brief Performs I2C transfer.
 *
 * This function performs an I2C transfer according to data given in the transfer structure.
 *
 * param handle The RTOS I2C handle.
 * param transfer Structure specifying the transfer parameters.
 * return status of the operation.
 */
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)
    {
        (void)xSemaphoreGive(handle->mutex);
        return status;
    }

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

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

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

 

As you can see, the call to I2C_MasterTransferNonBlocking() is guarded by a mutex. 

With regard to the above transaction, the following situation can occur: After a first task has finished the first part of above transaction, a second task calls  I2C_RTOS_Transfer() and 2C_MasterTransferNonBlocking().

I don't know if it is valid to interrupt an I2C transaction with a first target, start a second I2C transaction with a second target and later continue the transaction with the first target. I could not find an explicit hint in the I2C specification. It is my understanding that an ongoing I2C transaction should be finished before a next one is started.

To avoid this situation of interrupted I2C transactions (however unlikely it may be), one would need to add another guard to prevent I2C transactions to "intermingle".

Could anyone please comment on these issue?

 

0 Kudos
Reply
2 Replies

3,981 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I suppose that you can call the low level i2c function to implement the commands: I2C slave address plus write + sub-address + repeated start + I2C slave address plus read + reading data with the I2C_MasterTransferBlocking() api function.

Because this is a blocking function, it uses polling mode instead of interrupt mode, I suppose that you can disable interrupt before calling the function, after the api function has complete, then enable interrupt when you call it in a realtime OS.

The following function is in fsl_i2c.c in SDK driver for LPC family.

BTW, can you tell us the part number you are using? for example LPC, Kinetis or i.mxrt

Hope it can help you

BR

XiangJun Roing

 

/*!
* brief Performs a master polling transfer on the I2C bus.
*
* note The API does not return until the transfer succeeds or fails due
* to arbitration lost or receiving a NAK.
*
* param base I2C peripheral base address.
* param xfer Pointer to the transfer structure.
* retval kStatus_Success Successfully complete the data transmission.
* retval kStatus_I2C_Busy Previous transmission still not finished.
* retval kStatus_I2C_Timeout Transfer error, wait signal timeout.
* retval kStatus_I2C_ArbitrationLost Transfer error, arbitration lost.
* retval kStataus_I2C_Nak Transfer error, receive NAK during transfer.
*/
status_t I2C_MasterTransferBlocking(I2C_Type *base, i2c_master_transfer_t *xfer)
{
status_t result = kStatus_Success;
uint32_t subaddress;
uint8_t subaddrBuf[4];
int i;

assert(xfer != NULL);

/* If repeated start is requested, send repeated start. */
if (0U == (xfer->flags & (uint32_t)kI2C_TransferNoStartFlag))
{
if ((xfer->subaddressSize) != 0U)
{
result = I2C_MasterStart(base, xfer->slaveAddress, kI2C_Write);
if (result == kStatus_Success)
{
/* Prepare subaddress transmit buffer, most significant byte is stored at the lowest address */
subaddress = xfer->subaddress;
for (i = (int)xfer->subaddressSize - 1; i >= 0; i--)
{
subaddrBuf[i] = (uint8_t)subaddress & 0xffU;
subaddress >>= 8;
}
/* Send subaddress. */
result =
I2C_MasterWriteBlocking(base, subaddrBuf, xfer->subaddressSize, (uint32_t)kI2C_TransferNoStopFlag);
if ((result == kStatus_Success) && (xfer->direction == kI2C_Read))
{
result = I2C_MasterRepeatedStart(base, xfer->slaveAddress, xfer->direction);
}
}
}
else if ((xfer->flags & (uint32_t)kI2C_TransferRepeatedStartFlag) != 0U)
{
result = I2C_MasterRepeatedStart(base, xfer->slaveAddress, xfer->direction);
}
else
{
result = I2C_MasterStart(base, xfer->slaveAddress, xfer->direction);
}
}

if (result == kStatus_Success)
{
if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0U))
{
/* Transmit data. */
result = I2C_MasterWriteBlocking(base, xfer->data, xfer->dataSize, xfer->flags);
}
else
{
if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0U))
{
/* Receive Data. */
result = I2C_MasterReadBlocking(base, xfer->data, xfer->dataSize, xfer->flags);
}
}
}

if (result == kStatus_I2C_Nak)
{
(void)I2C_MasterStop(base);
}

return result;
}

0 Kudos
Reply

3,975 Views
danielholala
Senior Contributor II

Dear @xiangjun_rong ,

Thank you for your response.

When you have a realtime OS at hand, I don't recommend to use polling or disabling interrupts while polling.

Rather, I ditched the FreeRTOS driver fsl_i2c_freertos in favor of the CMSIS based driver fsl_i2c_cmsis. That is the API which Peripheral Tool generates code for. Further, I added resource locks and task notifications via FreeRTOS primitives. That worked for me.

Best regards,
Daniel

PS: Pro Tipp - If you want to add code samples to your posting, readability is greatly improved when you use the "Insert/Edit code sample" function, see screenshot below. 

danielholala_0-1644479106126.png

PPS: I think that it is not helpful to add a code sample if you just want to point to a certain function in a certain file and do not discuss the code you are presenting.