How to send or receive more than 256 byte on i2c in a single transfer using SDK API

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

How to send or receive more than 256 byte on i2c in a single transfer using SDK API

5,443 Views
mastupristi
Senior Contributor I

Despite what is written (or rather not written) in the documentation of the functions LPI2C_MasterReceive() and LPI2C_MasterSend(), it is not possible to transfer more than 256 bytes at a time.

How to send or receive more than 256 byte in a single transfer using SDK API?

If not, how should the APIs be patched to allow this?

best regards

Max

Labels (1)
0 Kudos
10 Replies

5,117 Views
benedek_kupper
Contributor III

The reference manual states that

Multiple transmit and receive commands can be inserted between the START condition and STOP conditon [...] The receive data command and the receive data and discard commands can be interleaved [...] The LPI2C master will automatically transmit a NACK on the last byte of a receive datacommand unless the next command in the FIFO is also a receive data command.

so the hardware itself doesn't limit the read transfer size, but just requires the software to queue multiple read data commands. This is where the SDK implementation fails, as it doesn't implement this interleaving of read data segments. @kerryzhou when could we expect to see a fix for this in the SDK?

0 Kudos

5,110 Views
benedek_kupper
Contributor III

Here's what I've come up with, I haven't tested it thoroughly though, so use it at your own risk. It has been serving our project well. (This patch is for IMXRT1010 SDK):

 

diff --git a/drivers/fsl_lpi2c.c b/drivers/fsl_lpi2c.c
index 8a046dc..f5e148f 100644
--- a/drivers/fsl_lpi2c.c
+++ b/drivers/fsl_lpi2c.c
@@ -1093,15 +1093,14 @@ static status_t LPI2C_RunTransferStateMachine(LPI2C_Type *base, lpi2c_master_han
                     break;
                 }
 
-                base->MTDR = (uint32_t)kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1U);
+                /* HACK: consider lengths > 256 */
+                base->MTDR = (uint32_t)kRxDataCmd | LPI2C_MTDR_DATA(MIN(handle->remainingBytes, I2C_MAX_READ_LENGTH) - 1U);
 
                 /* Move to transfer state. */
                 handle->state = (uint8_t)kTransferDataState;
-                if (xfer->direction == kLPI2C_Read)
-                {
-                    /* Disable TX interrupt */
-                    LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterTxReadyFlag);
-                }
+
+                /* Disable TX interrupt */
+                LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterTxReadyFlag);
                 break;
 
             case (uint8_t)kTransferDataState:
@@ -1140,6 +1139,15 @@ static status_t LPI2C_RunTransferStateMachine(LPI2C_Type *base, lpi2c_master_han
                     }
                     handle->state = (uint8_t)kStopState;
                 }
+                /* HACK: issue new read command each time a full READ LENGTH was received */
+                else if ((xfer->direction == kLPI2C_Read) &&
+                        (((xfer->dataSize - handle->remainingBytes) % I2C_MAX_READ_LENGTH) == 0))
+                {
+                    handle->state = (uint8_t)kIssueReadCommandState;
+
+                    /* Enable TX interrupt for issuing read command */
+                    LPI2C_MasterEnableInterrupts(base, (uint32_t)kLPI2C_MasterTxReadyFlag);
+                }
                 break;
 
             case (uint8_t)kStopState:
@@ -1240,8 +1248,8 @@ static void LPI2C_InitTransferStateMachine(lpi2c_master_handle_t *handle)
                                   (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read);
             }
 
-            /* Read command. */
-            cmd[cmdCount++] = (uint16_t)((uint32_t)kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1U));
+            /* Read command. HACK: consider lengths > 256 */
+            cmd[cmdCount++] = (uint16_t)((uint32_t)kRxDataCmd | LPI2C_MTDR_DATA(MIN(xfer->dataSize, I2C_MAX_READ_LENGTH) - 1U));
         }
 
         /* Set up state machine for transferring the commands. */
diff --git a/drivers/fsl_lpi2c.h b/drivers/fsl_lpi2c.h
index 130fc96..8233e91 100644
--- a/drivers/fsl_lpi2c.h
+++ b/drivers/fsl_lpi2c.h
@@ -32,6 +32,8 @@
 #define I2C_RETRY_TIMES 0U /* Define to zero means keep waiting until the flag is assert/deassert. */
 #endif
 
