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.

USCI30

Other Parts Discussed in Thread: MSP430F5528, MSP430F2122

Hi,


I think, I run into the I2C-Erratum USCI30. But the Workaround doesn't work.

I use Master Repeated Receive with UCB0.

The Symptoms are  ( s Picture):

  • SCL clocks endlessy
  • No Interrupt any more

The green Signal shows the duration of the Interrupt-Routine.

The delay comes from the Workaround ( waiting for SCL == LOW). The error comes during the reading of bytes.

Do you have any hint?

I've appended the ISR.

regards,

Steffen

6354.i2c_bug.c
#pragma vector = USCI_B0_VECTOR
__interrupt void I2C_B0_ISR(void) {
	bool test = true;
	int i;
	int count;
	switch (__even_in_range(UCB0IV, 12)) {
	case USCI_I2C_UCNACKIFG: /* NAK interrupt. */
		i2c[0].state = STATE_NACK;
		UCB0CTL1 |= UCTXSTP;
		break;
	case USCI_I2C_UCRXIFG: /* RX interrupt. */
		/* Read byte, decrement counter. */
		/*
		 UCSI30
		 other bytes follow the workaround:
		 Code flow for workaround
		 (1) Enter RX ISR for reading receiving bytes
		 (2) Check if UCSCLLOW.UCBxSTAT == 1
		 (3) If no, repeat step 2 until set
		 (4) If yes, repeat step 2 for a time period > 3 x t (BitClock) where t (BitClock) = 1/ f
		 (BitClock)
		 (5) If window of 3 x t(BitClock) cycles has elapsed, it is safe to read UCBxRXBUF
		 */
		if (--i2c[0].length) {
			test = true;
			while (test) {
				test = false;
				while ((UCB0STAT & UCSCLLOW)!= UCSCLLOW )
					;
				// High

				for (i = 0; i < 7; i++) {
					__delay_cycles(15);  // BR / 2  = 0x1E / 2 ;
					if ((UCB0STAT & UCSCLLOW)!= UCSCLLOW) {  // Low
						test = true;
						break;
					}
				}
			}
			*i2c[0].data = UCB0RXBUF;
			/* If only one byte left, prepare stop signal. */
			if (i2c[0].length == 1) {
				UCB0CTL1 |= UCTXSTP;
			}
			i2c[0].data++;
		} else {
			*i2c[0].data = UCB0RXBUF;
			i2c[0].state = STATE_READY;
		}
		break;
	case USCI_I2C_UCTXIFG:
		/* TX interrupt. */
		if (i2c[0].state == STATE_WRITING) {
			if (!i2c[0].slave_reg_written) {
				UCB0TXBUF = i2c[0].slave_reg;
				i2c[0].slave_reg_written = 1;
			} else if (i2c[0].length) {
				/* Send next byte, increment pointer. */
				UCB0TXBUF = *(i2c[0].data);
				i2c[0].data++;
				i2c[0].length--;
			} else {
				UCB0CTL1 |= UCTXSTP;
				UCB0IFG &= ~UCTXIFG;
				i2c[0].state = STATE_READY;
			}
		} else if (i2c[0].state == STATE_READING) {
			if (!i2c[0].slave_reg_written) {
				UCB0TXBUF = i2c[0].slave_reg;
				i2c[0].slave_reg_written = 1;
			} else {
				/* Repeated start, switch to RX mode. */
				UCB0CTL1 &= ~UCTR;
				UCB0CTL1 |= UCTXSTT;

				/* If single byte, prepare stop signal immediately. */
				if (i2c[0].length == 1) {
					/* Well, not IMMEDIATELY. First we need to make sure
					 * the start signal got sent.
					 */
					while (UCB0CTL1 & UCTXSTT)
						;
					UCB0CTL1 |= UCTXSTP;
				}
			}
		}
		break;
	case USCI_I2C_UCALIFG:
		/* Arbitration lost interrupt. */
		SNASSERT(33);
		break;
	default:
		break;
	}
	__bic_SR_register_on_exit(LPM0_bits);
}

  • Hi Steffen,

    I have a couple of questions for you:

    1. What device are you using?
    2. What is the I2C clock rate?
    3. What are your I2C pullup resistor values?
    4. How long are your I2C traces?
    5. How many I2C devices are connected to the bus (including the master)?

    From what I can see of the scope shot, it looks like your I2C lines may not have a strong enough pullup for the bus speed you are running.  The shot is zoomed out enough to make it difficult to tell for sure, but that might be an issue.  If the waveforms are not square enough, the 430 (or other devices on the bus) might be missing some bits and lose sync with the I2C transmitter.

    Another potential issue is your USCI30 workaround.  From quickly looking at your code, I don't think you have implemented it correctly.  The errata document doesn't do a great job of explaining the workaround, but you need to monitor the UCSCLLOW bit continuously (or nearly so) to ensure it stays continuously low for >3x bitclock cycles.  This guarantees you have stalled the bus and it is safe to read the RXBUF.

    Looking at your code, I think you are checking UCSCLLOW a 2x the rate of the bit clock, which means you are checking twice every bit clock cycle.  Depending on the timing of this read, it is possible to catch a sequence where the sampling occurs while SCL is low, then during the delay before the next UCSCLLOW check, SCL goes high and then low again, but your check misses it and assumes SCL was low the entire time.  I typically recommend checking at 3x the bit clock frequency or even more often to ensure you don't run into the scenario.

    When the USCI30 erratum workaround is correctly implemented, you will see the MSP430 stall the I2C bus after the 7th bit of every byte except the first byte received.

    Mike

  • Hi Mike,

    thanks for your quick reply.

    1. What device are you using? MSP430F5528
    2. What is the I2C clock rate?  380kHz
    3. What are your I2C pullup resistor values? 10k
    4. How long are your I2C traces? 5cm
    5. How many I2C devices are connected to the bus (including the master)? 1M+1S

    I will try an loop  only with UCSSTAT test. How many loops do you suggest?

    Is there a testcase to verify the fix? I have to wait for minutes until the bug appears.

    regards,

    Steffen

  • Steffen,

    10K is definitely too weak of a pullup value for I2C at 400k (or close to it).  I typically recommend 2.2K to 3.3K for 1M + 1S  with relatively short traces at ~400K.  5cm is a little bit longer, so try maybe 2.2K and see if that helps.

    Also, I found some generic code I wrote a while back for USCI30 workaround that you might try using:

    uint8_t i = 0;
    while (i < 9)
    {
        if (UCSCLLOW & UCBnSTAT)
        {
            i++;
        }
        else
        {
            i = 0;
        }
        __delay_cycles( x );  // x = 1/3 SCLK Frequency
    }

    Steffen Netz said:
    Is there a testcase to verify the fix?

    There is not a testcase to verify the fix.  As I mentioned in my previous post, when the workaround is correctly implemented, you will see the bus stall during every byte received (except the address and the first data byte).

    Steffen Netz said:
    I have to wait for minutes until the bug appears.

    Unfortunately, that is difficult part of trying to debug race conditions...sometimes it takes a while for everything to time out perfectly.  I have spent way more time than I wish to remember tracking down this type of bug.

    Mike

  • Hello,

    I'm using MSP430F2122 and I've problem with I2c. I think it's the problem related of USCI30.

    The DCO clock is 16MHz and the i2c clock is 66KHz. The mSP430 is a slave device. On the bus there is only a master and a slave.

    I've added your patch, but it doesn't work.

    Here there's a piece of i2c code:

    #pragma vector=USCIAB0TX_VECTOR
    __interrupt void I2C_TransmitReceive_ISR(void)
    {
            
            if ((IFG2 & UCB0RXIFG) != 0)
            {
                usci30_workaround_counter = 0;
                while (usci30_workaround_counter < 5)
                {
                    if ( UCB0STAT & UCSCLLOW )                
                    {           
                        usci30_workaround_counter++;
                    }
                    else
                    {
                        usci30_workaround_counter = 0;
                    }
                    sleep_us(45); //45usec sleep
                }
                date = UCB0RXBUF;
                ...
    
                if ((IFG2 & UCB0TXIFG) != 0)
                {
                ...
                 UCB0TXBUF = tx_date;
                ...
                } 
    
    
    

    without USCI30 patch, i see a i2c error every 4 hours of i2c stress-test.

    Thanks a lot for your help.

  • Mr Naic,

    I don't think you implemented the workaround for USCI 30 correctly.  At 66KHz, the bit period is ~15uS.  In my earlier post, I said:

    Mike Pridgen said:
    I typically recommend checking at 3x the bit clock frequency or even more often to ensure you don't run into the scenario.

    This means you should check at least a 198KHz frequency (66KHz * 3), which is roughly every 5uS.  You are checking every 45uS, which isn't often enough to guarantee you are not sampling between SCL pulses.  I.e. you check when SCL is low, then during your delay, SCL goes high then low again before you sample the next time.  In that situation, your code thinks the bus has stalled (sees low for two samples), but in actuality the bus is still active.  Please see my initial post for more information on this.

    Mike

  • Thanks for your answer.

    #pragma vector=USCIAB0TX_VECTOR __interrupt void I2C_TransmitReceive_ISR(void) { if ((IFG2 & UCB0RXIFG) != 0) { usci30_workaround_counter = 0; while (usci30_workaround_counter < 5) { if ( UCB0STAT & UCSCLLOW ) { usci30_workaround_counter++; } else { usci30_workaround_counter = 0; } sleep_us(5); //5usec sleep } date = UCB0RXBUF; ... if ((IFG2 & UCB0TXIFG) != 0) { ... UCB0TXBUF = tx_date; ... }

    But the behavior is the same. I tried different times and the post i added before is the last try with a wrong time sleep.

  • It sounds strange that a protocol that can works for few hours without problem (gives one error every 4 hours), with that patch isn't able to make also just one good comunication.

    The implmemented protocol  is the Write Word - Read Word of System Management Bus Specification 2.0 (SMBUS)

    I don't know where the problem could be; the patch is quite easy to apply but in my protocol doesn't go.

    I have to apply it because it could overcome a serious problem.

    Thanks for comprehension and for help.

  • Any chance you could post a shot of the SCL/SDA lines from an oscilloscope or LSA (in waveform mode)?

  • Thanks for posting the screen shots.  From what I can see, it looks like the USCI30 workaround timing is functioning correctly, you can tell because the I2C bus is stalled after the 7th bit of each byte received (after the first data byte).  However, I just realized you aren't waiting long enough.  The erratum workaround says to wait for SCL to stay low for 3x Bit Clock Cycles.  Since you are sampling every 1/3 bit clock cycle, sampling for 5 consecutive SCLLOW gives you less than 2 bit clock cycles.  I recommend increasing your workaround counter check from 5 to 9

    Also, there is one other additional piece to the work around.  The last byte of the packet must be read without the workaround.  By the time the interrupt for the last byte occurs and the cpu starts executing the ISR, the I2C lines have already gone idle (after the last byte), which is high.  This means the UCSCLLOW bit never goes to 1 because the SCL line is always high.

    Try adding some handling into your ISR to skip the waiting for a the consecutive UCSCLLOW = 1 checks when you are at the last byte in the packet.

    Mike

  • Thanks Mike,

    summarizing:

    CASE i2C SLAVE @  100KHz - Protocol SMBUS - Write Word

    Below there's a picture of the SMBUS protocol mentioned.

    The red arrow indicates the ONLY byte in the protocol where to apply the USCI30 workaround, right?

    In the following code I've added a signal (CHECK_SIGNAL) to indicate the time where the patch is working

    Thanks for your answer.#pragma vector=USCIAB0TX_VECTOR
    __interrupt void I2C_TransmitReceive_ISR(void)
    {
            
            if ((IFG2 & UCB0RXIFG)!= 0)
            {
                usci30_workaround_counter = 0;
                // USCI30 workaround applied ONLY on DATA_byte_Low
    CHECK_SIGNAL_HIGH while ( usci30_workaround_counter < 9 && data==DATA_Byte_Low ) { if ( UCB0STAT & UCSCLLOW ) { usci30_workaround_counter++; } else { usci30_workaround_counter = 0; } sleep_us(3); //3usec sleep }
                CHECK_SIGNAL_LOW
    date = UCB0RXBUF; 
    ...
    if ((IFG2 & UCB0TXIFG) != 0) { ... UCB0TXBUF = tx_date; ... }

    Using this code, I obtain the following waveform (green signal-> SMBUS_CLOCK; yellow signal->CHECK_SIGNAL)
  • Post the correct code because the page formatting is gone

    Thanks for your answer.#pragma vector=USCIAB0TX_VECTOR
    __interrupt void I2C_TransmitReceive_ISR(void)
    {
            
            if ((IFG2 & UCB0RXIFG)!= 0)
            {
                usci30_workaround_counter = 0;
    
                // USCI30 workaround applied ONLY on DATA_byte_Low     
            
                CHECK_SIGNAL_HIGH
                while ( usci30_workaround_counter < 9 && data==DATA_Byte_Low )
                {
                    if ( UCB0STAT & UCSCLLOW )               
                    {          
                        usci30_workaround_counter++;
                    }
                    else
                    {
                        usci30_workaround_counter = 0;
                    }
                    sleep_us(3); //3usec sleep
                }
    
                CHECK_SIGNAL_LOW
    
               date = UCB0RXBUF; 
               ... 
               if ((IFG2 & UCB0TXIFG) != 0) { ... UCB0TXBUF = tx_date; ... }

  • Mr.Naic said:
    The red arrow indicates the ONLY byte in the protocol where to apply the USCI30 workaround, right?

    No, the workaround should be applied to both the command byte and the data byte low.

    In I2C on the MSP430 USCI module, the RX flag is set once the entire byte has been received.  See the green arrows below, where A is the command byte RXIFG, B is the data byte low RXIFG, and C is the data byte high RXIFG.

    Since USCI30 applies to every byte except the last one, it needs to be applied to the ISRs for A and B, but not C.  When applied correctly, the USCI30 workaround will NOT read the data out of the RXBUF and wait for the bus to stall after bit 7 on the next byte.  Once this stall last for >3x bit clock cycles, the workaround should read the byte out of the RXBUF, which allows the bus to resume sending the 8th bit.  The net effect of this is the ISR that is generated at green arrow A (for the command byte) should stay in the loop waiting for SCL to be low for the required amount of time, eventually causing a bus stall at red arrow A.  After the 430 observes SCL held low for a sufficient period of time, the RXBUF should be read (the command byte), which releases the bus stall and I2C communication resumes.  The exact same thing should happen with the RXIFG at green arrow B, causing a bus stall to be observed at red arrow B.

    However, since the RXIFG at green arrow C is the last byte of the packet, the USCI30 workaround should NOT be applied, and should be read immediately.

    Mike

  • Code modified:

    #pragma vector=USCIAB0TX_VECTOR
    __interrupt void I2C_TransmitReceive_ISR(void)
    {
            
            if ((IFG2 & UCB0RXIFG)!= 0)
            {
                usci30_workaround_counter = 0;
    
                // USCI30 workaround applied ONLY on DATA_byte_Low     
            
                CHECK_SIGNAL_HIGH
                while ( usci30_workaround_counter < 9 && (data==Command || data==DATA_Byte_Low) )
                {
                    if ( UCB0STAT & UCSCLLOW )               
                    {          
                        usci30_workaround_counter++;
                    }
                    else
                    {
                        usci30_workaround_counter = 0;
                    }
                    sleep_us(3); //3usec sleep
                }
    
                CHECK_SIGNAL_LOW
    
               date = UCB0RXBUF; 
               ... 
               if ((IFG2 & UCB0TXIFG) != 0) { ... UCB0TXBUF = tx_date; ... }

    And below the waveform:

    What bout the read word protocol ? There's a command byte also there.

    and the relative waveform:

    But while the write is now ok, the read is still doing errors.

  • wrong picture about smbus read word-> the correct is below

  • On the SMBus Read, the USCI30 workaround does not need to be applied to the command code, as long as the command code is only one byte.  This is an I2C packet that is only a single byte long, so there is no need to apply the workaround as the only byte is also the last byte of the packet.

    Your waveform the the SMBus write looks like the USCI30 workaround is functioning correctly.

    Mike

  • Hi Mike,

    there's another problem: since the read-word protocol and the write-word protol have the same pattern for the first 19 bits, how is it possible to discriminate if it is a write or a read, consider that the workaround must be applied in a write-word protocol also at the command-code byte?

    In a write-word-protocol, after the command-code byte, the patch stalls the bus and then allows the bus to resume sending the 8th bit of the "Data Byte LOW" byte. But in a read-word protocol, the patch causes problems because there isn't another data-byte after the command byte, but there's a second start.

    So the question is, when I'm applying the patch on the Command-code byte, how can I discriminate if then will arrive a "Data Byte LOW" byte or a second-start bit? I checked that at the moment of discriminate what will happen then,  the USCI's registers have  exactly the same values.

  • Mr.Naic said:
    when I'm applying the patch on the Command-code byte, how can I discriminate if then will arrive a "Data Byte LOW" byte or a second-start bit?

    Perhaps a global static variable that indicates whether you are doing a write transaction or a read transaction at the next higher level of the communications stack. Modify your driver to set this variable when starting a new transaction, and check it in the ISR to determine appropriate behavior.

  • The device is a slave and the only way to comunicate with the master is via i2C. 

  • Mr.Naic said:
    since the read-word protocol and the write-word protol have the same pattern for the first 19 bits, how is it possible to discriminate if it is a write or a read,

    Simple. I2C is uni-directional. A transfer is either from master to slave or from slave to master (only the start byte is always from master to slave).
    In a read operation, the Wr bit is 1 while in a write operation, the Wr bit is 0. The USCI uses the inverted UCTR bit at this place. Also, the Wr bit is reflected in the UCTR bit of the slave USCI. In addition, the slave will have UCTXIFG set in a master receive operation (slave has to send something) and the master will have UCRXIFG set when the byte has arrived. In a master transmit operation (UCTR set), the slave will have UCRXIFG set when a byte arrived while the master will have UCTXIFG set when the USCI is ready to send the next byte.
    Between a start and a stop, you’ll always get only one type of interrupts, read or write, never both

**Attention** This is a public forum