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.

TMS320F280049: I2C Between 3 TMS320F280049 (master + 2 slaves): Occasionally experiencing NAKs, lost arbitration, and message corruption.

Part Number: TMS320F280049

Have experimented with various GPIO configs:

Original (from examples):

    GPIO_setPinConfig(DEVICE_GPIO_CFG_SDAA);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SDAA, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SDAA, GPIO_QUAL_ASYNC);
            
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCLA);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCLA, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCLA, GPIO_QUAL_ASYNC);

Modified:

    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCLA);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCLA, GPIO_PIN_TYPE_OD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCLA, GPIO_QUAL_6SAMPLE);
            
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SDAA);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SDAA, GPIO_PIN_TYPE_OD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SDAA, GPIO_QUAL_6SAMPLE);

The original setup yields NAK rate: 0.6%, arbitration lost rate: 0.014%, corrupted messages rate: ?

The modified setup performs considerably better - NAK rate: 0.0015%, arbitration lost rate: 0.011%, corrupted messages rate: 0.014%

The slaves never experience NAKs when trying to send to the master.

Does it make sense that the modified GPIO configs should perform better?  Is there a better combination of configs to use?  Anything else I should consider (software-wise) to improve performance?  If a hardware filter is applied to SDA and SCL, would the expected performance be significantly better than indicated above?

FWIW my code is as follows:

I2C Initialization:

    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCLA);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCLA, GPIO_PIN_TYPE_OD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCLA, GPIO_QUAL_6SAMPLE);
            
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SDAA);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SDAA, GPIO_PIN_TYPE_OD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SDAA, GPIO_QUAL_6SAMPLE);

    I2C_disableModule(I2CA_BASE);

    #ifdef I2C_MASTER
    I2C_initMaster(I2CA_BASE, DEVICE_SYSCLK_FREQ, 400000, I2C_DUTYCYCLE_50);
    #endif

    I2C_setAddressMode(I2CA_BASE, I2C_ADDR_MODE_7BITS);
    I2C_setBitCount(I2CA_BASE, I2C_BITCOUNT_8);

    I2C_disableLoopback(I2CA_BASE);

    #ifdef I2C_SLAVE
    I2C_setOwnSlaveAddress(I2CA_BASE, I2C_ADDRESS);
    #endif

    I2C_setEmulationMode(I2CA_BASE, I2C_EMULATION_STOP_SCL_LOW); // When debugger hits a breakpoint, SCL will be held low (indefinitely) to pause I2C activity.

    // Interrupt when the Tx FIFO has 4 bytes (or less) left to transmit, OR...
    // when the Rx FIFO has at least 1 byte of received data.
    I2C_setFIFOInterruptLevel(I2CA_BASE, I2C_FIFO_TX4, I2C_FIFO_RX1);
    
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP8);
    Interrupt_register(INT_I2CA, &i2cAISR);
    Interrupt_enable(INT_I2CA);
    Interrupt_register(INT_I2CA_FIFO, &i2cAFIFOISR);
    Interrupt_enable(INT_I2CA_FIFO);

Message Transmission:

    Master & Slave, Tx & Rx:
    
      I2C_disableModule(I2CA_BASE);

      I2C_disableFIFO(I2CA_BASE);
      I2C_enableFIFO(I2CA_BASE);

      I2C_disableInterrupt(I2CA_BASE, (I2C_INT_RXFF | I2C_INT_TXFF | I2C_INT_STOP_CONDITION |
                                       I2C_INT_ARB_LOST | I2C_INT_NO_ACK | I2C_INT_REG_ACCESS_RDY |
                                       I2C_INT_RX_DATA_RDY | I2C_INT_TX_DATA_RDY | I2C_INT_ADDR_SLAVE));
      I2C_clearInterruptStatus(I2CA_BASE, (I2C_INT_RXFF | I2C_INT_TXFF | I2C_INT_STOP_CONDITION |
                                           I2C_INT_ARB_LOST | I2C_INT_NO_ACK | I2C_INT_REG_ACCESS_RDY |
                                           I2C_INT_RX_DATA_RDY | I2C_INT_TX_DATA_RDY | I2C_INT_ADDR_SLAVE));   
    
    Master Tx:

      I2C_enableInterrupt(I2CA_BASE, (I2C_INT_TXFF | I2C_INT_STOP_CONDITION | I2C_INT_ARB_LOST | I2C_INT_NO_ACK));

      I2C_setSlaveAddress(I2CA_BASE, i2cControl.slaveAddress);
      I2C_setDataCount(I2CA_BASE, i2cControl.nBytesToTx);
      I2C_setConfig(I2CA_BASE, (I2C_MASTER_SEND_MODE | I2C_START_BYTE_MODE));
      I2C_enableModule(I2CA_BASE);
      I2C_sendStartCondition(I2CA_BASE);
      I2C_sendStopCondition(I2CA_BASE);
       
    Master Rx:
    
      I2C_enableInterrupt(I2CA_BASE, (I2C_INT_RXFF | I2C_INT_STOP_CONDITION | I2C_INT_ARB_LOST | I2C_INT_NO_ACK));

      I2C_setSlaveAddress(I2CA_BASE, i2cControl.slaveAddress);
      I2C_setDataCount(I2CA_BASE, i2cControl.nBytesToRx);
      I2C_setConfig(I2CA_BASE, (I2C_MASTER_RECEIVE_MODE | I2C_START_BYTE_MODE));
      I2C_enableModule(I2CA_BASE);
      I2C_sendStartCondition(I2CA_BASE);
      I2C_sendStopCondition(I2CA_BASE);


    Slave Tx:    
    
      I2C_enableInterrupt(I2CA_BASE, (I2C_INT_TXFF | I2C_INT_STOP_CONDITION | I2C_INT_NO_ACK));
      I2C_setConfig(I2CA_BASE, I2C_SLAVE_SEND_MODE);
      I2C_enableModule(I2CA_BASE);

        
    Slave Rx:    
    
      I2C_enableInterrupt(I2CA_BASE, (I2C_INT_RXFF | I2C_INT_STOP_CONDITION | I2C_INT_NO_ACK));
      I2C_setConfig(I2CA_BASE, I2C_SLAVE_RECEIVE_MODE);
      I2C_enableModule(I2CA_BASE);


