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 Repeated Start Behavior / RM bit / Accurate RX Count

Part Number: TMS320F280049

So i've got my i2c driver 99% functional but i've stumbled over a problem that is bugging me.  

It appears to me that in order to issue repeated starts to a bus, repeat mode has to be on.  I've done a fair amount of testing and no matter what I try, if repeat mode isn't on setting the start bit doesn't generate a repeated start even when we've finished everything we were doing previously.  That's fine, I can live with that.  What I can't live with is erroneous documentation.

Page 2257 of the TRM:

*Ahem...* "Only applicable when the I2C module is a master transmitter".  That is 100% incorrect as far as I can tell.  Setting the RM bit when in master receive, allows a repeated start to be generated on the bus.  If I don't have RM set, I cannot generate a new start while I have control of the bus.  Would you disagree with my above assertion?

Another thing.... The TRM discusses what a repeated start is in section 25.3.5.4 but never really tells the user how to do it.  If my observations that it is only allowed when RM is set are correct, this should be explicitly spelled out in the TRM.  The documentation for the RM bit make it seem like its behavior is only tied to the CNT register and repeated data transmission.

OOOoookkkkk.....now that brings me to the main point of this post.  Assuming my observations are accurate and I'm not doing something dumb in my software, the above raises a small but IMO significant issue.  How do I clock in an accurate number of bytes?  Let's run through an example:

  1. Ack poll the slave - Setup master transmit repeat mode, send a start condition, look for the ADRY bit to be set, test the nack bit to determine if slave is ready
  2. Setup slave read address - Setup master transmit repeat mode, send a repeated start condition, send the address in the memory of the slave device (eeprom), interrupt on ARDY
  3. Read slave data - Configure master receive repeat mode, send a repeated start condition, interrupt on the RXFF, copy data in, when I've read all the bytes requested by the higher level application, send a stop condition

In the above case, because I'm sending the stop in software I don't have deterministic control of the number of bytes that are shifted in.  My two choices are to send it after I've received the last requested byte which results in at least one additional unwanted data byte being shifted in.  Alternatively, I can try to send the stop at n-1 and hope the timing is right that I get my last byte in before stop is sent.

