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.

MSP430FR68xx I2C transfer on launchpad is blocked with UCBBUSY and UCSCLLOW

Other Parts Discussed in Thread: MSP430FR6989

On launchpad MSP430FR6989, I have problem with blocked I2C transfer (slave mode).

In general the transfer is working, but after a while (100..1000 transfers), the status flags UCBBUSY and UCSCLLOW are set.

One single transfer is:

1. Receive 5 Bytes
2. Transmit 3 Bytes

If the I2C is in the blocked state I already tried to write bytes manually with debugger into the TX buffer in order to send more bytes, but this does not help.
If it happens, it is between start and stop condition within the rx section of the transfer.

The bus lines SDA and SCL are both low in this state (no I2C interrupt anymore).

The only way I found to get out of this blocked state is to reset (UCSWRST) and reinitialize the I2C.
As I am not able to detect the blocked state by software I have to do it manually.

What can be the reason for the blocked state?

Is there a possibility to detect the state by some flags? I cannot use UCBBUSY and UCSCLLOW because those flags are
also set if I2C is not blocked (while normal I2C transfer is active).

How can it prevented to get into this blocked state?


  • Section 23 26.3.2 of the User's Guide says:

    UCBBUSY is set after a START and cleared after a STOP.

    For when UCSCLLOW is set, see section 23 26.3.7.1.

    Neither bit necessarily indicates an error.
    What are the values of the other status flags when this happens?

  • Which user's guide are you using? I am using MSP430FR68xx (SLAU367i from October 2012/April 2016).

    In the blocked state in the status register always the bits UCSCLLOW and UCBBUSY are set. The hardware byte counter has different values (1..5).

    In the interrupt flag register only bit 14 (UCBIT9IFG) is set, which seems to be always the case after each transfer.

    If the blocked state is reached, 4 bytes are received. A complete transfer consists of 5 Rx bytes and afterwards 3 Tx bytes.
    No I2C interrupt is generated anymore.

    The same blocked state happens always if I stop program execution with debugger in ISR after first byte is received.
    Afterwards also UCSCLLOW and UCBBUSY is set an no I2C interrupt is generated anymore (I assume because clock is low and not toggling anymore).

    How should this state be left? At the moment the only way to get out of this state is to reset and reinitialize the eUSCI_B(I2C) block.

  • There might be a bug in your code.
  • What can be wrong?
    In the I2C ISR I read 5 bytes and send afterwards 3 bytes which is triggered by the Tx buffer empty interrupt flag.

    On the master side of I2C bus (PC) I see that the transmitter has the two bytes in its receive buffer what it was sending before.
    The slave (MSP430) has received those bytes, but for the last byte no interrupt was generated anymore.
  • You are not describing what your code actually does, but what you think your code should be doing.

    If you continue to keep your code secret, nobody will be able to help you.

  • The code now is inserted by "Insert Code" button.

  • I changed the code on the I2C master side in order to have 3 separated parts.
    1. Send 5 bytes (e.g. 0x80, 0x10, 0x00, 0x00, 0x00) and wait 100 ms
        HID_SMBUS_STATUS status = HidSmbus_WriteRequest(*m_pHidSmbus, m_slaveAddress, writeData, m_writeDataSize);
        Sleep( 100 );

    2. Send Read Request for 3 bytes and wait 100 ms
        status = HidSmbus_ReadRequest(*m_pHidSmbus, m_slaveAddress, m_bytesToRead);
        Sleep( 100 );
        status = HidSmbus_ForceReadResponse(*m_pHidSmbus, m_bytesToRead);

    3. Read the 3 requested bytes and wait for 100 ms
        status = HidSmbus_GetReadResponse(*m_pHidSmbus, &status0, buffer, HID_SMBUS_MAX_READ_RESPONSE_SIZE, &numBytesRead);
        Sleep( 100 );

    With 10 kHz I2C clock it seems to work.
    If I use 100 kHz I still see the blocked state where UCSCLLOW and UCBBUSY is set and no interrupt is triggered anymore.

  • To insert code, use the "Insert Code" button.

    Show the definitions of all variables and symbols used.
  • #pragma vector=USCI_B1_VECTOR
    __interrupt void ISR_EUSCI_B1( void )
    {
        static Uint16 readWord = 0;
    
        switch( __even_in_range( UCB1IV, 0x1E ) )
        {
            case USCI_I2C_UCTXIFG0:
                ////////////////////
                // tx - interrupt //
                ////////////////////
                {
                    Uint8 byteToSend;
                    i2cTxIrqCount++;
                    allTxIrqCount++;
                    switch( i2cTxIrqCount ) 
                    {
                        case 1:
                            byteToSend = (readWord & 0xFF00) >> 8;
                            break;
    
                        case 2:
                            byteToSend = (readWord & 0x00FF);
                            break;
    
                        case 3:
                            byteToSend    = 0xFF;
                            i2cTxIrqCount = 0;
                            break;
    
                        default:
                            break;
                    }
                    if( i2cRxIrqCount == 0 )
                    {
                        switchLED( LED_RED, false );
                    }
    
                    EUSCI_B_I2C_slavePutData(   EUSCI_B1_BASE, byteToSend );
                    EUSCI_B_I2C_clearInterrupt( EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 );
                }
                break;
    
            case USCI_I2C_UCRXIFG0: 
                ////////////////////
                // rx - interrupt //
                ////////////////////
                // 0 : command (0x80=read, 0x00=write)
                // 1 : word address
                // 2 : value MSB
                // 3 : value LSB
                // 4 : not used
                rxBytes[i2cRxIrqCount] = EUSCI_B_I2C_slaveGetData( EUSCI_B1_BASE );
                i2cRxIrqCount++;
                allRxIrqCount++;
    
                if( i2cRxIrqCount>=RxBufferSize )
                    i2cRxIrqCount = 0;
    
                if( i2cRxIrqCount==5 )
                {
                    // Request complete
                    if( rxBytes[0]==0x80 )
                    {
                        // read request 
                        Uint16*    i2cIFMemoryMapArray = (Uint16*) &i2cIFMemoryMap;              
                        Uint8      wordAddress         = rxBytes[1];
                        readWord = i2cIFMemoryMapArray[wordAddress];
                        lastReceivedAddress = wordAddress;
                        i2cTxIrqCount = 0;
                        switchLED( LED_RED, true );
                    }
    
                    if( rxBytes[0]==0x00 )
                    {
                        // write request 
                        Uint16* i2cIFMemoryMapArray = (Uint16*) &i2cIFMemoryMap;              
                        Uint8      wordAddress = rxBytes[1];
                        readWord      = rxBytes[2] << 8;
                        readWord     += rxBytes[3];
                        i2cIFMemoryMapArray[wordAddress] = readWord;
                    }
                    i2cRxIrqCount = 0;
                }
                EUSCI_B_I2C_clearInterrupt( EUSCI_B1_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0 );
                break;
    
            case USCI_I2C_UCSTTIFG:        
                ///////////////////////
                // start - interrupt //
                ///////////////////////
                startInterrupt = true;
                stopInterrupt  = false;
                EUSCI_B_I2C_clearInterrupt( EUSCI_B1_BASE, EUSCI_B_I2C_START_INTERRUPT );
                break;
    
        case USCI_I2C_UCSTPIFG:        
                //////////////////////
                // stop - interrupt //
                //////////////////////
                stopInterrupt  = true;
                startInterrupt = false;
                EUSCI_B_I2C_clearInterrupt( EUSCI_B1_BASE, EUSCI_B_I2C_STOP_INTERRUPT );
                break;
    
            default:
                break;
        }
    }

  • The relevant variables/symbols which are defined outside the ISR are:

    #define  RxBufferSize        32
    volatile Uint8 rxBytes[RxBufferSize];
    Uint32  i2cRxIrqCount = 0;
    Uint32  i2cTxIrqCount = 0;
    Uint32  allRxIrqCount = 0;
    Uint32  allTxIrqCount = 0;
    Uint16  lastReceivedAddress = 0xFFFF;
    bool    startInterrupt = false;
    bool    stopInterrupt  = false;
    
    

  • All variables shared between the interrupt handler and other code must be volatile.

    The interrupt flags are automatically cleared by reading/writing the corresponding RXBUF/TXBUF register, or by reading the interrupt vector register; you must not do that manually.

    This code is rather brittle; it will break if there is ever a wrong number of bytes. The RX/TX counts should be reset on a start condition, and a stop condition should reset everything.

    If the SDA line is stuck low, the slave did not see a clock pulse. This usually happens if the pull-up resistor is too large for the speed used; either use smaller ones, or reduce the speed. To check whether the I²C signals are OK, you must use an oscilloscope. (And don't design the circuit so that it barely works; use a large safety factor.)
    But in any case, the master should be able to handle this error; see section 3.1.16 of the I²C specification.

    (This problem sounds suspiciously like erratum USCI30, but your chip does not have it.)

  • Now it is working.
    The key change was the remove of clearing the interrupt flags.
    I have tested the transfers with 400 kHz I²C clock.

    Now I want to change the handling of the RX/TX counters as you have proposed.
    The problem here is, that I get two interrupts for the start condition and one for the stop condition.
    I would expect only one interrupt for the start condition at the begin of the transfer (5 x RX + 3 x TX).
    The second start condition interrupt is triggered just before the first TX interrupt is triggered.

**Attention** This is a public forum