Basic Interrrupt Service Routine:

__interrupt void i2cAISR(void)
{
    I2C_InterruptSource i2cInterruptSource;

    i2cInterruptSource = I2C_getInterruptSource(I2CA_BASE);

    if (i2cInterruptSource == I2C_INTSRC_STOP_CONDITION)
    {
        i2cControl.bStopRxd = true;

        if (i2cControl.bTxInProgress && (i2cControl.nTxdBytes == i2cControl.nBytesToTx))
        {
            i2cControl.bTxInProgress = false;
        }
        else if (i2cControl.bRxInProgress && (i2cControl.nRxdBytes == i2cControl.nBytesToRx))
        {
            i2cControl.bRxInProgress = false;
        }       
    }
    else if (i2cInterruptSource == I2C_INTSRC_NO_ACK)
    {
        Comms.nI2cNaks++; // (DEBUG)
    }
    #ifdef I2C_MASTER
    else if (i2cInterruptSource == I2C_INTSRC_ARB_LOST)
    {
        Comms.nI2cArbitrationLosts++; // (DEBUG)
    }
    #endif

    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP8);
}

FIFO Interrrupt Service Routine:

__interrupt void i2cAFIFOISR(void)
{
    uint32_t i2cInterruptStatus;

    i2cInterruptStatus = I2C_getInterruptStatus(I2CA_BASE);

    if ((i2cInterruptStatus & I2C_INT_RXFF) != 0)
    {
        if (i2cControl.bRxInProgress)
        {
            if (i2cControl.nRxdBytes < i2cControl.nBytesToRx)
            {
                I2C_RxFIFOLevel i2cRxFIFOLevel = I2C_getRxFIFOStatus(I2CA_BASE);

                uint16_t nAddlRxdBytes = mapRxFIFOLevelToCount(i2cRxFIFOLevel);

                while (nAddlRxdBytes && (i2cControl.nRxdBytes < i2cControl.nBytesToRx))
                {
                    uint16_t newByte = I2C_getData(I2CA_BASE);
                    i2cControl.RxBuffer[i2cControl.nRxdBytes] = newByte;
                    i2cControl.nRxdBytes++;
                    nAddlRxdBytes--;
                }

                if (i2cControl.bStopRxd && (i2cControl.nRxdBytes == i2cControl.nBytesToRx))
                {
                    i2cControl.bRxInProgress = false;
                }
            }

            I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_RXFF);
        }
    }

    if ((i2cInterruptStatus & I2C_INT_TXFF) != 0)
    {
        if (i2cControl.bTxInProgress)
        {
            if (i2cControl.nTxdBytes < i2cControl.nBytesToTx)
            {
                I2C_TxFIFOLevel i2cTxFIFOLevel = I2C_getTxFIFOStatus(I2CA_BASE);

                uint16_t nBytesAvailableFIFOCapacity = I2C_FIFO_SIZE - mapTxFIFOLevelToCount(i2cTxFIFOLevel);

                uint16_t nRemainingBytes = i2cControl.nBytesToTx - i2cControl.nTxdBytes;
                uint16_t nAddlTxBytes;

                if (nRemainingBytes > nBytesAvailableFIFOCapacity)
                {
                    nAddlTxBytes = nBytesAvailableFIFOCapacity;
                }
                else
                {
                    nAddlTxBytes = nRemainingBytes;
                }

                while (nAddlTxBytes)
                {
                    I2C_putData(I2CA_BASE, i2cControl.TxBuffer[i2cControl.nTxdBytes]);
                    i2cControl.nTxdBytes++;
                    nAddlTxBytes--;
                }

                I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_TXFF);
            }
            // If there are NOT more bytes to Tx...
            else
            {
                if (i2cControl.bStopRxd)
                {
                    i2cControl.bTxInProgress = false;
                }
            }
        }
    }

    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP8);
}

  • Hi Michael,

    You posted a lot of code here. Please only do so in the future if necessary and utilize the "Insert Code" feature or attach it as a file. This will make the post look a lot cleaner and easier to follow =).

    Are you using external pull-up resistors for your I2C bus? I'd suggest doing that if you're not already (in case you're using internal pullups currently) as it might improve the communication.

    As for your observations, I could see the modified configuration improving your comms if the bus initially has some noise on it. I'd imagine this improvement, if any, is due to adding QUAL rather than making the pins Open-drain, since setting pins as I2C signals should make them Open-drain by itself. Adding QUAL to I2C signals however is not advised, since the I2C module performs its own noise filtering internally. QUAL adds extra filtering, but can increase latency or possibly corrupt the bus if the I2C spec isn't met. Asynchronous is the recommended configuration.

    It's better to try and make your I2C signals as clean as possible. There are a number of resources online that explain best practices and ways to decrease noise on the I2C bus (i.e. describing how to trace signals on a PCB, or additional HW/filtering that can be added).

    Software wise, you can implement a CRC to verify data integrity. Adding some functionality to your application that will 'fix' the bus if it gets stuck or confused would be good too (i.e. if SDA or SCL gets stuck low resetting the bus or bit banging may be nice to implement).

    Hope this helps,

    Kevin

  • Thanks Kevin -

    Yes we have external pullups (4.7K) - is the GPIO_PIN_TYPE_PULLUP configuration preferred regardless?

    I have added code to detect (and compensate for) errors and "fix" the bus if necessary. Basically if an error is detected, the I2C module and FIFO are disabled and re-enabled. Errors are detected through message transmission timeout as well as via I2C module status/events. Have seen a slave hold the bus in the past - the disable/re-enable (within the slave) causes the slave to release the bus. Is there anything else that can/should be done to reset the bus?

    I also see a significant performance improvement in terms of decreased NAKs if a short delay (200us) is inserted between a master's transmission and a slave's response to that transmission (i.e. master Tx then Rx, slave Rx then Tx). Is this due to the slave having to reconfigure from Rx to Tx and the delay allows more time for it to do so? I would have thought the slave hardware would hold the bus ("stretch the clock") until the software was ready to Tx but apparently not?
  • Hi Michael,

    I'd expect that disabling the pull-up would be better since you're using an external pull-up, using the config GPIO_PIN_TYPE_STD instead.

    Are you using F28004x's as slave devices as well? If not, what are your two slave devices if you don't mind sharing?

    What you described should be good, so long as your slave and master devices aren't getting confused after any reset occurrence. Some slave devices do not have the ability to reset like you described and 'fixing' the I2C bus can be more complicated.

    I see you're running at 400KHz fast mode. If the slave device meets the Fast mode spec I would think clock stretching would be sufficient and this delay wouldn't be needed. Probing SCL and SDA with an oscilloscope and catching an occurrence of this issue might give a better idea of what's happening. Feel free to provide screenshots of the waveforms if you'd like me to take a look.

    Best,
    Kevin
  • Thanks Kevin -

    Yes the slaves are TMS320F280049c as well.  Perhaps this is an unusual configuration.  Regardless, I'm interested in any insight you may have concerning the effect of including a 200us delay between a master Tx and a master Rx to the same slave.  The delay drastically improves the NAK rate - had seen zero NAKs after running over 24 hours (43 million messages).  My intuition is that the delay provides more time for the slave to switch from Rx to Tx.  But as I mentioned, I would have thought this unnecessary assuming the I2C hardware of the slave is capable of "stretching" the clock.

    When I've seen a NAK on the logic analyzer, the transmission from the master doesn't look unusual - the slave simply fails to pull SDA low during the ACK clock period.  It must only occur during the master's attempt to Rx, since I haven't seen the NAK with a delay inserted between the Tx and Rx, and of course the delay would only change the master's Rx, not its Tx.  I would probably get a better picture of the situation with an analog capture - will try to set this up but suspect the triggering will be difficult. 

    Mike

  • Hi Mike,

    OK, that is good information to know. I don't know for certain why the delay is fixing this NACK issue right now...

    I'm a little confused as to where you're seeing the NACK. Is a NACK being seen after the master's initial transmit to the slave (i.e. slave device NACKing)? Or is the NACK being seen when the slave is transmitting information back to the master receiver (i.e. master device NACKing)?

    Have you tried testing this with a lower clock speed, say 100KHz? I think this would be good to try if you have not already.

    Another thing that would be good to try is placing 60-75 ohm series termination resistors on the SDA and SCL signals, locating them close to the SCL/SDA terminals. The pull-up resistors should be located near the SCL/SDA terminals as well. See the image below:

    Best,

    Kevin

  • Hi Mike,

    Haven't heard from you in a week or so. Are you needing any additional support on this issue or can we go ahead and close this thread?

    Best,
    Kevin