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.

Multi-Byte Receive Issues with MSP430F5529 USCI I2C

Other Parts Discussed in Thread: MSP430F5529

I have been working on setting up communication between an MSP430F5529 I2C Master and several peripheral slaves.  I've encountered some difficulty terminating multi-byte receives at higher clock speeds.  Below is the ISR I have been using from the MSP430F55xx C Samples provided by TI:

//-------------------------------------------------------------------------------
// The USCI_B0 data ISR is used to move received data from the I2C slave
// to the MSP430 memory. It is structured such that it can be used to receive
// any 2+ number of bytes by pre-loading RXByteCtr with the byte count.
//-------------------------------------------------------------------------------
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
{
switch(__even_in_range(UCB0IV,12))
{
case 0: break; // Vector 0: No interrupts
case 2: break; // Vector 2: ALIFG
case 4: // Vector 4: NACKIFG
  UCBOCTL1 |= UCTXSTP;
  UCB0STAT &= ~UCNACKIFG
  break;
case 6: break; // Vector 6: STTIFG
case 8: break; // Vector 8: STPIFG
case 10: // Vector 10: RXIFG
  RXByteCtr--; // Decrement RX byte counter
  if (RXByteCtr)
  {
    *PRxData++ = UCB0RXBUF; // Move RX data to address PRxData //CASE 1
    if (RXByteCtr == 1) // Only one byte left?
    UCB0CTL1 |= UCTXSTP; // Generate I2C stop condition
    //*PRxData++ = UCB0RXBUF; // Move RX data to address PRxData //CASE2
  } 
  else
  {
    *PRxData = UCB0RXBUF; // Move final RX data to PRxData
    //__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
  }
  break;
case 12: break; // Vector 12: TXIFG
default: break;
}

Note that I have removed the call to exit LPM0.  For debugging purposes I instead idle inside of a while( UCB0STAT & UCBBUSY) to verify that the transaction has truly finished.  I may return to making use of the LPM once this is working.  For clarity, it may be worth mentioning that RXByteCtr is initially set to the number of bytes I wish to receive, so handling the final byte will begin by decrementing RXByteCtr from 1 to 0.  Anyways, on to the problem.

At low bus speeds (60 kHz) everything works.  At higher bus speeds (>100kHz) I observe the following, even when communicating with another MSP430F5529 set in slave mode which should be capable of much faster speeds: 

CASE 1: When UCTXSTP is set AFTER reading the 2nd to last byte. Normally everything works, but occasionally I will observe 1 extra bytes on the bus, and the would-be final byte will NOT be transferred into PRxData.  EDIT I have also verified that the ISR is NOT getting called an extra time to account for the extra byte.  This is important, as it eliminates one very easy explanation to why the would-be-final byte was not showing up in PRxData. /EDIT

CASE 2: When UCTXSTP is set BEFORE reading the 2nd to last byte.  Normally everything works, but occasionally I will observe the correct number of bytes on the bus and yet the 2nd-to-last byte will NOT be transferred into PRxData.

Here is my theory on what is happening:

CASE1: If UCTXSTP gets set BEFORE the UCSI module acks the final byte, everything's dandy.  However, if the USCI module manages to ACK the final byte in-between the MSP reading from UCRXBUF and setting UCTXSTP, then somehow the would-be final byte is lost. Presumably this lost byte gets over-written by the new final byte. 

CASE2: I assume the USCI module will always have begun transmission of the final byte by the time the ISR for the reception of the 2nd-to-final byte begins.  So I suspect that once UCTXSTP gets set, it's a race between the MSP430 reading RXBUF to clear the 2nd-to-last byte and the USCI module finishing reception of the final byte and over-writing the RXBUF before the MSP430 has a chance to read it.

In both cases, the fundamental problem seems to revolve setting UCTXSTP allowing the USCI modle to over-write the UCRXBUF before the msp has a chance to read it.  However, I have not read anything in the user's guide or elsewhere that explicitly confirms this behavior (the user's guide seems a little vague about the specifics of these transactions.  What happens when a stop condition gets set "immediately"?  I believe this is where it might be worth clarifying whether or not the UCRXBUF gets overwritten).