+#define I2C_MAX_READ_LENGTH     ((LPI2C_MTDR_DATA_MASK >> LPI2C_MTDR_DATA_SHIFT) + 1U)
+
 /*! @brief LPI2C status return codes. */
 enum
 {

 

0 Kudos

5,177 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Massimiliano Cialdi ,

  After my test, the master can send more than 256 bytes with LPI2C_MasterSend, but you need to configure the related slave also can receive that data.

   The following , I send out 260bytes.

pastedImage_1.png

pastedImage_2.png

pastedImage_3.png

You can find my I2C bus really sendout the correct data.

Wish it helps you!

Have a great day,
Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

5,177 Views
mastupristi
Senior Contributor I

consider the LPI2C_MasterReceive():

/*!
 * brief Performs a polling receive transfer on the I2C bus.
 *
 * param base  The LPI2C peripheral base address.
 * param rxBuff The pointer to the data to be transferred.
 * param rxSize The length in bytes of the data to be transferred.
 * retval #kStatus_Success Data was received successfully.
 * retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus.
 * retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte.
 * retval #kStatus_LPI2C_FifoError FIFO under run or overrun.
 * retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error.
 * retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout.
 */
status_t LPI2C_MasterReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize)
{
    status_t result;
    uint8_t *buf;

    assert(rxBuff);

    /* Handle empty read. */
    if (!rxSize)
    {
        return kStatus_Success;
    }

    /* Wait until there is room in the command fifo. */
    result = LPI2C_MasterWaitForTxReady(base);
    if (result)
    {
        return result;
    }

    /* Issue command to receive data. */
    base->MTDR = kRxDataCmd | LPI2C_MTDR_DATA(rxSize - 1);

#if LPI2C_WAIT_TIMEOUT
    uint32_t waitTimes = LPI2C_WAIT_TIMEOUT;
#endif

    /* Receive data */
    buf = (uint8_t *)rxBuff;
    while (rxSize--)
    {
        /* Read LPI2C receive fifo register. The register includes a flag to indicate whether */
        /* the FIFO is empty, so we can both get the data and check if we need to keep reading */
        /* using a single register read. */
        uint32_t value;
        do
        {
            /* Check for errors. */
            result = LPI2C_MasterCheckAndClearError(base, LPI2C_MasterGetStatusFlags(base));
            if (result)
            {
                return result;
            }

            value = base->MRDR;
#if LPI2C_WAIT_TIMEOUT
        } while ((value & LPI2C_MRDR_RXEMPTY_MASK) && (--waitTimes));
        if (waitTimes == 0)
        {
            return kStatus_LPI2C_Timeout;
        }
#else
        } while (value & LPI2C_MRDR_RXEMPTY_MASK);
#endif

        *buf++ = value & LPI2C_MRDR_DATA_MASK;
    }

    return kStatus_Success;
}

line 35 contains the MTDR register's assegment using LPI2C_MTDR_DATA macro.

that macro is defined as:

#define LPI2C_MTDR_DATA_MASK                     (0xFFU)
#define LPI2C_MTDR_DATA_SHIFT                    (0U)
#define LPI2C_MTDR_DATA(x)                       (((uint32_t)(((uint32_t)(x)) << LPI2C_MTDR_DATA_SHIFT)) & LPI2C_MTDR_DATA_MASK)

and the RT1051 Ref Man states:

i2c_receive_256.png

and the MTDR register is describer here:

LPi2C_MTDR.png

Since DATA field is 8bit, it cannot store numbers greater than 255, so no more of 256 bytes can be received.

I tried to receive 400 bytes, and the LPI2C_MasterReceive() hangs in an infinite loop (lines 49~66) after receiving 144 bytes, that is exactly 400-256 (in hexadecimal: 0x190 & 0xff = 0x90 = 144).

Please try, and fix it (or better document the APIs)

best regards

Max

0 Kudos

5,177 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Max,

   So, if the API just limit to the 255 bytes, can you add try to add other function code which include the LPI2C_MasterReceive API code, then which can receive the data more than 255 bytes? In factor the LPI2C_MasterReceive is the low level API driver code, if you want to have more function, you can add some other code to realize it.

Wish it helps you!

Have a great day,
Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

5,177 Views
mastupristi
Senior Contributor I

Are you suggesting patching your APIs? Or do you suggest that I use your API differently?

At the moment I've been following the second approach.

