This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

F28M36P63C2: C2000 I2C Master-Receiver mode timing out.

Part Number: F28M36P63C2


I am writing for the C2000 I2C bus and having some issues in the Master-Receiver operation mode. I am using polling instead of an interrupt-based method.

Every few minutes my I2C communication times out when receiving data from a slave device.  It times out in the highlighted area in the code below, with the I2caRegs.I2CSTR.all == 0x2430 (NACKSNT, XSMT, SCD,XRDY). I am not sure if I programed the registers correctly when switching from Master-Transmitter to Master-Receiver modes. I don't see any NACKs from slave devices. I do not experience any issues when I am just in Master-Transmiter mode (code not shown).

The timer is clocked at 150MHZ, so 200,000 cycles is a 133 ms timeout, which is very long since the largest read is 6-bytes.

void I2C_Read(Uint8 addr, Uint16 regPtr, Uint16 numBytes, Uint16 * data, enum I2C_DEF_ERROR *err, bool msbFirst, Uint32 timeoutMicroSecs)
{
    *err = I2C_OK;
    i2cMutex.Lock();

    DINT;
    Uint32 lo = CtoMIpcRegs.MIPCCOUNTERL;
    Uint32 hi = CtoMIpcRegs.MIPCCOUNTERH;
    EINT;
    Uint32 timed_out = 0;

    // The Master bit will self clear, so it is a good thing to wait on to ensure
    // the bus is free and ready for a new Master
    while(I2caRegs.I2CMDR.bit.MST && !(timed_out=I2C_TimedOut(lo, hi, 20000000))) {};

    if(timed_out)
    {
        DbgInfo("Got Timed out in Read TX 1. Status: 0x%x", I2caRegs.I2CSTR.all);
        *err = I2C_ERROR_TIMEOUT;
        i2cMutex.Unlock();
        return;
    }

    //I2caRegs.I2CMDR.bit.IRS = 0;      // this is needed sometimes, but I would prefer not to reset it all the time

    I2caRegs.I2CCNT = 1;                // In non-repeat mode (RM = 0), this dictates the number of bytes to send. Just 1 for reading (the register)
    I2caRegs.I2CSAR = addr;             // The address of the slave device
    I2caRegs.I2CDXR = regPtr;           // The first data byte to transmit, which is the remote register to address
    I2caRegs.I2CMDR.bit.TRX = 1;        // Transmitting data to the slave
    I2caRegs.I2CMDR.bit.MST = 1;        // Set the master bit to start the SCL line.
    I2caRegs.I2CMDR.bit.FREE = 1;       // Allow I2C to run while in a breakpoint
    I2caRegs.I2CMDR.bit.STP = 0;        // Since we are receiving data from the slave, we want to do a repeat-start so disable the Stop bit
    I2caRegs.I2CMDR.bit.STT = 1;        // Start a new transaction
    I2caRegs.I2CMDR.bit.IRS = 1;        // Pull the I2C out of reset

    // Wait for the registers to be free before starting the read phase of the transaction
    while(!I2caRegs.I2CSTR.bit.ARDY && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

    if(timed_out)
    {
        DbgInfo("Got Timed out in Read TX 2. Status: 0x%x", I2caRegs.I2CSTR.all);
        *err = I2C_ERROR_TIMEOUT;
        i2cMutex.Unlock();
        return;
    }

    // Check if we got a No-Acknowledgment
    if(I2caRegs.I2CSTR.bit.NACK)
    {
        DbgInfo("Got NACK in Read TX. Status: 0x%x", I2caRegs.I2CSTR.all);

        I2caRegs.I2CMDR.all = 0;
        //I2caRegs.I2CMDR.bit.STP = 1;    // We need to force a stop condition in this case
        I2caRegs.I2CSTR.bit.NACK = 1;   // Clear the NACK bit (by writing 1 to it)

        *err = I2C_NACK;
        i2cMutex.Unlock();
        return;
    }

    // I2caRegs.I2CMDR.all = 0;
    I2caRegs.I2CCNT = numBytes;         // In non-repeat mode (RM = 0), this dictates the number of bytes to read.
    I2caRegs.I2CMDR.bit.TRX = 0;        // We are the Master-receiver, so turn off the transmit bit
    I2caRegs.I2CMDR.bit.MST = 1;        // Still the master (probably not needed)
    I2caRegs.I2CMDR.bit.FREE = 1;       // Allow I2C to run while in a breakpoint
    I2caRegs.I2CMDR.bit.STP = 1;        // Stop bit will be applied after I2CCNT reaches 0
    I2caRegs.I2CMDR.bit.STT = 1;        // Repeat start for the receiving part of the  transaction
    I2caRegs.I2CMDR.bit.IRS = 1;        // Pull the I2C out of reset (probably not needed)

    Uint16 i(0);

    for(i=0; i < numBytes; i++)
    {
        while(!(I2caRegs.I2CSTR.bit.RRDY || I2caRegs.I2CSTR.bit.ARDY) && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

        if(timed_out)
          {
              DbgInfo("Got Timed out in Read RX 1. Status: 0x%x", I2caRegs.I2CSTR.all);
              *err = I2C_ERROR_TIMEOUT;
              i2cMutex.Unlock();
              return;
          }

        // Check if we got a No-Acknowledgment
        if(I2caRegs.I2CSTR.bit.NACK)
        {
            DbgInfo("Got NACK in Read RX. Status: 0x%x", I2caRegs.I2CSTR.all);

            I2caRegs.I2CMDR.all = 0;
            //I2caRegs.I2CMDR.bit.STP = 1;    // We need to force a stop condition in this case
            I2caRegs.I2CSTR.bit.NACK = 1;   // Clear the NACK bit (by writing 1 to it)

            *err = I2C_NACK;
            break;
        }

        // Make sure the Receive Ready signal was triggered
        while(!I2caRegs.I2CSTR.bit.RRDY && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

        if(timed_out)
                  {
                      DbgInfo("Got Timed out in Read RX 2. Status: 0x%x", I2caRegs.I2CSTR.all);
                      *err = I2C_ERROR_TIMEOUT;
                      i2cMutex.Unlock();
                      return;
                  }

        // Read the next data byte into the buffer, in the appropriate index based on MSB/LSB first.
        __byte((int*)data, (msbFirst) ? numBytes - i - 1 :  i) = I2caRegs.I2CDRR;
    }

    i2cMutex.Unlock();
}

  • Derek,

    I'm sorry to hear that you are having some issues with the I2C. I am not sure which line the program is getting stuck at. Can you copy the line you are referring to in the code snippet you posted? the highlight didn't come through.

    After you set up the I2C, I would not recommend continually writing the configuration bits other than the ones that you need to change. i.e. if you are only moving between master transmit and master receive, you should only need to change the TRX bit.

    Does a module reset (toggling the IRS bit) after timing out resolve the issue?
    Does application hang in the same spot consistently? i.e. after sending the address and but during the master receive phase?

    Thanks,
    Mark
  • Mark,

    Here is the line:

    <span style="background-color:#ffff00;">DbgInfo("Got Timed out in Read RX 1. Status: 0x%x", I2caRegs.I2CSTR.all);</span>

    looks like the highlighting feature doesn't work in the code segments?

    After the timeout, toggling the IRS does work and the application will proceed for a few minutes before another time out occurs (with several thousands I2C transactions in between).

    The application does hang in the same spot every time I have seen so far (a few hundred times as of last count), and the status register is always the same: 0x2430.

    About not changing the CMD register bits, wouldn't I need to change the STP bit from 0->1 to have the repeat start work? Also, since I am enabling the bits in the CMDR register one at a time, is there any ordering I should follow? I saw examples online where people write all the bits to the I2CMDR.all union, but obviously that is not as clear as what is being set.

    Finally, if I clear the ICMDR before the Master-Recieve portion starts (uncommenting the following line)

        I2caRegs.I2CMDR.all = 0;
        I2caRegs.I2CCNT = numBytes;         // In non-repeat mode (RM = 0), this dictates the number of bytes to read.

    I get the bus working with significantly less time outs, but i am wary that is not the correct way to operate, especially with your comment about not writing the configuration bits all the time.

    Derek

  • Hi Derek,

    I don't see any issues with your code at the moment. I don't have any reason to believe that rewriting the configuration bits is wrong, and it shouldn't matter if you write to them all at once like the examples or separately.

    Does it make any difference if you do I2caRegs.I2CMDR.bit.IRS = 0 instead of I2caRegs.I2CMDR.all = 0?

    Is there a typical value of numBytes when you get the timeout? Is it timing out on the first byte or does it vary?

    Thanks,
    Whitney
  • Hi Whitney,

    Here is my current code that is running better, but I still get the occasional timeout (but much lest frequently, only 4 timeouts over 48 hours). The big change was I moved the setting of the I2CDXR until after I check for the XRDY and NACK. I also moved to the write all registers at once, just to eliminate possible places of errors.

    It occurs when reading just 2 bytes, though I don't record if the timeout happens on the first or second byte. When I look at the data buffer, it does contain non-zero data, so I assume it is being populated at least once, but cannot definitively state that.

    void I2C_Read(Uint8 addr, Uint16 regPtr, Uint16 numBytes, Uint16 * data, enum I2C_DEF_ERROR *err, bool msbFirst, Uint32 timeoutMicroSecs)
    {
    *err = I2C_OK;

    DINT;
    Uint32 lo = CtoMIpcRegs.MIPCCOUNTERL;
    Uint32 hi = CtoMIpcRegs.MIPCCOUNTERH;
    EINT;
    Uint32 timed_out = 0;

    // The Master bit will self clear, so it is a good thing to wait on to ensure
    // the bus is free and ready for a new Master
    while(I2caRegs.I2CMDR.bit.MST && !(timed_out=I2C_TimedOut(lo, hi, 20000000))) {};

    if(timed_out)
    {
    DbgInfo("Got Timed out in Read TX 1. Status: 0x%x", I2caRegs.I2CSTR.all);
    *err = I2C_ERROR_TIMEOUT;
    Reset_Bus();
    //i2cMutex.Unlock();
    return;
    }

    i2cMutex.Lock();

    //I2caRegs.I2CMDR.bit.IRS = 0; // this is needed sometimes, but I would prefer not to reset it all the time

    I2caRegs.I2CCNT = 1; // In non-repeat mode (RM = 0), this dictates the number of bytes to send. Just 1 for reading (the register)
    I2caRegs.I2CSAR = addr; // The address of the slave device

    // I2caRegs.I2CMDR.bit.TRX = 1; // Transmitting data to the slave
    // I2caRegs.I2CMDR.bit.MST = 1; // Set the master bit to start the SCL line.
    // I2caRegs.I2CMDR.bit.FREE = 1; // Allow I2C to run while in a breakpoint
    // I2caRegs.I2CMDR.bit.STP = 0; // Since we are receiving data from the slave, we want to do a repeat-start so disable the Stop bit
    // I2caRegs.I2CMDR.bit.STT = 1; // Start a new transaction
    // I2caRegs.I2CMDR.bit.IRS = 1; // Pull the I2C out of reset
    I2caRegs.I2CMDR.all = 0x6620; // This does the commented lines above in one go

    while(!(I2caRegs.I2CSTR.bit.XRDY || I2caRegs.I2CSTR.bit.ARDY) && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

    // Check if we got a No-Acknowledgment
    if(I2caRegs.I2CSTR.bit.NACK)
    {
    DbgInfo("Got NACK in Read SAR TX. Status: 0x%x", I2caRegs.I2CSTR.all);

    I2caRegs.I2CMDR.all = 0;
    //I2caRegs.I2CMDR.bit.STP = 1; // We need to force a stop condition in this case
    I2caRegs.I2CSTR.bit.NACK = 1; // Clear the NACK bit (by writing 1 to it)

    *err = I2C_NACK;
    i2cMutex.Unlock();
    return;
    }

    if(timed_out)
    {
    DbgInfo("Got Timed out in Read TX 2. Status: 0x%x", I2caRegs.I2CSTR.all);
    *err = I2C_ERROR_TIMEOUT;
    I2caRegs.I2CMDR.all = 0;
    i2cMutex.Unlock();
    return;
    }

    I2caRegs.I2CDXR = regPtr; // The first data byte to transmit, which is the remote register to address

    // Wait for the registers to be free before starting the read phase of the transaction
    while(!I2caRegs.I2CSTR.bit.ARDY && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

    if(timed_out)
    {
    DbgInfo("Got Timed out in Read TX 3. Status: 0x%x", I2caRegs.I2CSTR.all);
    *err = I2C_ERROR_TIMEOUT;
    I2caRegs.I2CMDR.all = 0;
    i2cMutex.Unlock();
    return;
    }

    // Check if we got a No-Acknowledgment
    if(I2caRegs.I2CSTR.bit.NACK)
    {
    DbgInfo("Got NACK in Read TX. Status: 0x%x", I2caRegs.I2CSTR.all);

    I2caRegs.I2CMDR.all = 0;
    //I2caRegs.I2CMDR.bit.STP = 1; // We need to force a stop condition in this case
    I2caRegs.I2CSTR.bit.NACK = 1; // Clear the NACK bit (by writing 1 to it)

    *err = I2C_NACK;
    i2cMutex.Unlock();
    return;
    }

    //I2caRegs.I2CMDR.all = 0;
    I2caRegs.I2CCNT = numBytes; // In non-repeat mode (RM = 0), this dictates the number of bytes to read.
    // I2caRegs.I2CMDR.bit.TRX = 0; // We are the Master-receiver, so turn off the transmit bit
    // I2caRegs.I2CMDR.bit.MST = 1; // Still the master (probably not needed)
    // I2caRegs.I2CMDR.bit.FREE = 1; // Allow I2C to run while in a breakpoint
    // I2caRegs.I2CMDR.bit.STP = 1; // Stop bit will be applied after I2CCNT reaches 0
    // I2caRegs.I2CMDR.bit.STT = 1; // Repeat start for the receiving part of the transaction
    // I2caRegs.I2CMDR.bit.IRS = 1; // Pull the I2C out of reset (probably not needed)
    I2caRegs.I2CMDR.all = 0x6C20; // This does the commented lines above in one go

    Uint16 i(0);

    for(i=0; i < numBytes; i++)
    {
    while(!(I2caRegs.I2CSTR.bit.RRDY || I2caRegs.I2CSTR.bit.ARDY) && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

    if(timed_out)
    {
    DbgInfo("Got Timed out in Read RX 1. Status: 0x%x", I2caRegs.I2CSTR.all);
    *err = I2C_ERROR_TIMEOUT;
    I2caRegs.I2CMDR.all = 0;
    break;
    }

    // Check if we got a No-Acknowledgment
    if(I2caRegs.I2CSTR.bit.NACK)
    {
    DbgInfo("Got NACK in Read RX. Status: 0x%x", I2caRegs.I2CSTR.all);

    I2caRegs.I2CMDR.all = 0;
    //I2caRegs.I2CMDR.bit.STP = 1; // We need to force a stop condition in this case
    I2caRegs.I2CSTR.bit.NACK = 1; // Clear the NACK bit (by writing 1 to it)

    *err = I2C_NACK;
    break;
    }

    // Make sure the Receive Ready signal was triggered
    while(!I2caRegs.I2CSTR.bit.RRDY && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));

    if(timed_out)
    {
    DbgInfo("Got Timed out in Read RX 2. Status: 0x%x", I2caRegs.I2CSTR.all);
    *err = I2C_ERROR_TIMEOUT;
    I2caRegs.I2CMDR.all = 0;
    break;
    }

    // Read the next data byte into the buffer, in the appropriate index based on MSB/LSB first.
    __byte((int*)data, (msbFirst) ? numBytes - i - 1 : i) = I2caRegs.I2CDRR;
    }

    i2cMutex.Unlock();
    }
  • Hey Derek,
    Sorry for the delay in replying. Have you been able to figure out this issue?

    -Mark
  • Mark,

     The latest post I made definitely made things better, but I still get the occasional timeout. I could live with that, since I just try the transaction again a few milliseconds later and that usually works. However, I still have occurrences after a timeout where the bus is completely locked. I try resetting the I2C module and wait for the MST flag, but it never self-clears. So I assume an external device is holding the SDA low.

    I am not able to reliably release the SDA line in this situtation, without a power reboot. I'm still investigating how best to do this in an automatic way.

    Derek

  • Derek,

    One thing to remember about this I2C is that there really is no built in error detection other than what comes in the protocol itself. If there are some timeouts, you could potentially use a watchdog that is reset whenever there is I2C activity. This would help the C2000 I2C as you could perform a module reset, but I am not sure how you want to approach the rest of the network.

    It looks like your next step is to find the offending I2C module holding the line low and try to figure out how that gets hung up in the first place.

    -Mark