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.

CCS/MSP430F5529: I2C communication issue

Part Number: MSP430F5529
Other Parts Discussed in Thread: MSP430G2452

Tool/software: Code Composer Studio

Hi,

I'm struggeling with making i2c communication work properly. I'm working on MSP430F5529, I2C target device is DS2482-100 but right now I'm testing I2C communication on LCD (because I'm sure hardware connection and commands are fine, I got it working with MSP430G2452). 

The code I'm running is:

#include <msp430.h>

volatile unsigned char PTxData;                     // Pointer to TX data
volatile unsigned char TXByteCtr;
unsigned int i = 0;
const unsigned char TxData[] =              // Table of data to transmit
        { 0x78, 0xF1, 0x67, 0xC0, 0x40, 0x50, 0x2B, 0xEB, 0x81, 0x5F, 0x89, 0xAF };

int main(void)
{

    UCB0IE = 0x00;
    WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    P3SEL |= 0x03;
    while (1)
    {
        _disable_interrupts();                 // Assign I2C pins to USCI_B0
        UCB0CTL1 |= UCSWRST;                      // Enable SW reset
        UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;    // I2C Master, synchronous mode
        UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
        UCB0BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
        UCB0BR1 = 0;
        UCB0I2CSA = 0x60;                         // Slave Address is 048h
        UCB0CTL1 &= ~UCSWRST;                // Clear SW reset, resume operation
        UCB0IE |= UCTXIE;                         // Enable TX interrupt
        UCB0IFG = 0xFF;
        _enable_interrupts();

        unsigned int over = 0;
        _delay_cycles(100000);
        over = 0;
        i = 0;
        while (over == 0)
        {
            _delay_cycles(1000);           // Delay required between transaction
            PTxData = TxData[i];
            TXByteCtr = sizeof TxData;
            UCB0CTL1 |= UCTR + UCTXSTT;
            i++;
            if (i > TXByteCtr)
                over = 1;
            while (UCB0CTL1 & UCTXSTP)
                ;             // Ensure stop condition got sent
        }
    }
}


#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch (__even_in_range(UCB0IV, 12))
    {
    case 0:
        break;                           // Vector  0: No interrupts
    case 2:
        break;                           // Vector  2: ALIFG
    case 4:
        break;                           // Vector  4: NACKIFG
    case 6:
        break;                           // Vector  6: STTIFG
    case 8:
        break;                           // Vector  8: STPIFG
    case 10:
        break;                           // Vector 10: RXIFG
    case 12:                             // Vector 12: TXIFG
        if (TXByteCtr)                          // Check TX byte counter
        {
            UCB0TXBUF = PTxData;               // Load TX buffer
            TXByteCtr--;                          // Decrement TX byte counter
        }
        else
        {
            UCB0CTL1 |= UCTXSTP;                  // I2C stop condition
            UCB0IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
            __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
        }
    default:
        break;
    }
}

This:

const unsigned char TxData[] =              // Table of data to transmit
        { 0x78, 0xF1, 0x67, 0xC0, 0x40, 0x50, 0x2B, 0xEB, 0x81, 0x5F, 0x89, 0xAF };

are commands that should init LCD and set random pixels on it but nothing happens. 

I only have single channel osciloscope, these are readings of SDA and SCL lines:

Time laps differ for SDA (50us) and SCL(20us). 