I prefer not to patch the API directly, because in case of upgrading to a new SDK I should remember which patches I applied and reapply them. This risks to be a lot of prone error. Then the new SDK could have fixed patch problems already patched by me.

In general, it would be appropriate to document and highlight this type of limitation well. Personally I lost a few hours to understand that the LPI2C_MasterReceive() can not receive more than 256 bytes.

If NXP wants to maintain compatibility between LPI2C_MasterReceive() implementations of all platforms, NXP should write the API so that it behaves as described in the documentation, in all implementations (otherwise the code is no longer portable between platforms but same SDK version).

Or it should write and highlight this limit well.

best regards

Max

5,177 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Massimiliano Cialdi,

  Thanks a lot for your notification and the suggestion.

  I think your suggestion is very good.

  I also recommend you to write your specific API function if it is larger than 255.

  Now, could you share your working API? I will also transfer it to our related department,  and let they add some highlight for the exist API.

  Thank you again.

Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

5,177 Views
mastupristi
Senior Contributor I

I already told you I didn't change the SDK code. I just used the APIs that the SDK provides differently.

static int32_t MemRead(uint16_t addr, void *data, uint32_t len)
{
 int32_t ret = MEM_OP_OK;
 if(0 == len){
  return ret;
 }
 if(kStatus_Success == LPI2C_MasterStart(MEM_I2C_PERIPH, I2C_EEPROM_ADDRESS, kLPI2C_Write)){
  uint8_t a[2];
  a[0] = (addr>>8)&0xff;
  a[1] = (addr>>0)&0xff;
  if(kStatus_Success != LPI2C_MasterSend(MEM_I2C_PERIPH, a, 2)){
   ret = MEM_OP_FAIL;
   goto read_end;
  }
  uint32_t ToGo = len;
  uint32_t chunk;
  uint8_t *pData8 = data;
  uint32_t idxData = 0;
  while(ToGo > 0){
   chunk = ToGo > 256 ? 256 : ToGo;
   if(kStatus_Success != LPI2C_MasterRepeatedStart(MEM_I2C_PERIPH, I2C_EEPROM_ADDRESS, kLPI2C_Read)){
    ret = MEM_OP_FAIL;
    goto read_end;
   }
   if(kStatus_Success != LPI2C_MasterReceive(MEM_I2C_PERIPH, &pData8[idxData], chunk)){
    ret = MEM_OP_FAIL;
    goto read_end;
   }
   ToGo -= chunk;
   idxData += chunk;
  }
 }
read_end:
 if(kStatus_Success == LPI2C_MasterStop(MEM_I2C_PERIPH)){
  if(MEM_OP_FAIL == ret){
   return MEM_OP_FAIL;
  }else{
   return MEM_OP_OK;
  }
 }else{
  return MEM_OP_FAIL;
 }
}

I use this function (which should not be part of the HAL provided by the SDK) to read, via i2c, from an EEProm memory, for example 24C64.

You can see (lines 15~31) that there are more repeated starts for readings of more than 256 bytes.

This works because the device is an EEprom memory, that require the address of memory location that are going to be read.

MemRead.png

There is a set of devices that instead requires the sequential reading of all registers (e.g. some sensors, or complex devices with multiple functions). For these devices the readings always take place from the first register and go in sequence. It is not possible to address the single register, in other words it is not possible to make random readings. At the beginning of each reading transaction they restart from the first register. And the start of the transaction is given by a start or a repeated start.

Assuming the registers are all 8-bit and assuming the device has 300 registers, how can I read all 300 registers in a single transaction? Remember that each start/repeated start condition restarts the reading from the first register. So adopting the solution I adopted for the memory (ie use repeated start) I could read only the first 256 registers and never the following.

best regards

Max

0 Kudos

5,176 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Max,

   Thanks a lot for your updated detail information.

   To the specific EEPROM, just as you assuming it has more than 256, then you need to add the start/repeated start.

  I think, whether you can divide the frame which is more than 256 to two frames, then just add the related address, the first frame Start+address+data(256B), the second frame start+address(orginal address +256)+data(the other register data).  Just don't read it in the one frame. Whether your EEPROM memory can support it or not?

Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

4,722 Views
leopoldgemsch
Contributor I

Dear all,

 

we phased the same issue and my conclusion is, that even auto stop sending is disabled, the Tx FIFO underflow is generated in case the next chunk of read command is generated on last data byte reception.

Therefore we generate the next chunk of read command on second last data byte reception.

0 Kudos