I am looking for a way to circumvent this issue without slowing the bus. 

Any suggestions or clarifications are appreciated, thanks a lot! 

  • After a bit of brainstorming I think I've hit upon a solution, although I'm not sure if this procedure will have subtle unintended effects on the USCI module's operation. 

    When I wish to set the UCTXSTP bit, I first wait for the USCI module to hold the SCL line low (which should be after reading the 7th bit of the final byte).  Once I have verified that the bus is stalled, I manually revert the SCL pin to a standard IO set to output low.  Then I am free to both set the UCTXSTP bit AND read from the UCRXBUF without the USCI module running off and receiving the rest of the last byte.  Once both of these are completed, I return control of the SCL line to the USCI module. 

    See the following code:

    if ( RXByteCtr == 1)
    {
        while( !(UCB0STAT & UCSCLLOW) );  //wait for master to hold clock low - are now mid-reception of final byte
        P3SEL &= ~SCL_PIN;                       //Hold SCL Low Manually
        *PRxData++ = UCB0RXBUF;             //Read Buffer of 2nd to last byte
        UCB0CTL1 |= UCTXSTP;                   //send stop after finishing this final byte
        P3SEL |= SCL_PIN;                          //Hand SCL control Back to USCI
    }

    This seems to work at all speeds without any delay on the bus except for a small pause when this extra code executes.  Can anyone verify that I am not taking excessive liberties with the USCI module here? 

    Thanks again for any help, 

    ~Rocky

    EDIT This solution may not be flawless.  The bus now hangs periodically (but not in the while loop in the ISR, much to my surprise).  I am beginning to suspect that I am bumping into USCI30, since after increasing the bus speed I can no longer reliably read the UCRXBUF before the USCI stalls the SCL line, and on the scope the length of time for which the line is stalled is not always greater than 3*tBitClock .  If that is indeed the case, the above solution may not be to blame.  

    There is most certainly a flaw in this solution.  I have caught on the scope an instance in which the bus gets held low briefly on reception of the 5th bit (instead of the 7th).  Once the SCL resumes, the slave sends the rest of the byte appropriately and the USCI module responds with a NACK. However, instead of following the NACK with a STOP bit, the USCI module continues to pulse the SCL line endlessly.  I am not sure of why the bus would have been stalled on the 5th bit, or why this would result in the USCI module failing to send a stop condition.

  • Rocky said:
    I have caught on the scope an instance in which the bus gets held low briefly on reception of the 5th bit (instead of the 7th).

    Possibly your detection of 'USCI holds SCL low on the 7th bit' was not perfect. When the USCI pulls SCL high, there is a short window in which SCL seems to be still low (due to line capacitance etc.) while the USCI has already released it and SCLLOW is flagged.
    You'll have to ensure that SCLLOW is flagged for more than the clock pulses for 1/2 clock cycle to be sure that there is indeed a clock stretching situation.

    Also, you should set UCTXNACK along with UCTXSTP. No need to manually hold SCL low. Just set NACK and STP, then read RXBUF, wait for RXIFG and read the last byte.

    Yes, I2C on the USCI is still not perfect.

    If timing is not critical (and it doe snot disturb the high-level protocol of the client), you may just ignore all this. When you get the interrupt for the last byte you want, simple set UCTXNACK+UCTXSTP, read the byte and drop the surplus byte that may or may not follow (depending on how fast you responded to the interrupt).

  • Sorry I have let this thread sit for a while.  I kept telling myself I'd go back and put time into figuring things out, but once I got something working 'enough' it was hard not to move on.

    Anyways, at least with the F5529, setting UCTXNACK along side UCTXSTP seems to have no effect.  The user's guide even says that a NACK will be generated upon setting only UCTXSTP. 

    I did take your suggestion and monitor SCLLOW to ensure it stays set.  This seems to have taken care of my problems, and now my code has been working for quite a while.  

    My original question hasn't actually been answered though.  I still don't know why I was loosing the second to last byte of some transmissions.  I don't actually mind receiving surplus bytes and ignoring them (although I can think of situations where that would be extremely undesirable), but that still didn't allow me to reliably receive all the bytes of the transaction I did need. 

**Attention** This is a public forum