What am I doing wrong?

  • Hi Toann,
    It looks like you may be missing pull up resistors on SCL. I2C should idle HIGH.
  • Hi Cameron,
    I have 4k7 resistors on both SDA and SCL lines (Vcc=3.3V).
    Best regards,
    toann
  • I'd verify your connections again, because that clock should be high, but it looks like it's being held low
  • And what about launchpad jumpers? Should I keep all as it was or should I change something? Previous photos were with all jumpers as they were, now I'm thinking maybe it's wrong? I tried to run programm with RXD and TXD jumpers taken off, here's what I got:

    This was both SCL line. 

    Best regards,

    toann

  • It almost seems like the slave is holding the SCL line low, do you see this when using the G2 device?
  • I don't have G2 with me right now but it wasn't like that, signal changes in SCL (in one package) were next to each other.
    Best regards,
    toann
  • I commented line:

    UCB1IE |= UCTXIE;    

    because the program was stacking in 

    __TI_ISR_TRAP:
            BIS.W     #(0x0010),SR
            JMP __TI_ISR_TRAP //<-here

    Then I checked registers. It looks like the value in UCB1TXBUF is changing but the output (SDA line) is the value of UCB1I2CSA all the time. I have also set some breakpoints:

    case 12:                             
            if (TXByteCtr)                          
            {
                UCB1TXBUF = PTxData;               //<- here
                TXByteCtr--;                          
            }
            else
            {
                UCB1CTL1 |= UCTXSTP;                 
                UCB1IFG &= ~UCTXIFG;                  //<- and here
                __bic_SR_register_on_exit(LPM0_bits); 
            }

    but the program never stopped there. 

    Whole code right now:

    const unsigned char TxData[] =              // Table of data to transmit
            {  0x78 ,  0xF1 ,  0x67 ,  0xC0 ,  0x40 ,  0x50 ,  0x2B ,  0xEB ,  0x81 ,  0x5F ,  0x89 ,  0xAF  };
    
    int main(void)
    {
    
        UCB1IE = 0x00;
        WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
        P4SEL |= 0x06;
        while (1)
        {
            __disable_interrupt();                 // Assign I2C pins to USCI_B0
            UCB1CTL1 |= UCSWRST;                      // Enable SW reset
            UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC;    // I2C Master, synchronous mode
            UCB1CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
            UCB1BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
            UCB1BR1 = 0;
            UCB1I2CSA = 0x78;                         // Slave Address is 048h
            UCB1CTL1 &= ~UCSWRST;                // Clear SW reset, resume operation
          // UCB1IE |= UCTXIE;                         // Enable TX interrupt
            UCB1IFG = 0x00;
           __enable_interrupt();
    
            unsigned int over = 0;
            _delay_cycles(100000);
            over = 0;
            i = 0;
            while (over == 0)
            {
                _delay_cycles(100);           // Delay required between transaction
                PTxData = TxData[i];
                UCB1TXBUF = PTxData;
                TXByteCtr = sizeof TxData;
                UCB1CTL1 |= UCTR + UCTXSTT;
                i++;
                if (i > TXByteCtr)
                    over = 1;
                while (UCB1CTL1 & UCTXSTP)
                    ;             // Ensure stop condition got sent
            }
        }
    }
    
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCI_B0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
        switch (__even_in_range(UCB1IV, 12))
        {
        case 0:
            break;                           // Vector  0: No interrupts
        case 2:
            break;                           // Vector  2: ALIFG
        case 4:
            break;                           // Vector  4: NACKIFG
        case 6:
            break;                           // Vector  6: STTIFG
        case 8:
            break;                           // Vector  8: STPIFG
        case 10:
            break;                           // Vector 10: RXIFG
        case 12:                             // Vector 12: TXIFG
            if (TXByteCtr)                          // Check TX byte counter
            {
                UCB1TXBUF = PTxData;               // Load TX buffer
                TXByteCtr--;                          // Decrement TX byte counter
            }
            else
            {
                UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
                UCB1IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
                __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
            }
        default:
            break;
        }
    }

    Oscilloscope readings:

  • > #pragma vector = USCI_B0_VECTOR
    You need to change this for UCB1:
    > #pragma vector = USCI_B1_VECTOR
    then put back the UCB1IE assignment.
    ---------------------
    > UCB1CTL1 |= UCTR + UCTXSTT;
    This restarts the transaction for every byte, so all you'll ever see is the I2CSA byte (repeatedly). I recommend you move this outside the loop.
    ---------------------
    More generally, I think you'll find it very difficult to manage the array pointer index and length (i and TXByteCtr) partly in main and partly in the ISR. I recommend you move all this into the ISR and have main() set these once, then either (1) go into LPM and/or (2) have the ISR set "over" when it's done.
  • Okay, so I moved all from main() into ISR:

     UCB1CTL1 |= UCTR + UCTXSTT;
            while (over == 0)
            {
                _delay_cycles(100);           // Delay required between transaction
            }


    case 12: // Vector 12: TXIFG TXByteCtr = sizeof TxData; while (over == 0) { if (i < TXByteCtr) // Check TX byte counter { UCB1TXBUF = TxData[i]; // Load TX buffer i++; //TXByteCtr--; // Decrement TX byte counter while (UCB1CTL1 & UCTXSTP) ; } else { over = 1; UCB1CTL1 |= UCTXSTP; // I2C stop condition UCB1IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag over = 1; __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 } }

    SCL is still being held down for some reason but finally something else than 0x78 showed up on SDA.

  • The startup in main should look something like:

    > i = 0; // start back at the beginning
    > TXByteCtr = sizeof TxData; // Full buffer
    > over = 0; // Not complete yet
    > UCB1CTL1 |= UCTR + UCTXSTT; // Transmit + start condition

    The ISR should look pretty much the same as before (no loops), except that when issuing the UCTXSTP also set over=1.

    Don't forget to declare "over" as "volatile".

    Example MSP4320F55xx_uscib0_i2c_08.c (SLAC300G, "Tools and Software" on the product page) does something very similar. You might save yourself some typing by just adopting it.
  • I still didn't manage to get it working :( I tried MSP4320F55xx_uscib0_i2c_08.c, also didn't work. I first of all I figured out I had some problems with timing (that's why SCL seemed to be hold by slave, it was waiting for response that couldn't be sent because of bad timing). Now the timing is fine, SCL looks good but SDA keeps sending slave address instead of data I'm expecting it to be sending.

    Can you please check again if I'm not making some stupid mistakes?

    const unsigned char TxData[] =              // Table of data to transmit
            { 0x00,0xF1, 0x67};
    int main(void)
    {
    
        UCB1IE = 0x00;
        WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
        P4SEL |= 0x06;
        initClock();
        while (1)
        {
            __disable_interrupt();                 // Assign I2C pins to USCI_B0
            UCB1CTL1 |= UCSWRST;                      // Enable SW reset
            UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC;    // I2C Master, synchronous mode
            UCB1CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
            UCB1BR0 = 32;                             // fSCL = SMCLK/12 = ~100kHz //2===209k, 3==200k, 6==40k
            UCB1BR1 = 32;
            UCB1I2CSA = 0x7A;                         // Slave Address is 048h
            UCB1CTL1 &= ~UCSWRST;                // Clear SW reset, resume operation
            UCB1IE |= UCTXIE;                         // Enable TX interrupt
            UCB1IFG = 0x00;
            __enable_interrupt();
            UCB1CTL1 |= UCTR + UCTXSTT;
            TXByteCtr = sizeof TxData;
            if (over == 1)
            {
               over = 0;
                __delay_cycles(100000);
                __delay_cycles(100000);
            }
            P2OUT |=128;
    
        }
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
        switch (__even_in_range(UCB1IV, 12))
        {
        case 0:
            break;                           // Vector  0: No interrupts
        case 2:
            break;                           // Vector  2: ALIFG
        case 4:
            break;                           // Vector  4: NACKIFG
        case 6:
            break;                           // Vector  6: STTIFG
        case 8:
            break;                           // Vector  8: STPIFG
        case 10:
            break;                           // Vector 10: RXIFG
        case 12:                             // Vector 12: TXIFG
           if (i < TXByteCtr)                          // Check TX byte counter
            {
                UCB1TXBUF = TxData[i];               // Load TX buffer
                i++;
                //TXByteCtr--;                          // Decrement TX byte counter
                while (UCB1CTL1 & UCTXSTP)
                    ;
            }
            else
            {
                over = 1;
                UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
                UCB1IFG &= ~UCTXIFG;                // Clear USCI_B0 TX int flag
                i = 0;
                __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
            }
    
        default:
            break;
        }
    }

  • What exactly about the euscib_i2c_08.c example didn't work? At first glance it appears to do everything you need; all you need to do is paste in the Tx data you want.
    ------------
    You're (still) not waiting for the transaction to complete before starting another one. That's why all you're sending is one byte. euscib_i2c_08.c does this with:
    > __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupts
    ------------
    > UCB1BR0 = 32; // fSCL = SMCLK/12 = ~100kHz //2===209k, 3==200k, 6==40k
    > UCB1BR1 = 32;
    This sets the divider to 0x2020=8224, or about 1.5kHz. I suspect you intended to set UCB1BR1=0 instead.
  • When I try debug this example, program never reaches __no_operation(), it hangs somewhere between leaving "case 12" and __no_operaton(). When I paused debugging, all that was there was "No source available for "0x2402". At the same time register UCB0TXBUF contains first element of TxData.

  • Did you remember to change the slave address (UCB0I2CSA)? That would produce a symptom similar to what you describe.
  • Yes, all I changed was slave address, TxData and B0 register to B1.

    #include <msp430.h>
    
    unsigned char *PTxData; // Pointer to TX data
    unsigned char TXByteCtr;
    
    const unsigned char TxData[] = // Table of data to transmit
            { 0xF1, 0x67, 0xC0, 0x40, 0x50, 0x2B, 0xEB, 0x81, 0x5F, 0x89, 0xAF };
    
    int main(void)
    {
        unsigned int i;
    
        WDTCTL = WDTPW + WDTHOLD; // Stop WDT
        P4SEL |= 0x06; // Assign I2C pins to USCI_B1
        UCB1CTL1 |= UCSWRST; // Enable SW reset
        UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
        UCB1CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
        UCB1BR0 = 2; // fSCL = SMCLK/12 = ~100kHz
        UCB1BR1 = 0;
        UCB1I2CSA = 0x78; // Slave Address is 048h
        UCB1CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
        UCB1IE |= UCTXIE; // Enable TX interrupt
    
        while (1)
        {
            for (i = 0; i < 10; i++)
                ; // Delay required between transaction
            PTxData = (unsigned char *) TxData; // TX array start address
                                                // Place breakpoint here to see each
                                                // transmit operation.
            TXByteCtr = sizeof TxData; // Load TX byte counter
    
            UCB1CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
    
            __bis_SR_register(LPM0_bits + GIE);
            // Enter LPM0, enable interrupts
            __no_operation(); // Remain in LPM0 until all data
                              // is TX'd
            while (UCB1CTL1 & UCTXSTP)
                ; // Ensure stop condition got sent
        }
    }
    
    //------------------------------------------------------------------------------
    // The USCIAB1TX_ISR is structured such that it can be used to transmit any
    // number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
    // points to the next byte to transmit.
    //------------------------------------------------------------------------------
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
        switch (__even_in_range(UCB1IV, 12))
        {
        case 0:
            break; // Vector  0: No interrupts
        case 2:
            break; // Vector  2: ALIFG
        case 4:
            break; // Vector  4: NACKIFG
        case 6:
            break; // Vector  6: STTIFG
        case 8:
            break; // Vector  8: STPIFG
        case 10:
            break; // Vector 10: RXIFG
        case 12: // Vector 12: TXIFG
            if (TXByteCtr) // Check TX byte counter
            {
                UCB1TXBUF = *PTxData++; // Load TX buffer
                TXByteCtr--; // Decrement TX byte counter
            }
            else
            {
                UCB1CTL1 |= UCTXSTP; // I2C stop condition
                UCB1IFG &= ~UCTXIFG; // Clear USCI_B1 TX int flag
                __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
            }
        default:
            break;
        }
    }
    

  • > UCB1I2CSA = 0x78; // Slave Address is 048h
    In your other code you used
    > UCB1I2CSA = 0x7A; // Slave Address is 048h
    -------
    Once you get to the state you described, display UCB1IFG ("Registers->EUSCIB1") and see if NACKIFG is set.
  • toann,
    Did changing the address solve your issue?
  • Bruce,
    My mistake, I was checking both addresses (0x78 is for sending commands, 0x7A is for data), neigther works. I checked UCNACKIFG flag, it is 1.

    Cameron,
    No, it didn't.
  • What LCD are you working with?
  • Based on the F5 User Guide (SLAU208P) Table 38-11 and the LCD data sheet p. 5 (tables at bottom) I think you want to use I2CSA=0x3C (Command) and 0x3D (Data). The USCI will shift that value and OR in the (inverted) UCTR bit for you.

    This is different from the USI since for that you need to construct your own address byte (doing the shift+OR yourself).

    [Edit: Fix UCTR sense.]

  • It worked, thank you so much!

**Attention** This is a public forum