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 doesn't generate STOP condition

I am using the I2C module in master mode with RM=1 (repeat mode).

The communication should look like this:

  1. START (r/w=0)
  2. send some bytes to the slave
  3. RESTART (r/w=1)
  4. receive one byte from the slave
  5. STOP

Everything works fine (the bus analyzer shows that all data is transmitted and received as expected and every byte gets ACKed), but the I2C module doesn't generate the STOP condition at the end (and it doesn't release the SCL line).

To generate the STOP condition I do the following:

while(!I2C_FGETH(i2c0,I2CSTR,ICRRDY));    // wait until data is ready to read
data = I2C_readByte(i2c0);                                  // read data (see step 4 above)
while(!I2C_FGETH(i2c0,I2CSTR,ARDY));         // wait until ready to access registers
I2C_FSETSH(i2c0,I2CMDR,STP,STOP);          // try to generate the STOP condition (see step 5 above)

After that, the I2CMDR register has the value 0x00004CA0, which means that the STP bit has not been cleared. The status register (I2CSTR) has the value 0x00001C1C, which shows that the bus is still in use (BB=1) and it also shows an overrun (RSFULL=1) and an underflow (XSMT=1)...

Robert

  • Figure 18 in the I2C reference guide implies that I should read a dummy byte after setting the STP bit. I think this will solve my problem.

    Robert

  • I've not used the peripheral much with RM=1.  However, I suspect the key might be to set the STP bit *before* reading I2CDRR for the final time.  That is, forget the dummy read and just make sure to set the STP bit *before* you read out the final data. 

    Do you need to use RM=1?  I've always found it much more straight-forward using the RM=0 mode since that utilizes a hardware counter.  The only reason you would not be able to use this mode of operation would be if you didn't know the length of your transfer ahead of time, e.g. if the transfer length was dependent on the info being received.  The I2C specification requires that in master receiver mode that the master NACK the slave when it wants to stop getting data.  The documentation says that if you're using RM=1 that you do this by setting the NACKMOD bit to 1 before the last bit of the transfer.  If you're using RM=0 the hardware will automatically NACK the last transfer for you...

  • I need RM=1 because (for more complex transfers than the example above) the number of bytes to receive depends on the data.

    I tried both versions and looked at them on the bus monitor:

    • When doing [restart, read, stop, dummy read], after the desired byte to read, there is a 0xFF (ACKed) and another 0xFF (not ACKed) on the bus. Then the STOP condition is generated.
    • With your version [restart, stop, read], after the desired byte to read, there is only one 0xFF (not ACKed) on the bus before the STOP condition is generated.

    So your version looks better. But I still have problems with it:

    When I call sendCommand (see below) only once, everything is fine. But when I want to call it a second time (immediately after the first time), it stops after writing DC1 (ICXRDY never gets set). As you can see, the only thing I reinitialize to start a new command is the I2CMDR register. Is there something else I have to initialize before starting a new transmission?

    When I call sendCommand only once, immediately after reading the data byte, the status register has the value 0x1410. If I look at the status register later (during normal program execution, without any I2C activity), it has the value 0x2418.

    So there is still a byte to read (the 0xFF I can see on the bus). Therefore I also tried do do one more read (or a second dummy read for the other version). Then the status register is 0x3410 immediately after reading and 0x2410 later. But a second call of sendCommand still doesn't work.

     

    void sendCommand(Uint8 *data, Uint8 len)
    {
     Uns i;
     Uint8 bcc;
     Uint8 ack;

     while(I2C_FGETH(i2c0,I2CSTR,BB));    // wait until bus is free
     I2C_RSETH(i2c0,I2CMDR,I2CMDR_START_TRANSMIT); // set up mode register for transmission

     while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
     I2C_writeByte(i2c0,DC1);

     while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
     I2C_writeByte(i2c0,len);

     bcc = DC1 + len;

     for (i=0; i<len; i++)
     {
      while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
      I2C_writeByte(i2c0,data[ i]);
      bcc = bcc + data[ i];
     }

     while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
     I2C_writeByte(i2c0,bcc);

     while(!I2C_FGETH(i2c0,I2CSTR,ARDY));   // wait until ready to access registers
     I2C_RSETH(i2c0,I2CMDR,I2CMDR_START_RECEIVE); // set up mode register for receiving

     while(!I2C_FGETH(i2c0,I2CSTR,ARDY));   // wait until ready to access registers
     I2C_FSETSH(i2c0,I2CMDR,STP,STOP);    // generate stop condition after reading next byte

     while(!I2C_FGETH(i2c0,I2CSTR,ICRRDY));
     ack = I2C_readByte(i2c0);

     statusAfterRead = I2C_RGETH(i2c0,I2CSTR);
    }

  • I also tried RM=0 for this simple case where the number of bytes does not depend on the data (see code below).

    On the bus analyzer it looks much more beautiful than with RM=1... The desired byte is NACKed and the STOP condition is generated immediately.

    But I have the same problem: A second call to sendCommand stops after writing DC1.

    Do you have example code (for RM=0 or RM=1) where I can see how you re-initialize a new transfer?

     

    void sendCommand(Uint8 *data, Uint8 len)
    {
     Uns i;
     Uint8 bcc;
     Uint8 ack;

     while(I2C_FGETH(i2c0,I2CSTR,BB));    // wait until bus is free
     I2C_RSETH(i2c0,I2CCNT,len+3);
     I2C_RSETH(i2c0,I2CMDR,I2CMDR_START_TRANSMIT); // set up mode register for transmission

     while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
     I2C_writeByte(i2c0,DC1);

     while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
     I2C_writeByte(i2c0,len);

     bcc = DC1 + len;

     for (i=0; i<len; i++)
     {
      while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
      I2C_writeByte(i2c0,data[ i]);
      bcc = bcc + data[ i];
     }

     while(!I2C_FGETH(i2c0,I2CSTR,ICXRDY));
     I2C_writeByte(i2c0,bcc);

     while(!I2C_FGETH(i2c0,I2CSTR,ARDY));

     I2C_RSETH(i2c0,I2CCNT,1);
     I2C_RSETH(i2c0,I2CMDR,I2CMDR_START_STOP_RECEIVE);

     while(!I2C_FGETH(i2c0,I2CSTR,ICRRDY));
     ack = I2C_readByte(i2c0);

    }

  • At the end of your sendCommand function poll for I2CMDR.MST == 0.  That bit will self-clear once the entire transaction is complete.

  • FYI, I added a couple more sections to this wiki page in your honor:

    http://wiki.davincidsp.com/index.php?title=I2cTips

    Good questions.

    Brad

  • Brad, thank you very much for your help. I've been debugging this I2C stuff for four days now (starting with some hardware problems), but now it works!

    Regarding your wiki page: In my experience, with RM=1 there have to be two reads after setting the STP bit. So if I set it before the last data byte I have to read one dummy byte; if I set it afterwards, I have to read two of them. But maybe you have to double-check this.

    Robert

  • Thanks for sharing your feedback on using the I2C with RM=1.  I added your comment to the wiki about needing to read a dummy byte.  Thanks for letting me know.  I'm swamped with things to do so I don't have time to test it out myself at the moment, although I'd like to.