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.

F5438 I2C Issue

Other Parts Discussed in Thread: MSP430F249, TMP100

Hi,

I am using the USCI B1 in I2C mode on the F5438 and I am observing a behaviour that is different from what specified in the SLAU208D User guide.

In I2C Master Transmitter Mode (page 478) is showed that after setting UCTXSTT=1 and writing a value in the TX Buffer, two different things (in single master) can happen:

  1. Slave Address is ACK'ed, UCTXSTT=0 and UCTXIFG is set.
  2. Slave Address is not ACK'ed, UCTXSTT=0, UCTXIFG stays reset and UCNACKIFG is set.

I can successfully replicate the 1st case.

However, when the ACK is not received after the slave address transmission (for example if no slave is present), UCTXSTT stays set, UCNACKIFG is not set and my software gets stuck.

 

This is what I do:

UCB1CTL1 |= UCTR;
UCB1CTL1 |= UCTXSTT;
while( (UCB1IFG & UCTXIFG) == 0 );
UCB1TXBUF = value;

while(UCB1CTL1 & UCTXSTT);

 

The software gets stuck on the 2nd while with the flags value written above.

I dont think it is because of some wrong configuration of the I2C peripheral, because in normal conditions it works fine.

 

Do you have some ideas?

  • I am not sure what might be causing this and I don't suspect the software, but just as a sanity check to rule out any variables, you can use the code example "MSP430x54x_uscib0_i2c_06.c" and "MSP430x54x_uscib0_i2c_07.c" from the following zip file:
    http://www.ti.com/lit/zip/slac166

    If possible, please use two MSP430's to test this out (or 2 USCI_Bs on the same chip). You can do the following tests with this software:
    1. Change the Slave address so a NACK is received when run "as-is" using the interrupt method and monitor this on a scope
    2. Change the code to a polling type method like you have to see the results

  • Hi Cyberstorm,

     

    I use USCI B3 in I2C mode on F5419   and I use interrupt, so the system works in the follow way:

     

    1. after  UCB3CTL1 |= (UCTR | UCTXSTT)  there is an interrupt which shows UCTXIFG ON

    2.  data is sent doing UCB3TXBUF = value

    3.  there is another interrupt with UCTXIFG ON and data is sent until the routine decides to send a STOP

     

    I guess your code should be

     UCB1CTL1 |= UCTR;
    UCB1CTL1 |= UCTXSTT;
    while( (UCB1IFG & UCTXIFG) == 0 );   or   while(UCB1CTL1 & UCTXSTT);
    UCB1TXBUF = value;

    while( (UCB1IFG & UCTXIFG) == 0 );

     

    because  UCTXSTT will be OFF after all bits of START are sent.

  • hi guys,

    thanks for your answer. I have written the code both with ISR and flag polling and they both works.

    If master and slave are set up correctly,  after some time (like after 100 write-read session) the ISR variant  gets stuck with STT flag never reset. The polling code instead seems to continue to work, but I haven't done long endurance tests so far.

    Digging into the problem I described in the 1st post, I noticed that that the fault condition happens when the UCBBUSY remains set before sending the START condition. I noticed that it, in these conditions, the UCBBUSY is set immediately after clearing the UCSWRST or between two I2C sessions. I dont know how this could happen, since all my code issues a STOP condtion at the end of each I2C session.

    I've then noticed the errata USCI26 on SLAZ046C that, while not targeting specifically to my problem, suggests to check UCBBUSY flag before sending a START condition. So a workaround for my code I have added, before the transmission of the START condition, this checks:

    if (UCB1STAT & (UCSCLLOW | UCBBUSY) ){
      return -1;
    }

    Anyway I am still worried that the stuck STT flag can happen with a repeated START condition.

  • Unluckily I still have problems.
    Problem is that the code gets stuck unpredictably on the "LMP3;" instruction exactly after having sent the first byte. That is, Start condition is generated, UCTXIFG is set for the first time, the ISR writes into TX Buffer the first byte and from now on nothing happens.

    The problem is that this could happen after hundreds of correct i2c sessions!

    This time I will post you the full source code in case you guys want to take a peek.


    static signed char i2c_write_register(void)
    {
        i2cbuf[0]  = 0xAA;
        i2cbuf[1]  = 0x55;
        i2cbuf[2]  = 0xAA;
        i2cbuf[3]  = 0x55;

        i2cbufcntr = 4;


        if (UCB1STAT & (UCSCLLOW | UCBBUSY) ){                                // SCL is held low or bus is BUSY.
            return -1;
        }
       
        UCB1CTL1 |= UCTR;                                                // Transmitter mode
        UCB1CTL1 |= UCTXSTT;                                         // Start Condition
       
        i2cxfer = 1;
        UCB1IE |= (UCTXIE+UCNACKIE);
       
        while (i2cxfer == 1){
            LPM3;                                                                      // Go in Low power. Here sometimes code gets stuck
        }
       
        UCB1IE &= ~(UCTXIE+UCNACKIE);                    //Transmission is over

    // ..continues...
    }


    #pragma vector = USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    {
        __delay_cycles(10);                                // Workaround for Errata SYS7 (SLAZ046C).
       
      switch(__even_in_range(UCB1IV,12))
      {
      case  0: break;                           // Vector  0: No interrupts
      case  2: break;                           // Vector  2: ALIFG
     
        case  4:                                      // Vector  4: NACKIFG
            i2cfault = 1;
            UCB1IFG &= ~UCNACKIFG;
            UCB1CTL1 |= UCTXSTP;                                       // I2C stop condition
            UCB1IFG &= ~UCTXIFG;                                        // Clear the empty buffer interrupt flag.
            i2cxfer = 0;
            __low_power_mode_off_on_exit();
        break;
      case  6: break;                            // Vector  6: STTIFG
      case  8: break;                                                            // Vector  8: STPIFG
                                  
        case 10:                                                                     // Vector 10: RXIFG
                //...
        break;                          
       
      case 12:                                      // Vector 12: TXIFG
            if (i2cbufcntr){
                i2cbufcntr--;
                UCB1TXBUF = i2cbuf[i2cbufcntr];
            } else {
                UCB1IFG &= ~UCTXIFG;
                i2cxfer = 0;
                __low_power_mode_off_on_exit();
            }
      break;
       
      default: break;
      }
    }

  • I've found that setting or clearing the UCTR bit without setting the UCSWRST bit first results in unpredictable behavior. 

     

     

  • Hi Cyberstorm,

    This is the code I'm using with an I2C  EEPROM (AT24C1024B) and after a week in a hell (eeprom + 5419...) it is working. Maybe you can use part of it (I'm using CrossStudio)

     

    // USCI init

    I2C_UCBxCTL1 = UCSWRST;                                 // disable USCI

    I2C_UCBxCTL0 = UCMST | UCMODE_3 | UCSYNC; // master + I2C + Sync

    I2C_UCBxCTL1 |= UCSSEL_3 | UCSWRST;             // MSCLK

    I2C_UCBxBR0 = 10;                                               // configure divider

    I2C_UCBxBR1 = 0;

    I2C_UCBxCTL1 &= ~UCSWRST;                             // enable USCI

    I2C_UCBxIFG = 0;                                                  // clear flags

    I2C_UCBxIE = UCNACKIE | UCTXIE | UCRXIE;          // interrupts: TX+RX+NACK

     

    void I2C_Interrupt(void) __interrupt[USCI_B3_VECTOR]

    {

    _NOP(); // workaround

    _NOP();

    _NOP();

    _NOP();

    _NOP();

    _NOP();

    switch (I2C_UCBxIV)

    {

       case USCI_I2C_UCNACKIFG:            // NAK interrupt

          I2C_UCBxIFG &= ~UCNACKIFG;

          I2C_UCBxCTL1 |= UCTR |               // transmitter

                                     UCTXSTP |         // tx STOP

                                     UCTXSTT;           // tx START

          while (I2C_UCBxCTL1 & UCTXSTT); // wait ACK

          break;

     

       case USCI_I2C_UCRXIFG:                    // RX interrupt

          I2C_UCBxIFG = 0;

          buff[tx_i++] = I2C_UCBxRXBUF;         // get byte

          if (tx_i == (tx_n - 1))                           // last byte - 1?

          {

             I2C_UCBxCTL1 |= UCTXSTP;           // tx STOP

          }

          else if (tx_i >= tx_n)                             // end reception?

          {

             while (I2C_UCBxSTAT & UCBBUSY); // wait STOP end

             state = I2C_NULL;

             I2C_ok = TRUE;                               // end of reception

          }

          break;

     

     

       case USCI_I2C_UCTXIFG:                      // TX interrupt

          I2C_UCBxIFG &= ~UCTXIFG;

          switch (state)

          {

             case I2C_ADTX_H:

             case I2C_ADRX_H:

                ++state;

                I2C_UCBxTXBUF = I2C_addr_H; // tx MSB address

                break;

     

     

          case I2C_ADTX_L:

          case I2C_ADRX_L:

             ++state;

             I2C_UCBxTXBUF = I2C_addr_L; // tx LSB address

             break;

     

     

          case I2C_TX_DATA:

             if (tx_i < tx_n)                                       // end transmission?

             { // no

                I2C_UCBxTXBUF = buff[tx_i++];          // tx byte

                if (tx_i == tx_n)                                   // last byte?

                {                                                        // yes

                   I2C_UCBxCTL1 |= UCTXSTP;            // tx STOP

                   while (I2C_UCBxSTAT & UCBBUSY); // wait STOP end

                   ++state;

                   I2C_UCBxIFG = 0;

                   I2C_ok = TRUE;                                // end of transmission

                }

             }

             break;

     

          case I2C_TX_ACK: // end the write process

             I2C_UCBxIFG = 0;

             state = I2C_NULL;

             I2C_ok = TRUE;

             break;

     

          case I2C_RX_DATA:                   // send slave address

             ++state;

             I2C_UCBxCTL1 &= ~UCTR;      // receiver

             I2C_UCBxCTL1 |= UCTXSTT;     // tx START

             break;

          }

          break;

        }

    }

     

    void I2C_TxBlock(uchar *pt, ulong addr, ushort num)

    {

       I2C_UCBxCTL0 |= UCMST; // MASTER

       I2C_SetWrite();                  // select WR for EEPROM

       I2C_ok = FALSE;

       state = I2C_ADTX_H;          // transmission init

       buff = pt;                            // TX buffer

       tx_n = num;                       // total of bytes

       tx_i = 0;

       I2C_addr_L = (uchar)(addr & 0xFF);                                      // LSB address

       I2C_addr_H = (uchar)((addr >> 8) & 0xFF);                            // MSB address

       I2C_UCBxI2CSA = (ushort)((addr & 0x010000) ? 0x51 : 0x50); // page 0 or 1

       I2C_UCBxIFG = 0;

       I2C_UCBxCTL1 |= UCTR |     // transmitter

                                  UCTXSTT; // tx START

       while (!I2C_ok);                    // wait the end

     

       // check the write process: tx START+STOP

       I2C_ok = FALSE;

       I2C_UCBxCTL1 |= UCTR |      // transmitter

                                  UCTXSTP | // tx STOP

                                  UCTXSTT;   // tx START

       while (!I2C_ok);

    }

     

    void I2C_RxBlock(uchar *pt, ulong addr, ushort num)

    {

       I2C_SetRead();         // select RD for EEPROM

       I2C_ok = FALSE;

     

       buff = pt;                  // RX buffer

       tx_n = num;             // total of bytes

       tx_i = 0;

       I2C_UCBxIFG = 0;

       I2C_UCBxCTL0 |= UCMST;                 // MASTER

       if (addr == 0xFFFFFFFF)                     // continue from the last position+1?

       {

          I2C_UCBxCTL1 &= ~UCTR;              // receiver

          I2C_UCBxCTL1 |= UCTXSTT;            // tx START

       }

       else

       {

          state = I2C_ADRX_H;                                                          // start reception

          I2C_addr_L = (uchar)(addr & 0xFF);                                      // LSB address

          I2C_addr_H = (uchar)((addr >> 8) & 0xFF);                            // MSB address

          I2C_UCBxI2CSA = (ushort)((addr & 0x010000) ? 0x51 : 0x50); // page 0 or 1

          I2C_UCBxCTL1 |= UCTR |                                                     // transmitter

                                     UCTXSTT;                                                 // tx START

       }

       while (!I2C_ok);                                                                        // wait the end

    }

    .....

    where

    #define I2C_NULL 0

    #define I2C_ADTX_H 1

    #define I2C_ADTX_L 2

    #define I2C_TX_DATA 3

    #define I2C_TX_ACK 4

    #define I2C_ADRX_H 5

    #define I2C_ADRX_L 6

    #define I2C_RX_DATA 7

    buff: "unsigned char pointer"

     

  • Hi,

    I am using Master as both transmitter and Receiver in the same program. I did try enabling both the interrupts (UC1IE |= UCB1RXIE + UCB1TXIE; ), but since there is a single USCIAB1TX_VECTOR, and I dint find (in the user guide or sample codes) which case handles which interrupt. I am using MSP430f249. I checked the header file but I dint find the IV definations in the included header files (there is no UCB1IV  or I2C_UCBxIV as suggested in previous posts).

    Hence I used:

    #pragma vector = USCIAB1TX_VECTOR
    __interrupt void USCIAB1TX_ISR(void)
    {

      if (UC1IFG & UCB1RXIFG)
      {    
          code to handle RX interrupt
      }

      if (UC1IFG & UCB1TXIFG)
      {
          UCB1TXBUF = 0x01;
          UCB1CTL1 |= UCTXSTP;                    // I2C stop condition
          UC1IFG &= ~UCB1TXIFG;
        __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0     
      }

    I have a couple of questions too:I am using MSP430 chip as both Master and slave in the same program.

    ----Code to initialize USCI-B1

    UCB1CTL1 |= UCSWRST;
      UCB1CTL1 |= UCTR;  //TX                         
      UCB1CTL1 &= ~UCSWRST;    
     
      UCB1CTL1 |= UCTR + UCTXSTT;                    // I2C start condition
     
      __bis_SR_register(CPUOFF + GIE);
           
      UCB1CTL1 |= UCSWRST; //if here i couldnt single step
      UCB1CTL1 &= ~UCTR;  //RX again                         
      UCB1CTL1 &= ~UCSWRST;  */


        /// ###################  code  to use MSP430 as Receiver ###############
      while (1)
      {
        RxByteCtr = 2;                          // Load RX byte counter. Read 2 bytes from tmp

        UCB1CTL1 &= ~UCTR;                      // I2C RX
        //while (UCB1CTL1 & UCTXSTP);             // Ensure stop condition got sent
        UCB1CTL1 |= UCTXSTT;                    // I2C start condition
    -------

    --------

    The code is getting stuck when I change from Master Transmitter to Master Receiver.

    Can anyone suggest a solution to this?

    Thanks,

    Deepika

  • My question asked above is solved. I have another question which I am posting at a new post.

    The answer to above question was simply to add a __disable_interrupt();

    --- code to init USCI----


      UCB1CTL1 |= UCTR + UCTXSTT;                    // I2C  TX start
                                                      //here when UCB1TXIFG=1 indiacates that data should be placed in TXBUF.
                                                      //code to send data in TXBUF and send stop signal is in ISR
      while ( UCB1CTL1  & UCTXSTT);
     /* while (UCB1STAT & UCNACKIFG){                 I kept getting NACK here, but the code worked without checking NACKIFG.
        UCB1CTL1 |= UCTXSTP;
        UCB1STAT &= ~UCNACKIFG;
        while (UCB1CTL1 & UCTXSTP);            
        UCB1CTL1 |= UCTXSTT;
        while ( UCB1CTL1  & UCTXSTT);
      } */
      //UCB1TXBUF = 0x01;
      __bis_SR_register(CPUOFF + GIE);

      __disable_interrupt();    //if you dont disable here, then you lose 1st data in RXBUF     
       --code for Master RX from sample code  msp430x24x_uscib0_i2c_01.c

      --code for handling TA0 and USCIAB1TX ISR                        


  • Deepika Ranade86842 said:
    (there is no UCB1IV  or I2C_UCBxIV as suggested in previous posts).

    The other posts refereed to a 5c family MSP. The USCI in 2x family does nto have them. In 5x family, also each USCI has its own ISR that handles all itnerrupts. So there is an USCIA0 vector and an USCIB0 vector rather than AB0RX and AB0TX vectors.

    Deepika Ranade86842 said:
    Hence I used:

    And it looks good so far. For a 2x device.

    Your init code, however, is a bit faulty. You can only set UCTR and UCSTT when you're master. And you don't set the UCMST bit. You also don't select UCMODE_3 (for I2C), yo you're using SPI mode, and all the bits (including UCNACKIFG) have a different meaning - or you didn't post the complete initialization here.
    I don't see you setting a baudrate too, or a slave address. So there's definitely somethign missing in the posted code.

    However, you don't need to set UCSWRST to change the UCTR bit. It is only required for changing baudrate, slave address or the operation mode (SPI/I2C)

    P.s.: if you're getting NACKIFG, then the slave hasn't answered to the request. Usually, this is doe to a wrong slav address. Most datasheeds of I2C devices list the slave address including the R/W bit. The USCI generates this bit automatically based on UCTR, so the slave address to be used is without it (divide the address by two)

     

  • Hi Jens- Michael,

    Yes, I had used the complete initialization routine, with: (maybe i should have used some different notation than --init_code? )

     WDTCTL = WDTPW + WDTHOLD;
      P5SEL |= 0x06;                            // Assign I2C pins to USCI_B1
      UCB1CTL1 |= UCSWRST;                      // Enable SW reset

      UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
      UCB1CTL1 = UCTR + UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
      UCB1BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
      UCB1BR1 = 0;
      UCB1I2CSA = 0x48;                         // Set slave address
      UCB1CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation.

      UC1IE |= UCB1RXIE + UCB1TXIE;             // Changed for TX + RX intr
      TACTL = TASSEL_2 + MC_2;                  // SMCLK, contmode

    Yes, I was getting NACKIFG during write, but not during read. I will try with the other slave address suggested by you.

    Thanks, will keep you posted if this solution works.

  • Thanks Jens-Micheal,

    the 7 bit address was 1001000. Hence I should havae used 0x90 as the slave address?

    But it is working with 0x48 ( referring the example code for TMP100).

    -Deepika

  • Deepika Ranade86842 said:
    the 7 bit address was 1001000. Hence I should havae used 0x90 as the slave address?

    The 7 bit address is the correct one to use. But most datasheets list two 8 bit addresses, including the R/W bit. Using this 8-bit value as slave address is one of the most common mistakes with MSP I2C.
    Using 0x48, the MSP is sending 0x90 for write and 0x91 for read.

**Attention** This is a public forum