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.

I2C - when is transmit ready

Wondering if anyone could clarify operation of the I2C module on the C6748.

Testing this transmitting multiple bytes using RM=1 IGNACK=1. If I check either ICXRDY==1 or ARDY==1 it will only send 2 bytes (i.e. the others seem to get written to the TX reg before it is ready, but then why 2 not 1?). It appears to work correctly if I check that both ICXRDY and ARDY are 1 before  writing the next byte to the Tx register ICDXR.

When transmitting multiple bytes  what is the appropriate bit to check after sending a byte and before sending the next byte? And why?

Roger

  • Roger,

    There is a good rCSL example for the I2C.  Once you install the pspdrivers, look in C:\Program Files\Texas Instruments\pspdrivers_01_30_01\packages\ti\pspiom\cslr\evmOMAPL138\examples\i2c\src (if it is installed using the default directory).  You'll find an I2C example that does multiple transmits and receives.

    - Christina

  • Christina,

    Thanks for answering, I've been distracted on something else for a while. I had a look at the code you mentioned & it checks ICXRDY only. However since its a loopback test it waits for a receive ready, then checks for transmit ready before sending out the next byte. Whereas I am checking for transmit ready immediately I have sent out the byte.

    In my test earlier I did not have an I2C device connected, so I told it to ignore NACKs. Now I have a device connected and it appears to work OK by just checking ICXRDY only immediately (i.e. I do not have to check ARDY) after transmit. If I do check ARDY as well then there is a short delay between byte transmissions.

    It would be helpful if there were some clarification of what ICXRDY, ARDY etc really mean because to me it is not clear & I am reduced to testing the code on the hardware to find out what happens?

    Regards   ---   Roger

  • Roger,

    I'm not very familiar with the low-level logic of the I2C module, but below are the definitions of the ARDY and IXCRDY bits.  Based on my speculation of the bit definitions, it seems like ARDY waits for the previous data, command and address to fully execute before setting this bit.  Therefore in your application, it should be waiting for the previous data in ICXSR to transmit before setting this bit.  The ICXRDY only monitors whenever the data in the ICDXR register is copied to the ICXSR.  Therefore, it doesn't wait for the data to transmit before setting the ICXRDY bit. 

    - Christina

     

    ARDY:  Register-access-ready interrupt flag bit (only applicable when the I2C is in the master mode). ARDY indicates that the I2C registers are ready to be accessed because the previously programmed address, data, and command values have been performed and the status bits have been updated.

    ARDY is set when the registers are ready to be accessed. This bit is set after the slave address appears on the I2C bus
    • In the nonrepeat mode (RM = 0 in ICMDR): If STP = 0 in ICMDR, ARDY is set when the internal data counter counts down to 0. If STP = 1, ARDY is not affected (instead, the I2C generates a STOP condition when the counter reaches 0).
    • In the repeat mode (RM = 1): ARDY is set at the end of each data word transmitted from ICDXR.

     

    ICXRDY: Transmit-data-ready interrupt flag bit. ICXRDY indicates that the data transmit register (ICDXR) is ready to accept new data because the previous data has been copied from ICDXR to the transmit shift register (ICXSR). The CPU can poll ICXRDY or use the XRDY interrupt request. ICDXR is set when data has been copied from ICDXR to ICXSR.

  • Have you seen this wiki page:

    http://processors.wiki.ti.com/index.php/I2C_Tips

    Also, why are you using RM=1?  Personally I have found the I2C controller much easier to use when using RM=0 (i.e. using the hardware counter instead of counting in your software).  With RM=0 you set the transfer size in the CNT register and when you've transmitted the proper number of bytes it will send out the STP condition (assuming you set the STP bit at the start of the whole transfer).  There's some examples of this at the wiki page above.

    I agree with you that the ARDY bit is difficult to understand.  In fact, after spending a lot of time playing with the I2C peripheral I still cannot describe it well!  The place I have found it to be most useful is in detecting NACKs.  Note, however, that it will only work if you are using RM=0 because otherwise ARDY will trigger after every transfer!  Please read more in the section on the wiki page and let me know if something isn't clear and I'll try to update the wiki page.

    Why are you doing multiple reads of the register to look at ICXRDY and ARDY?  You should just do a single register read (into a temp variable) and then examine the bits of the temp variable.  Reads to peripheral space are very slow as you have discovered.

    Brad

  •  

    Hello Brad,

     

    Have you seen this wiki page:

     

    http://processors.wiki.ti.com/index.php/I2C_Tips

     

    Yes I saw that page. Its useful, but there seem to be some differences between the DM355 I2C and the C6748 I2C one of which is that RM=0 seems to work on both Tx & Rx on the DM355, but the I2C on the C6748 only works with RM=0 on transmit (SPRUFL9A page 35).

     

    Also, why are you using RM=1?

    In a misguided attempt at keeping it simple, since the C6748 does not do RM=0 on receive according to the user guide & I thought it would make both Rx &Tx code similar.

    Personally I have found the I2C controller much easier to use when using RM=0 (i.e. using the hardware counter instead of counting in your software).

    But you still have to count the bytes in software.

    With RM=0 you set the transfer size in the CNT register and when you've transmitted the proper number of bytes it will send out the STP condition (assuming you set the STP bit at the start of the whole transfer).  There's some examples of this at the wiki page above.

     

    I agree with you that the ARDY bit is difficult to understand.  In fact, after spending a lot of time playing with the I2C peripheral I still cannot describe it well! 

    If you can’t get the info about how it works what chance do I stand?

    The place I have found it to be most useful is in detecting NACKs.  Note, however, that it will only work if you are using RM=0 because otherwise ARDY will trigger after every transfer!  Please read more in the section on the wiki page and let me know if something isn't clear and I'll try to update the wiki page.

     

    Why are you doing multiple reads of the register to look at ICXRDY and ARDY?  You should just do a single register read (into a temp variable) and then examine the bits of the temp variable.  Reads to peripheral space are very slow as you have discovered.

     

    How did you deduce that I was using multiple reads to get ICXRDY & ARDY :-)  Because I said that when ARDY was used there was a small delay introduced between bytes? I didn’t measure it but that delay was much larger than the delay caused by the read alone I think so I assumed it was a I2C hardware thing. Reason is I have a couple of simple macros to do bit field manipulation (like CSL but simpler) & I use these to make the code more readable for me. One day I need to read up to fully understand why reads & writes are so slow, on this project it does not matter.

    We could do with a new I2C module being designed that did not require so much software interaction (and was easier to use)AND(did not have bugs). For example I guess in most use cases the I2C is the master and its used in 2 ways either just sending out some data, or sending some address + register address, then receiving some data. So the new module could implement the simple cases & would be pointed at some data array and told to get on with doing that & report back. Any chance of this happening one day? I heard it was going to appear as PRU code but that would be a waste of a PRU. Alternatively get rid of all modules and replace them with multiple PRUs (maybe that is the way they are implemented now for all I know).

     

    I have it working in transmit data OK. When I send a repeat start signal to receive – by setting TRX=0 to make it a receiver, then set STT to a 1, what it does is drive out 18 or 19 clocks (i.e. 2 bytes). It’s only supposed to send one byte namely the slave address, I cannot see why it is doing this. It could be something else on the bus (using the LogicPD EVM) but I don’t think so. Since I have spent some time on this module & things are still not clear (I hate this playing around guessing what bits do what when the documentation is inadequate) I’m going to bit bash the port, should not take too long.

     

    Brad

     

    Regards   ---   Roger

  • Hi Christina,

    Thanks for this, I replied in more detail to Brad today.

    Regards   ---   Roger

  • roger littlewood said:
    Yes I saw that page. Its useful, but there seem to be some differences between the DM355 I2C and the C6748 I2C one of which is that RM=0 seems to work on both Tx & Rx on the DM355, but the I2C on the C6748 only works with RM=0 on transmit (SPRUFL9A page 35).

    Here's a quote from the page you mention:

    Repeat mode bit (only applicable when the I2C is a master-transmitter). The RM, STT, and STP bits
    determine when the I2C starts and stops data transmissions (see Table 15). If the I2C is configured in
    slave mode, the RM bit is don't care
    .

    Perhaps Christina can confirm, but I believe the first statement in red should only say "applicable when the I2C is a master".  That corresponds better with the second statement I highlighted in red.  Additionally, page 21 "Configuring the I2C in Master Receiver Mode and Servicing Receive Data via CPU" specifically says in step 3 to set RM=0.

    roger littlewood said:
    Personally I have found the I2C controller much easier to use when using RM=0 (i.e. using the hardware counter instead of counting in your software).

    But you still have to count the bytes in software.

    I found it easier because of the way ARDY behaves in this mode and the fact that you don't have to set the STP bit at the very end.

    roger littlewood said:
    I agree with you that the ARDY bit is difficult to understand.  In fact, after spending a lot of time playing with the I2C peripheral I still cannot describe it well! 
    If you can’t get the info about how it works what chance do I stand?

    I can help!

    roger littlewood said:
    How did you deduce that I was using multiple reads to get ICXRDY & ARDY :-)  

    So are you or aren't you doing 2 reads?  That seemed the obvious explanation for the delay you were seeing though it could potentially be something else.

    roger littlewood said:
    We could do with a new I2C module being designed that did not require so much software interaction (and was easier to use)AND(did not have bugs).

    How do you make it "do the right thing" without software interaction?  What bugs?  I looked in the errata and I don't see any bugs.  We overhauled this peripheral back around 2003 (or maybe 2005?) to get rid of all the bugs.  My experience has been that the I2C module is quite robust.  Yes, it could be easier to use, which was why I wrote a wiki page to help clarify some of the primary points of confusion.

    roger littlewood said:
    When I send a repeat start signal to receive – by setting TRX=0 to make it a receiver, then set STT to a 1, what it does is drive out 18 or 19 clocks (i.e. 2 bytes). It’s only supposed to send one byte namely the slave address, I cannot see why it is doing this.

    When doing the repeat start I would expect to see it drive out 7-bit slave address plus R/W=0.  Then it should drive out the clocks to do the actual read.  A code example of what you want to do is in the "repeat start" section of the wiki page.

    roger littlewood said:
    Since I have spent some time on this module & things are still not clear (I hate this playing around guessing what bits do what when the documentation is inadequate) I’m going to bit bash the port, should not take too long.

    I think this whole issue may have begun due to the confusing statement I pointed out in the RM description.  Had you used RM=0 you could have copied all my code from the wiki page.  In fact, I recommend you do so regardless.  Bit banged I2C is not terribly hard, but I think copying my code should be easier (and more efficient).

  • Hello Brad,

    Yes I am doing 2 reads!

    Whoa, the "bugs" bit was a bit tongue in cheek, maybe just no ambiguities in the user guides would be enough.

    Yes I saw the ambiguity in the RM stuff in a couple of places. I'll test out RM=0 on receive (by copying your code). It should still have worked though by using RM=1, maybe I'm doing something wrong somewhere. I won't be able to test this until Friday but I'll let you know the result.

    Thanks for your help   ---   Roger

  • roger littlewood said:
    Yes I am doing 2 reads!

    Ok, then I guess the behavior your seeing makes sense.  FYI, be careful if you're polling make sure you do it like this:

    // Wait for "XRDY" flag to transmit data or "ARDY" if we get NACKed
    while ( !(*I2C_STR & (ICSTR_ICXRDY|ICSTR_ARDY)) );

    If you implement 2 separate loops (i.e. first xrdy then ardy) you can get stuck forever in the first loop in the case of a NACK.

    roger littlewood said:
    Whoa, the "bugs" bit was a bit tongue in cheek, maybe just no ambiguities in the user guides would be enough.


    Fair enough. :) Hopefully Christina can help us straighten it out!

    roger littlewood said:
    Yes I saw the ambiguity in the RM stuff in a couple of places. I'll test out RM=0 on receive (by copying your code). It should still have worked though by using RM=1, maybe I'm doing something wrong somewhere. I won't be able to test this until Friday but I'll let you know the result.


    I could review your code, but I think it would be better to try it the other way.

  • Hello Brad,

     

    So I changed my code to be similar to yours using RM=0. It now seems to work OK. I don’t know what I was doing wrong with RM=1 before & I’ve already spent too much time on this bit of the project to investigate that.

     

    I didn’t bother checking for NACK on every byte because in this design there’s nothing that can be done to get out of that anyway.

     

    After the transmit phase I check ARDY. I also did this after the receive phase but of course it hung there & I removed this after reading the description of the ARDY bit more closely, it does not change when the stop condition is sent when RM=0. So after the receive phase I check BB so it will only exit the routine when all is done – presumably checking MST could also have done the same thing.

     

    Thanks for your help!

     

    Regards   ---  Roger

     

     

  • Roger,

    I'm glad you're having a lot more success now!  That's good news!

    I recommend having the NACK detection in there.  It will make your application more robust.  If for some odd reason a slave ever did NACK you the processor will hang (i.e. XRDY will never become ready).  You could implement some kind of retry mechanism if you wanted.  If nothing else I would implement the code shown in the wiki page to free up the bus again.

    Best regards,
    Brad