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.

MSP430G2553: Use I2C to interface accelerometer and gas sensors and UART to see the values

Part Number: MSP430G2553
Other Parts Discussed in Thread: MSP430FR2433

Tool/software:

Hello everyone!

I'm interfacing the MSP with MPU-6050 and the CCS811 gas sensor, and want to see the values via UART.

For this purpose, I coded two functions, one to convert the integer values to string and another to print using the UART module; in the main function, I made this way:

while (1)
  {
    acc_comm();
    //int_to_string(xAccel, num);
    //uart_puts(num);
    //int_to_string(yAccel, num);
    //uart_puts(num);
    //int_to_string(zAccel, num);
    //uart_puts(num);
    gases_comm();
    //int_to_string(co2Lvl, num);
    //uart_puts(num);
    //int_to_string(tvocLvl, num);
    //uart_puts(num);
  
    // do something with the data
    __delay_cycles(500000);
  }

When I uncomment the function calls, the communication fails.

I'd like to know what causes this issue to happen. I suppose that the I2C lines cannot be on idle, but don't have confirmations of this; any help of you guys will be very much appreciated.

For reference, I'll leave the two functions code here:

void int_to_string(int num, unsigned char *str)
{
  unsigned char i = 0, j = 0;
  char aux[5];

  do
  {
    aux[i++] = num % 10 + '0';  
    num /= 10;
  }
  while(num > 0);
  
  while(i--)
  {
  	str[j++] = aux[i];
  }
  str[j] = '\0';
}

void uart_puts(unsigned char *str)
{
  while(*str)
  {
    while(!(IFG2 & UCA0TXIFG));
    UCA0TXBUF = *str++;
  }
  UCA0TXBUF = '\n';
}

