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.

MSP430F5529: I2C 'stuck' waiting for UCTXIFG

Part Number: MSP430F5529

Hey all,

I have been working on a custom PCB with an MSP430F5529 and LSM6DS33 break out sensor  (datasheet). I have tested this sensor using an Arduino Mega and the SparkFun Library (GitHub Library). Next, I have attempted to read data from this sensor using the USCI I2C Peripheral of the MSP430. I have read and examined the User Guide and the MSPWARE 3.60.00.10 examples. I tried mixing the examples together to write a function that will read up to twelve consecutive registers from the sensor. The code, however, gets stuck waiting for the TX buffer to finish shifting the byte (target register) into the peripheral. And I cannot figure out why.

First, I initialise the I2C peripheral:

    //Set the pin 3.0 and 3.1 as I2C
    P3SEL |= 0x03;

    //Reset USCI before configuring
    UCB0CTL1 |= UCSWRST;

    //Set USCI to I2C master mode and set MSB first
    UCB0CTL0 |= UCMST | UCMODE_3 | UCSYNC | UCMSB;

    //Set the baud rate 1MHz/10 = 100kHz
    UCB0BR0 = 10;
    UCB0BR1 = 0;

    //Set the clock source, enabling I2C
    UCB0CTL1 |= UCSSEL_2;

    //Disable interrupt requests
    UCB0IE &= ~(UCTXIE + UCRXIE);

    //Save Slave Address
    UCB0I2CSA = 0x6A;
//Enable I2C module UCB0CTL1 &= ~UCSWRST;


>> Assumption: the Slave Address is shifted one bit left by the peripheral when it adds the R/W bit.


Next, I attempt to send a byte with the address to the Slave, following the protocol as described in Table 15 on Page 30 of the LSM6DS33 Datasheet. According to the User Guide, the UCTXIFG bit will be set after the Slave Address has been sent, to indicate that UCB0TXBUF is ready to receive a byte. Then I write my data to the UCB0TXBUF and wait for the UCTXIFG bit to be set again. After that, a Repeat Start must be sent again, telling the Slave that it needs to send data to me.

    //Send start condition.
    UCB0CTL1 |= UCTR + UCTXSTT;

    //Wait for the start condition to be sent.
    while(!(UCB0IFG & UCTXIFG)){;}

    //Write byte into transfer buffer
    UCB0TXBUF = 0x28;
    
    //Wait for the data to be sent.
    while(!(UCB0IFG & UCTXIFG)){;}


    //Start receiving n bytes

    //Send repeat start condition
    UCB0CTL1 &= ~UCTR;
    UCB0CTL1 |= UCTXSTT;

The code, however, get stuck in the second while loop, right after the data has been written to the UCB0TXBUF.


Attempted solutions and additional information:

  • Add external pull-ups on SDA and SCL lines; they are already on the break-out board (schematic) so they shouldn't be necessary anyways.
  • Enable interrupts (I assumed that the corresponding bits in the UCB0IFG are set whether an interrupt is generated or not).
  • Check for connection, PCB and solder errors (also measured 3.27 VCC steady).
  • SDO is soldered to ground (making the LSB of the Slave Address a '0').
  • Add a UCSCLLOW check after writing UCB0TXBUF; there is no external device pulling the SCL low.

Unfortunately, I do not possess an oscilloscope or digital analyzer. I might be able to borrow one next week, then I can add signal images of SDA and SCL. For now, I need to know:

  1. Is my code correct? It doesn't provide build errors/warnings.
  2. Is my code complete? I might have overlooked some crucial setting/interrupt/register value.
  3. Is there anything else on the hardware that might cause issues?

Any help, insights, advice, design tips, code tricks, useful links or code testing is appreciated. I will gladly provide more details if they are necessary. This is the final piece of code I need to get working; after this, I can finally finish my demo and graduate...

Kind regards,