Ultimately I guess my point is the peripheral should be able to send repeated starts when not in repeat mode.  This would allow me to use the count register to shift in an accurate amount of data.  So am I crazy?  Have you ever been able to send a repeated start when RM isn't set?

  • Trey,

    It is good to hear from you. Hope you're doing great.

    Repeated Start can be generated in both repeat mode and non-repeat mode. Please check below example.

    2248.main.c
    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    //###########################################################################
    //
    // FILE: Example_2807xI2C_eeprom.c
    //
    // TITLE: I2C EEPROM Example
    //
    //! \addtogroup cpu01_example_list
    //! <h1>I2C EEPROM Example (i2c_eeprom)</h1>
    //!
    //! This program will write 1-14 words to EEPROM and read them back.
    //! The data written and the EEPROM address written to are contained
    //! in the message structure, I2cMsgOut1. The data read back will be
    //! contained in the message structure I2cMsgIn1.
    //!
    //! \b External \b Connections \n
    //! - This program requires an external I2C EEPROM connected to
    //! the I2C bus at address 0x50.
    //!
    //
    //###########################################################################
    // $TI Release: F2807x Support Library v3.10.00.00 $
    // $Release Date: Tue May 26 17:19:45 IST 2020 $
    // $Copyright:
    // Copyright (C) 2014-2020 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without
    // modification, are permitted provided that the following conditions
    // are met:
    //
    // Redistributions of source code must retain the above copyright
    // notice, this list of conditions and the following disclaimer.
    //
    // Redistributions in binary form must reproduce the above copyright
    // notice, this list of conditions and the following disclaimer in the
    // documentation and/or other materials provided with the
    // distribution.
    //
    // Neither the name of Texas Instruments Incorporated nor the names of
    // its contributors may be used to endorse or promote products derived
    // from this software without specific prior written permission.
    //
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    //
    // Included Files
    //
    #include "F28x_Project.h"
    //
    // Defines
    //
    #define I2C_SLAVE_ADDR 0x50
    #define I2C_NUMBYTES 2
    #define I2C_EEPROM_ADDRESS 0x0000
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    Regards,

    Manoj

  • Hope you're doing great as well Manoj!

    Let me look over this and I'll report back

    :)

    Trey

  • Ok I ran the test case you sent and I will concede that SOMETIMES the state machine will allow you to send a repeated start condition.  I still think there is some weird behavior that at the very least I need clarification on.

    Lets look at my ack polling function:

    bool i2cAckPoll(uint16_t slaveAddr, uint16_t maxNack)
    {
        uint16_t status = I2C_STS_NO_ACK;
        uint16_t nackCount = 0;
    
    
        if(i2cState != I2C_IDLE)
        {
            return false;
        }
    
        if(I2C_getStopConditionStatus(I2CA_BASE))
        {
            return false;
        }
    
        //
        // Setup slave address
        //
        I2C_setSlaveAddress(I2CA_BASE, slaveAddr);
    
        //
        // Check if bus busy
        //
        if(I2C_isBusBusy(I2CA_BASE))
        {
            return false;
        }
    
        i2cState = I2C_ACKPOLL;
        nackCount = 0;
        I2C_setDataCount(I2CA_BASE, 0);
        I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE );
        I2C_disableInterrupt(I2CA_BASE, I2C_INT_ADDR_SLAVE |
                                         I2C_INT_STOP_CONDITION |
                                         I2C_INT_TX_DATA_RDY |
                                         I2C_INT_RX_DATA_RDY |
                                         I2C_INT_REG_ACCESS_RDY |
                                         I2C_INT_NO_ACK |
                                         I2C_INT_ARB_LOST |
                                         I2C_INT_TXFF |
                                         I2C_INT_RXFF);
        I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_ADDR_SLAVE |
                                            I2C_INT_STOP_CONDITION |
                                            I2C_INT_TX_DATA_RDY |
                                            I2C_INT_RX_DATA_RDY |
                                            I2C_INT_REG_ACCESS_RDY |
                                            I2C_INT_NO_ACK |
                                            I2C_INT_ARB_LOST);
    
        while(status & I2C_STS_NO_ACK)
        {
    
            status = 0;
            //
            // Setup dummy transfer
            //
    
            I2C_sendStartCondition(I2CA_BASE);
    
            //wait for slave address to be sent out
            //TODO: add timeout mechanism
            while(!(status & I2C_STS_REG_ACCESS_RDY))
            {
                status = I2C_getStatus(I2CA_BASE) & (~I2C_STS_BYTE_SENT);
    
            }
    
            if(!(status & I2C_STS_NO_ACK))
            {
                i2cState = I2C_IDLE;
    
                return true;
            }
    
            I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_ADDR_SLAVE |
                                                I2C_INT_STOP_CONDITION |
                                                I2C_INT_TX_DATA_RDY |
                                                I2C_INT_RX_DATA_RDY |
                                                I2C_INT_REG_ACCESS_RDY |
                                                I2C_INT_NO_ACK |
                                                I2C_INT_ARB_LOST);
    
            DELAY_US(500);
    
            nackCount++;
            if(nackCount > maxNack)
            {
    
                I2C_sendStopCondition(I2CA_BASE);
                i2cState = I2C_IDLE;
    
                return false;
            }
    
        }
        i2cState = I2C_IDLE;
    
        return true;
    
    
    }

    I've modified this from my working code that uses repeat mode to show the issue.  If you run this with a valid slave attached, the peripheral will address the slave and the slave will ack, but the ARDY bit is never set.  If I change the count and add a dummy byte to be transferred, then ARDY gets set correctly. 

    I use the ARDY bit in order to tell when the slave addressing is complete and the status of the nack bit is valid.  To expand on my above observations.  When I turn on repeat mode, the ARDY bit is set after slave addressing and using it as a time reference to sample the nack bit works perfectly.  I feel like the behavior of this bit should be the same no matter what mode the peripheral is in.

    Trey

  • Trey,

    I've modified this from my working code that uses repeat mode to show the issue.  If you run this with a valid slave attached, the peripheral will address the slave and the slave will ack, but the ARDY bit is never set.  If I change the count and add a dummy byte to be transferred, then ARDY gets set correctly. 

    I use the ARDY bit in order to tell when the slave addressing is complete and the status of the nack bit is valid.  To expand on my above observations.  When I turn on repeat mode, the ARDY bit is set after slave addressing and using it as a time reference to sample the nack bit works perfectly.  I feel like the behavior of this bit should be the same no matter what mode the peripheral is in.

    Your observation is right.

    In non repeat mode (RM = 0), ARDY gets set only when I2CCNT not equal to 0.

    In repeat mode (RM = 1), ARDY gets set even when I2CCNT = 0. In repeat mode, value on I2CCNT doesn't matter. So, it actually sets ARDY. That is just the way it has been implemented.

    Regards.

    Manoj

  • Ok, great!  I think we're getting somewhere.  First things first, I feel like this difference in behavior should be documented in the TRM.  Wouldn't you agree? 

    With the above observation established, this would lead me to believe there is no good way to do 0 byte transmits for the purpose of ACK polling EXCEPT for repeat mode.  Would you agree with that?  If so, given how ack polling is commonly used for EEPROM interaction don't you feel this should be documented in the TRM?

    I'm going to continue going through my code that is working with repeat mode and change it back to non-repeat mode and report my findings.  I still feel like there was some additional undocumented behavior that should be in the TRMs.

    Thanks Manoj!

    Trey

  • Trey,

    I agree this difference in behavior needs to be documented and I have already filed a JIRA ticket for it.

    Regards,

    Manoj

  • Good morning!  I think I found another statement in the TRM which is false.

    So I've reverted my ack poll code back to using repeat mode since it appears that is the only way to to 0 byte transfers.  When this is successful it keeps control of the bus and doesn't issue a stop condition.  Immediately after this I start preparing to actually send my data to the slave device.  

    Section 24.3.4

    "When the BB bit is set to 1 and the STT bit is set to 1, a repeated START condition is generated."

    When my ack poll code exits, the BB is set to 1 which based on the above statement would indicate I can send a start condition and re-address the slave.  When repeat mode is on, I am able to.  When repeat mode is off, I am unable to. Here's a quick look at the transmit code:

        //
        // Setup slave address
        //
        I2C_setSlaveAddress(I2CA_BASE, 0x51);
    
    
        //
        // Send start as master transmitter
        //
        I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);// | I2C_REPEAT_MODE);
        I2C_setDataCount(I2CA_BASE, (currentMsg.numBytes*2) + 2);
        I2C_clearStatus(I2CA_BASE, I2C_INT_ADDR_SLAVE |
                                            I2C_INT_STOP_CONDITION |
                                            I2C_INT_TX_DATA_RDY |
                                            I2C_INT_RX_DATA_RDY |
                                            I2C_INT_REG_ACCESS_RDY |
                                            I2C_INT_NO_ACK |
                                            I2C_INT_ARB_LOST);
        I2C_sendStartCondition(I2CA_BASE);
    
        I2C_sendStopCondition(I2CA_BASE);
    
    
        //
        // Setup data to send
        //
        I2C_putData(I2CA_BASE, currentMsg.memoryHighAddr);
        I2C_putData(I2CA_BASE, currentMsg.memoryLowAddr);
    
        //While there is space in the fifo and we have data available
        txCount = 0;
    
        while((I2C_getTxFIFOStatus(I2CA_BASE) < I2C_FIFO_TX15) && (txCount < currentMsg.numBytes))
        {
            I2C_putData(I2CA_BASE, __byte((int *)&(currentMsg.msgBuffer[txCount]),0));
            I2C_putData(I2CA_BASE, __byte((int *)&(currentMsg.msgBuffer[txCount]),1));
            txCount++;
        }
    
    
        I2C_enableInterrupt(I2CA_BASE, I2C_INT_STOP_CONDITION | I2C_INT_TXFF | I2C_INT_NO_ACK);
        I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_TXFF);

    What actually ends up happening is the data is sent out as expected but the slave address isn't.  To illustrated this, I changed the slave address to 0x51 (doesn't exist on the bus) and the transfer still occurs because it relies on the address done during the ack poll.  This is why I was originally saying repeated starts aren't allowed unless repeat mode is turned on.

    What do you think?  Feature? Bug? Documentation shortcoming? 

    Best,

    Trey

  • Trey,

    I'm not sure whether I completely understand the problem. But, did you check the status of I2CSTR.XSMT? Is there an underflow condition (XSMT = 0) when you are trying to generate repeated start condition. In my setup, I'm not able to generate repeated start condition when there is a pending underflow condition in non-repeat mode.

    In non repeat mode, I2C master number of bytes to be transmitted depends on I2CCNT. It would not allow you generate repeated start condition until you finish transmitting I2CCNT number of bytes. Once you finish transmitted I2CCNT bytes and if the I2CMDR.STT is set to 1, then repeated start condition automatically gets generated.

    In repeat mode, number of bytes to be transmitted / received doesn't depend upon I2CCNT and I2C state machine allows you generate repeated start condition even on pending underflow condition (XSMT = 0).

    Again, this information is missing in TRM and I will update this information in next release of TRM.

    Thanks for bringing these corner cases, really helpful.

    Regards,

    Manoj

  • The problem is repeated start isn't sent in some cases.  You brought up a good point about underflow, but that still doesn't make a difference.

    For this test/observation, I moved the transmit buffer writes for the eeprom memory address (not the slave address) before I set the start bit in MDR.  This should prevent the underflow condition.  See below:

    I2C_setSlaveAddress(I2CA_BASE, 0x51);
    
    
    //
    // Send start as master transmitter
    //
    I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);// | I2C_REPEAT_MODE);
    I2C_setDataCount(I2CA_BASE, (currentMsg.numBytes*2) + 2);
    //
    // Setup data to send
    //
    I2C_putData(I2CA_BASE, currentMsg.memoryHighAddr);
    I2C_putData(I2CA_BASE, currentMsg.memoryLowAddr);
    
    //Start condition sent here

    All of the above happens immediately after the ack poll code I posted previously.  Based on what you've said, a repeated start condition should be generated in this case, but it is not.

    The Write 0x50 is the ack poll which is succeeding.  Then to illustrate my point, I've changed the desired slave address to 0x51 (doesn't exist on the bus).  If your assertions about the repeated start are correct, I should see a repeated start (and subsequent NACK) on the bus, but I do not.

    Does that better illustrate the problem?

    Trey

  • Trey,

    I think I have figured out the problem.

    In non-repeat mode, when I2CCNT = 0: it actually means number of bytes to transmit is 65536 before it generates the STOP condition. I tried this out on silicon and it is true. We have documented this vaguely in register bit-field description. I will make sure to clean this up in future TRMs.

    So, I2C state machine doesn't allow you generate another START condition until 65536 bytes are sent.

    Regards,

    Manoj

  • Manoj,

    Good point.  I had not seen that in the CNT register.  However, I don't think that explains this behavior.  In the above example with the scope capture, I am setting the CNT register to a reasonable value after I have turned off repeat mode... so shouldn't a repeated start be allowed?

    Trey

  • Trey,

     What actually ends up happening is the data is sent out as expected but the slave address isn't.  To illustrated this, I changed the slave address to 0x51 (doesn't exist on the bus) and the transfer still occurs because it relies on the address done during the ack poll.  This is why I was originally saying repeated starts aren't allowed unless repeat mode is turned on.

    In your earlier post, you had mentioned the above quote. I believe this condition is happening because of the following reason:

    I2CCNT = 0 (Number of bytes to be transferred is 65536)

    1st START condition generated.

    Now, I2C state machine will expect 65536 bytes to be transmitted. In your case you transmit only SLAVE address and then move on to below code.

    I2C_setSlaveAddress(I2CA_BASE, 0x51);

    I2C_setDataCount(I2CA_BASE, (currentMsg.numBytes*2) + 2);

    I2C_putData(I2CA_BASE, currentMsg.memoryHighAddr);
    I2C_putData(I2CA_BASE, currentMsg.memoryLowAddr);
    2nd START condition generated (STT = 1)
     
    Now, since the 1st START condition is incomplete (state machine expecting 65536 byte transfer), 2nd START condition doesn't get generated. Even though you set I2CCNT to some non-zero value it takes into effect only for second START condition.
     
    So, the bottom line is in non-repeat mode, you can't transfer zero byte. Minimum byte that needs to be transferred is 1 byte.
     
    Regards,
    Manoj

  • Manoj Santha Mohan said:
    Now, I2C state machine will expect 65536 bytes to be transmitted. In your case you transmit only SLAVE address and then move on to below code.

    I agree with this but I am doing the zero byte transfer in repeat mode.  The problem comes when I turn off repeat mode while I still have control of the bus.

    Right now I'm doing the zero byte transfer in repeat mode, waiting for it to finish, and then setting count to 0x42 before turning off repeat mode and setting the start bit.  I've tried I both by setting count before changing to non-repeat mode and after changing to non-repeat mode.

    To me it seems like this is an undocumented limitation of the peripheral.  If busy bit is set and non-repeat mode is disabled it is impossible to send another start condition.

    I'm happy to share the test code I'm using so you can see this yourself, but would prefer to not put it on the forums.  Chris Clearman has my email address or you can DM me on here.

  • Trey,

    Right now I'm doing the zero byte transfer in repeat mode, waiting for it to finish, and then setting count to 0x42 before turning off repeat mode and setting the start bit.  I've tried I both by setting count before changing to non-repeat mode and after changing to non-repeat mode.

    To me it seems like this is an undocumented limitation of the peripheral.  If busy bit is set and non-repeat mode is disabled it is impossible to send another start condition.

    I looked into your code and replicated above discussed issue on silicon. I need to confirm the behavior and hypothesis with design. But, here is what I think is happening.

    Once a START condition is generated with repeat mode enabled (RM = 1), I2C state machine doesn't allow you to switch to non-repeat mode (RM = 0) and generate repeated START until you generate STOP condition to close out repeat mode transaction completely. This information is clearly missing in TRM and needs update.

    I have commented on the I2C code in blue on how I2C machine behaves

    I2C_setConfig(base, (I2C_MASTER_SEND_MODE|I2C_REPEAT_MODE));
    I2C_setDataCount(I2CA_BASE, 0);

    I2C_sendStartCondition(I2CA_BASE);    //1st START condition

        START condition + Slave address gets generated
        After ARDY bit gets set

    I2C_setConfig(base, (I2C_MASTER_SEND_MODE));
    I2C_setDataCount(I2CA_BASE, non-zero value);

    I2C_sendStartCondition(I2CA_BASE);    //Repeated START doesn't get generated here

    I2C_putData(base, byte1); //Since STOP condition is not generated. I2C statemachine is still in repeat mode (RM = 1)
                                               //I2C transmits byte1. I2CCNT doesn't matter as I2C statemachine is still in repeat mode (RM = 1)
     
    I2C_putData(base, byte2);   //I2C transmit byte2

    I2C_sendStopCondition(base); //STOP condition doesn't take effect as I2C state machine wants repeat mode bit enabled (RM =1)
     
    I2C_putData(base, byte3);   //I2C transmit byte3. I2C state machine still in repeated mode (RM = 1)

    I2C_setConfig(base, (I2C_MASTER_SEND_MODE|I2C_REPEAT_MODE)); //STOP condition gets generated here as repeat mode bit is set (RM=1).

    Regards,

    Manoj

     

  • Trey,

    I have flagged design team on above issue. I expect them to confirm the above hypothesis and we shall go ahead and document this behavior in TRM.

    Regards,

    Manoj