Also, the configuration code of I2C and UART are the same of the examples in the TI developer zone.

  • In what way does the communication fail? (Hang? NACK? Invalid data?) Which TI example did you adapt/adopt? (I recall there are a few.)

    How is "num" (in main) declared? Is a buffer overflow possible?

    The CCS811 register abstraction is a bit unusual; I wonder if there are some corner cases in there.

  • Hi!

    The communication seems to not occur when applying the functions.

    The image below shows the logic analyzer output:

    Regarding the examples used, for UART I'm just using the configuration for 9600 of baud and 1 MHZ, without ISR; for I2C, Is better to share the code:

    //*********************************************************************************************
    void i2cInit(void)
    {
      // set up I2C pins
      P1SEL |= BIT6 | BIT7;         // Assign I2C pins to USCI_B0
      P1SEL2|= BIT6 | BIT7;         // Assign I2C pins to USCI_B0
    
      // set up I2C module
      UCB0CTL1 |= UCSWRST;        // Enable SW reset
      UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;         // I2C Master, synchronous mode
      UCB0CTL1 = UCSSEL_2 | UCSWRST;      // Use SMCLK, keep SW reset
      UCB0BR0 = 10;         // fSCL = SMCLK/10 = ~100kHz
      UCB0BR1 = 0;
      UCB0CTL1 &= ~UCSWRST;       // Clear SW reset, resume operation
      IE2 |= UCB0RXIE | UCB0TXIE;
    }
    
    //*********************************************************************************************
    void i2cWrite(unsigned char address)
    {
      IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);     // Clear pending interrupts
      UCB0I2CSA = address;        // Load slave address
      IE2 |= UCB0TXIE;        // Enable TX interrupt
      while(UCB0CTL1 & UCTXSTP);      // Ensure stop condition sent
      UCB0CTL1 |= UCTR | UCTXSTT;     // TX mode and START condition
      __bis_SR_register(CPUOFF | GIE);    // sleep until UCB0TXIFG is set ...
    }
    
    //*********************************************************************************************
    void i2cRead(unsigned char address)
    {
      IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);     // Clear pending interrupts
      UCB0I2CSA = address;        // Load slave address
      IE2 |= UCB0RXIE;        // Enable RX interrupt
      while(UCB0CTL1 & UCTXSTP);      // Ensure stop condition sent
      UCB0CTL1 &= ~UCTR;        // RX mode
      UCB0CTL1 |= UCTXSTT;        // Start Condition
      __bis_SR_register(CPUOFF | GIE);    // sleep until UCB0RXIFG is set ...
    }
    
    /**********************************************************************************************/
    // USCIAB0TX_ISR
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void)
    {
      if(UCB0CTL1 & UCTR)         // TX mode (UCTR == 1)
      {
        if (TX_ByteCtr)               // TRUE if more bytes remain
        {
          TX_ByteCtr--;       // Decrement TX byte counter
          UCB0TXBUF = TX_Data[TX_ByteCtr];  // Load TX buffer
        }
        else            // no more bytes to send
        {
          UCB0CTL1 |= UCTXSTP;      // I2C stop condition
          IFG2 &= ~UCB0TXIFG;     // Clear USCI_B0 TX int flag
          __bic_SR_register_on_exit(CPUOFF);  // Exit LPM0
        }
      }
      else // (UCTR == 0)         // RX mode
      {
        RX_ByteCtr--;               // Decrement RX byte counter
        if (RX_ByteCtr)               // RxByteCtr != 0
        {
          RX_Data[RX_ByteCtr] = UCB0RXBUF;  // Get received byte
          if (RX_ByteCtr == 1)      // Only one byte left?
            UCB0CTL1 |= UCTXSTP;      // Generate I2C stop condition
        }
        else            // RxByteCtr == 0
        {
          RX_Data[RX_ByteCtr] = UCB0RXBUF;  // Get final received byte
          IFG2 &= ~UCB0RXIFG;     // Clear RX flag
          //int_to_str(RX_Data, num);
          //uart_puts(num);
          __bic_SR_register_on_exit(CPUOFF);  // Exit LPM0
        }
      }
    }
     

    These functions and ISR works for both sensors, only switching the addresses is needed.

    Array num is declared as unsigned char; and regarding to buffer overflow, that's a good question.

  • If you pause in the debugger, where is the program executing? It seems odd that adding code after the first transaction causes the first transaction to fail.

  • Hi!

    I'm using platformIO for development, and I couldn't use the debugger, although following the documentation provided.

    The CCS Studio don't work for my MCU on Linux, and it was confirmed in some threads here that it is due to microcontroller obsolescence.

    I executed the code one more time to validate one observation and guess what? It is working (Unexplainably)!

    But, the 3 pairs of convertion and printing that separates the sensors readings sum 10 millis of delay. Maybe this hanging causes the communication to fail?

    This is pretty hard without a debugger.

  • I'm not familiar with platformIO, but since it refers to being a "Framework" I wonder if it imposes some stack size cost. The relatively small SRAM in the G2553 can make balancing the stack size tricky.

    I have no trouble working with the G2553 using CCS 12.6.0, but that's on Windows. platformIO also claims to be capable of debugging using the Launchpad (onboard debug interface), but maybe it's "not all there"?

    That said, if you're stuck with (what we used to call) "printf debugging", I'd start by scattering around calls like 'uart_puts("Step 1\r\m")' to see how far you get. uart_puts() has the advantage of being synchronous and requiring very little stack. The TI compiler puts constant strings in Flash, so there's no SRAM penalty.

    How is "num" in main declared? I understand it's "unsigned char num[<what?>];". The value of <what?> may be a clue.

  • Yes, the documentation says so but I don't know if there has some missteps from me, but following the references and adding solutions of errors documented in foruns wasn't enough.

    I declared the array this way:

    unsigned char num[] = {'0','0','0','0','0','\0'};

    Both sensors has the relevant data 16 bits long, so I believe an array of 5 positions will suffice.

    Right now, I'm investigating the weird and faulty output of some data, the execution seems stable.

    I'll provide a way of using Windows OS to use CCS, and study about the possible stack size cost and buffer overflow. Do you think the communication fail might be caused by memory issues?

    By the way, thanks for your help so far.

  • I don't have your equipment, nor your development tools (nor your entire source code), so all I can do is make general statements:

    1) If you Never see any activity on the I2C, the first guess is that your program isn't running (progressing).

    2) Stack or buffer overflow produce unpredictable results, one of which might be not-running (spinning in uninitialized memory, resetting repeatedly very fast, stuck in LPM).

    3) The SRAM on the G2553 is rather limited.

    So I'd want to know what the program thinks it's doing. If you have a debugger, you can just pause and look. Otherwise, a collection of uart_put() progress indicators can help.

  • Hi! Sorry for the delay.

    Tried to download ccs 12.7 on a windows, still couldn't use the debugger for the MCU.

    Now I changed the target, using the MSP430FR2433, don't know if this thread still suits or I'll have to create another.

    I'll migrate the code used to this new MCU, and right now I'm coding the I2C interface only to communicate with the accelerometer at the moment, and using the debugger of the ccs I had installed in my Linux and verified that UCBBUSY is set in the second iteration of data acquisition.

    I already putted 10k rsistors and checked the pins configuration, as stated here.

    Below is the configuration of I2C:

    /**
     * Configuration of I2C module
     */
    void i2c_conf(void)
    {
        // I2C pins configuration
        P1SEL0 |= BIT2 | BIT3;
    
        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings
        PM5CTL0 &= ~LOCKLPM5;
    
        // Configure USCI_B0 for I2C mode
        UCB0CTLW0 |= UCSWRST;                   // Software reset enabled
        UCB0CTLW0 |= UCMODE_3 | UCMST;          // I2C mode, Master mode
        UCB0CTLW0 |= UCSSEL__SMCLK | UCSYNC;    // Use SMCLK as clock source, sync
        UCB0CTLW1 |= UCASTP_2;                  // Automatic stop generated by
                                                // reaching data acquisition boundary
        UCB0BRW = 0x000A;                       // baudrate = SMCLK / 10 = 100 kHz
        UCB0CTL1 &= ~UCSWRST;                   // Disable SW reset
        UCB0IE |= UCTXIE | UCRXIE | UCBCNTIE;   // Enable Tx, RX, and count interruptions
    }

    And here is the ISR; the write and read functions remain unchanged, except that UCB0TBCNT receives byteCtr

    /**
     * UCB0 ISR
     */
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCIB0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCIB0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
      {
        case USCI_I2C_UCRXIFG0:
          RX_ByteCtr--;
          if(RX_ByteCtr)
          {
            RX_Data[RX_ByteCtr] = UCB0RXBUF;    // Store RX'd byte
          }
          break;
        case USCI_I2C_UCTXIFG0:
          if(TX_ByteCtr)
          {
            TX_ByteCtr--;
            UCB0TXBUF = TX_Data[TX_ByteCtr];    // Load TX buffer
          }
          break;
        case USCI_I2C_UCBCNTIFG:
            __bic_SR_register_on_exit(CPUOFF);
            break;
      }
    }

  • 1) Keep in mind that UCB0TBCNT can only be changed when the I2C unit is in Reset (UCSWRST=1). I don't see you setting it in i2c_conf().

    2) (a) UCBCNTIFG is set before the Stop condition is (automatically) issued after the last byte, (b) the Stop takes some time, (c) the eUSCI is sensitive to issuing a Start while a Stop is pending.

    That's why you'll often see this in the setup for a new operation:

    >  while (UCB0CTLW0 & UCTXSTP)/*EMPTY*/;   // Wait for previous operation (Stop) to complete

  • I removed the UCB0TBCNT interrupt enable and handling. Now I'm in trouble with UCBBUSY and UCSCLLOW being set after UCB0CTLW0 |= UCTR | UCTXSTT. Tried accessing through 8 bit version (UCB0CTL1) and got the same issue.

  • Are you resetting the I2C (UCSWRST=1, then =0) around setting UCB0TBCNT? If not it's probable that UCB0TBCNT==0, and I'm not quite sure what UCASTP=2 does with that (I haven't tried it). Keep in mind that resetting the I2C sets UCB0IE=0, so you have to put those back.

    Are you waiting for UCTXSTP to clear before starting (UCTXSTT) a new operation?

    I wrote code for an FR2433 to talk to an MPU-6050 a few weeks ago [owned by someone else now], and I don't remember anything particularly unusual about the device. Maybe it would be useful for you to post your I2C read/write functions.

  • Certainly, here it is:

    /**
     * Write function
     */
    void i2c_wr(unsigned char addr)
    {
        UCB0CTL1 |= UCSWRST;
        UCB0I2CSA = addr;                   // Slave address
        UCB0TBCNT = TX_ByteCtr;
        UCB0CTL1 &= ~UCSWRST;
        UCB0IE |= UCTXIE | UCRXIE;              // Enable Tx and RX interruptions
    
        while(UCB0CTL1 & UCTXSTP);          // Ensure stop condition sent
    
        UCB0CTL1 |= UCTR;        // Transmitter mode
        UCB0CTL1 |= UCTXSTT;     // and send START condition
    
        __bis_SR_register(LPM0_bits|GIE);   // Enter LPM0 w/ interrupt
    }
    /**
     * Read function
     */
    void i2c_rd(unsigned char addr)
    {
        UCB0CTL1 |= UCSWRST;
        UCB0I2CSA = addr;                   // Slave address
        UCB0TBCNT = RX_ByteCtr;
        UCB0CTL1 &= ~UCSWRST;
        UCB0IE |= UCTXIE | UCRXIE;              // Enable Tx and RX interruptions
    
        while(UCB0CTL1 & UCTXSTP);          // Ensure stop condition sent
    
        UCB0CTL1 &= ~UCTR;                  // Receiver mode
        UCB0CTL1 |= UCTXSTT;               // and send START condition
    
        __bis_SR_register(LPM0_bits|GIE);   // Enter LPM0 w/ interrupt
    }
    

    UCBBUSY and UCSCLLOW still being set. START is being sent (maybe?), because in the debugger UCTXSTT is clear.

  • This looks reasonable; I suggest moving the UCTXSTP check to precede the reset (UCSWRST) since the reset might interfere with the (ongoing) Stop.

    Looking back at the ISR, this appears to be off-by-1:

        case USCI_I2C_UCRXIFG0:
          RX_ByteCtr--;
          if(RX_ByteCtr)
          {
            RX_Data[RX_ByteCtr] = UCB0RXBUF;    // Store RX'd byte
          }
          break;
    

    If e.g. you start off with RxByteCtr==1, then it won't read UCB0RXBUF at all. Reading RXBUF ties into the flow control, so that could hang the bus. You should probably move the decrement inside the if() block.

  • Here's the updated functions and the ISR:

    /**
     * Write function
     */
    void i2c_wr(unsigned char addr)
    {
        while(UCB0CTL1 & UCTXSTP);          // Ensure stop condition sent
    
        UCB0CTL1 |= UCSWRST;
        UCB0I2CSA = addr;                   // Slave address
        UCB0TBCNT = TX_ByteCtr;
        UCB0CTL1 &= ~UCSWRST;
        UCB0IE |= UCTXIE | UCRXIE | UCBCNTIE;              // Enable Tx and RX interruptions
    
        UCB0CTL1 |= UCTR;        // Transmitter mode
        UCB0CTL1 |= UCTXSTT;     // and send START condition
    
        __bis_SR_register(LPM0_bits|GIE);   // Enter LPM0 w/ interrupt
    }
    /**
     * Read function
     */
    void i2c_rd(unsigned char addr)
    {
        while(UCB0CTL1 & UCTXSTP);          // Ensure stop condition sent
    
        UCB0CTL1 |= UCSWRST;
        UCB0I2CSA = addr;                   // Slave address
        UCB0TBCNT = RX_ByteCtr;
        UCB0CTL1 &= ~UCSWRST;
        UCB0IE |= UCTXIE | UCRXIE | UCBCNTIE;              // Enable Tx and RX interruptions
    
        UCB0CTL1 &= ~UCTR;                  // Receiver mode
        UCB0CTL1 |= UCTXSTT;               // and send START condition
    
        __bis_SR_register(LPM0_bits|GIE);   // Enter LPM0 w/ interrupt
    }
    /**
     * UCB0 ISR
     */
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCIB0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCIB0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
      {
        case USCI_I2C_UCRXIFG0:
                if (RX_ByteCtr--)
                {
                    RX_Data[RX_ByteCtr] = UCB0RXBUF;  // Get received byte
                }
                break;
        case USCI_I2C_UCTXIFG0:
                if (TX_ByteCtr--)               // TRUE if more bytes remain
                {
                    UCB0TXBUF = TX_Data[TX_ByteCtr];  // Load TX buffer
                }
            break;
        case USCI_I2C_UCBCNTIFG:
            __bic_SR_register_on_exit(CPUOFF);  // Exit LPM0
            break;
      }
    }

    And resulted in this:

    The data line remained high, but the clock is being generated.

    In a second try, got this:

    And in a third try, I got receive the accel values.

    This is too strange...

  • The top one looks particularly odd, since there are no Start/Stop conditions, but SCL looks reasonable.

    Are you resetting the MPU-6050 (register 0x6B)? If so, are you waiting for the device to start up? The datasheet claims 30ms, but as I recall I gave it more like 100ms. 

  • I added a delay in the sensor's wake-up block. Now it appears to work properly, thank you sir.

    Now I'm going to add the gases sensor registers and UART. I think It's better to create another thread.

  • I'm glad you got it working. I expect the next device will be easier.

    The only thing that looks unusual about the CCS811 is that it has multi-byte registers ("mailboxes") which are not consecutive bytes in the register space, which means you can't read multiple registers in a single transaction (maybe that's not important).

  • Hello!

    I created another thread to treat about this subject, because I believe it is different from the original issue.

    Click here to access.

    Again, thanks for your help so far!

**Attention** This is a public forum