Martin Janse

  • As I read the schematic, SA0 is pulled up, so the address should be 0x6B.

    More generally: You should probably look over those charts at the end of Users Guide (SLAU208O) chapter 38 -- Figure 38-12 is relevant here -- which describe the state machine. Scanning left to right gives time sequence, and top to bottom shows the failure modes. In your case I expect it transitioned down to NACK right after the box with the "A" in it.

    Even more generally: The I2C state machine is pretty easy to use interrupt-driven, but trickier (or at least more tedious) as in-line code, due to the failure modes . The example programs can provide skeleton ISRs.
  • Thank you for taking your time to look into the problem and replying!

    Bruce McKenney47378 said:
    As I read the schematic, SA0 is pulled up, so the address should be 0x6B.

    It should indeed be 6B if the 6th pin on the header is left floating. However, this pin is connected to ground. Just to be sure, I removed the connection and changed the Slave Address accordingly. Unfortunately, it did not resolve my issue.

    Bruce McKenney47378 said:
    More generally: You should probably look over those charts at the end of Users Guide (SLAU208O) chapter 38 -- Figure 38-12 is relevant here -- which describe the state machine. Scanning left to right gives time sequence, and top to bottom shows the failure modes. In your case I expect it transitioned down to NACK right after the box with the "A" in it.

    I did look at the charts and your comment inspired me to do extensive register checking at every step. I wrote a little function that reads all relevant register values. Then, I compare these values to the bits I expect so I could see any differences. Two things were out of place.

    First, the Multi-Master Enable bit was set. This is odd because I never set that bit and it should be '0' by default. I added a line to force the bit to '0' to the I2C initialization function.

    Secondly, the UCBBUSY bit was set as well, even before starting any transmission. This is unexpected behaviour; not in line with the datasheet. After all, the UCBBUSY bit should be reset when UCSWRST is released and the SCL pin is high. However, the bit remained '1'. I measured both SCL and SDA lines and they were steady at 3.27V. After googling for this odd behaviour, I ran into another post that described this phenomenon as an 'undocumented errata'. Two solutions were presented.

    First, preventing the UCBBUSY bit from getting set. This requires the SCL lines to be high during the configuration. In order to do this, I initialised the I2C pins as GPIO, enabled internal pull-ups and turned them on. Then I finish the configuration and enable the USCI again. Only after that, the pins are set to I2C again. This solves the permanent UCBBUSY issue. My initialization code now looks like this:

    int IMU_init(void){
    
        //Set the pin 3.0 and 3.1 as high output.
        //This prevents the USCI module from erroneously assuming
        //that the I2C bus is busy.
        P3SEL &= ~0x03;
        P3REN |=  0x03;
        P3DIR |=  0x03;
        P3OUT |=  0x03;
    
        //Reset USCI before configuring.
        UCB0CTL1 |= UCSWRST;
    
        //Set USCI to single master mode.
        UCB0CTL0 |= UCMST | UCMODE_3 | UCSYNC | UCMSB;
        UCB0CTL0 &= ~UCMM;
    
        //Set the baud rate @100kHz.
        UCB0BR0 = 10;
        UCB0BR1 = 0;
    
        //Set the clock source, enabling I2C
        UCB0CTL1 |= UCSSEL_2;
    
        //Disable interrupt requests
        UCB0IE &= ~(UCTXIE + UCRXIE);
    
        //Save Slave Address
        UCB0I2CSA = 0x6B;
    
        //Enable I2C module
        UCB0CTL1 &= ~UCSWRST;
    
        //Give P3.0 and P3.1 to the USCI module
        P3OUT &= ~0x03;
        P3DIR &= ~0x03;
        P3REN &= ~0x03;
        P3SEL |=  0x03;
    
        return 0;
    }

    However, a second problem still occurred. Sometimes - I am not sure what triggers it - the UCBBUSY bit stays set even though the transmission has ended with a STOP bit. For these rare cases, I added a short function which incorporates the second solution.

    The second solution is to toggle the I2C pins as GPIO a couple of times to 'fool' the USCI into thinking a transmission has ended. The function call I use:

    if(UCB0STAT & UCBBUSY){toggleBusy();}

    The function itself simply toggles the I2C pins a couple of times with a small delay in between:

    void toggleBusy(void){
    
        //Set P3.0 and P3.1 as GPIO, with pull ups as output
        P3SEL &= ~0x03;
        P3REN |= 0x03;
        P3DIR |= 0x03;
    
        //Toggle the pin value 10 times
        int i = 0;
        for (i = 10; i > 0; i--){
            P3OUT |= 0x03;
            __delay_cycles(150);
            P3OUT &= ~0x03;
            __delay_cycles(150);
        }
    
        //Disable interna pull ups and turn pins back to I2C
        P3REN &= ~0x03;
        P3DIR &= ~0x03;
        P3SEL |= 0x03;
    }

    It adds a minor delay in the rare case it gets stuck on that bit, but it's better to delay a little than to get stuck indefinitely! I hope this clarifies the issue and that i can be helpful to others who have similar problems.

    Kind regards,

    Martin Janse

**Attention** This is a public forum