AnsweredAssumed Answered

How can I read variable length message w/ I2C?

Question asked by Kevin Luty on Feb 9, 2017
Latest reply on Feb 10, 2017 by Kevin Luty

I'm using a K22, which is acting as a slave, on a single master bus, and the K22 is the only slave device on the bus.  I'm using IAR EWARM w/ KSDK v1.3 and MQX.  I'm using the non-blocking I2C Send/Receive methods.

 

The requirement I have is that my K22 should be able to talk to 2 different devices, and should have knowledge of what device its talking to.  To do that, the first step is reading the system information from the devices and taking appropriate action based on which device was detected.  Here is the catch: ProductA's system information message is 5 bytes long, ProductB's system information message is 12 bytes long.

 

So what I do is, attempt to read 12 bytes and implement my own timeout of 2 seconds.  If I get 12 bytes back, great, we know its ProductB.  However, if the receive happens to timeout after 2 seconds, then I check to see if 5 bytes were read (7 bytes remaining left to be read), and if so, we know its ProductA.

 

Here's pseudocode to give a rough idea of what I'm doing:

bool ReadSysInfo(uint8_t rxBuff, uint32_t * numBytesRemaining)
{
    bool result = true;
    I2C_Slave_SendData(txBuff) //Send data to the master, who then parses this data and responds with the system status information
    timeout = OSA_TimeGetMsec() + I2C_RX_TX_TIMEOUT;
    I2C_Slave_ReceiveData(rxBuffer, 12) //Receive 12 bytes
    do
    {
        currentTime = OSA_TimeGetMsec();;
        status = I2C_DRV_SlaveGetReceiveStatus(I2C_RTOS_SLAVE_INSTANCE, numBytesRemaining); 
    } while(status != kStatus_I2C_Success && timeout > currTimeMs);

    if(timeout <= currTimeMs)
    {
        result = false;
    }

    return result;
}

uint8_t rxBuff[12];
uint32_t nbr = 0; //number of remaining bytes left to be read
ProductType product;
bool result = ReadSysInfo(rxBuff, &nbr);
if(result)
{
    product = ProductB;
}
else if(nbr == 7) //If we still have 7 bytes left to receive, then that means we read 5 bytes, which is the number of bytes ProductA will respond with
{
    product = ProductA;
}

 

The issue is that, if I follow this method, and I'm talking to ProductA type, because I don't receive 12 bytes (and I only receive 5), the busy flag is held high and all following I2C communications fail because the I2C lines are pulled low.  If I change the call to read 5 bytes instead of 12, the busy bit is cleared after the STOP signal is received and everything works as expected.

 

My question is how should I properly handle this kind of issue?  Is there something in the SDK I can call that will force the I2C module to stop pulling the line low?  Any suggestion is appreciated.

Outcomes