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.

i2c hangs up on while (!(IFG2 & UCB0TXIFG));

Other Parts Discussed in Thread: MSP430F2274

HI all I have run into a problem that I have been banging my head against the wall for a couple of week. I have using the MSP430F2274 and the i2c bus and getting mixed results. I have the SDA and SCL lines pulled up to VCC with 10k resistors.

I would say about 30% of the time everything works great and I get good data from the device that I am connecting to. The rest of the time I am getting hung up on the polling loop waiting for the TX Flag to be set in my I2C_write function. It seems that the UCTXSTT bit is not getting cleared out on the first byte TX. I have to power cycle to get things going again. Please provide some guidance if you can. Thank you for your time.

Attached is my code:

void I2C_init() 

           P3SEL |= 0x06;                                                                     // Assign I2C pins to USCI_B0s P3.2 = SCL; P3.1 = SDA
           UCB0CTL1 |= UCSWRST;                                                 // Enable SW reset
            UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;        // I2C Master, synchronous mode
            UCB0CTL1 = UCSSEL_2 + UCSWRST;                         // Use SMCLK, keep SW reset
           UCB0BR0 = CLK_DIVIDER;                                              // fSCL = SMCLK/3 = ~333kHz
           UCB0BR1 = 0;
            UCB0I2CSA = 0x68;                                                             // Set slave address 0x68
            UCB0CTL1 &= ~UCSWRST;                                              // Clear SW reset, resume
}

void I2C_write(char reg_addr, char parameters)
{
          while(UCB0STAT & UCBUSY);
          UCB0CTL1 |= UCTXSTT + UCTR;                                       //Start Condition sends address of slave
          while (!(IFG2 & UCB0TXIFG));      
          UCB0TXBUF = reg_addr;                                                       //Load register Address
        
          while (!(IFG2 & UCB0TXIFG));      //<---LOCKS UP HERE
          UCB0TXBUF = parameters;                                                   //Load register parameters
          while (!(IFG2 & UCB0TXIFG));      
          UCB0CTL1 |= UCTXSTP;                                                       //Send stop message   
          while (UCB0CTL1 & UCTXSTP);                                           //Wait until the stop message is sent 
}

signed int I2C_read(char reg_addr)
{
           signed int Data = 0;
          while(UCB0STAT & UCBUSY);                                            //Make sure bus in not busy
            UCB0CTL1 |= UCTXSTT + UCTR;                                      //Start Condition sends address of slave

          while (!(IFG2 & UCB0TXIFG));                                             //Make sure data has been shifted out
          UCB0TXBUF = reg_addr;                                                     //Load register address to read
          while (UCB0CTL1 & UCTXSTT);                                         //Wait until the ACK message has been RX     


          while(UCB0STAT & UCBUSY);                                             //Make sure bus is not busy
           UCB0CTL1 &= ~UCTR                                                         //Sets the master as a receiver
            UCB0CTL1 |= UCTXSTT;                                                     //Start Condition sends address of slave
      
          while ((UCB0CTL1 & UCTXSTT));                                    //Make sure start has been cleared
          UCB0CTL1 |= UCTXSTP;                                                   //Send stop message
         while (!(IFG2 & UCB0RXIFG));                                            //Make sure RXIFG is set
         Data = UCB0RXBUF;                                                          //Save data to memory
          while((UCB0CTL1 & UCTXSTP));                                     //Make sure stop has been sent


         return Data;
}

  • Hi,

    From the users manual

    " The UCBxTXIFG bit is set when
    the START condition is generated and the first data to be transmitted can be
    written into UCBxTXBUF. As soon as the slave acknowledges the address the
    UCTXSTT bit is cleared. 

    The data written into UCBxTXBUF is transmitted if arbitration is not lost during
    transmission of the slave address."

    It sounds like arbitration is being lost and the slave address is not being acknowledged. Perhaps checking for the UCTXSTT bit being cleared and if after a certain amount of time if it isn't, restart the process by resending the START condition.

    HTH,

    Barry

     

  • It might be the case that you have received NACK when it got stuck in loop.

  • Thank you for the quick response. I will implement the UCTXSTT check, but with respect to arbitration I only have the one slave device on the i2c bus. Although, I suppose, a loss of arbitration can occur if an interrupt occurs while the i2c bus is busy?

    One thing that I would like to mention and get some feedback on is that I have connected the pullup resistors(10K) to a ~2.5 volt source and the SDA line is consistently  pulled low, but when I connect the pullups to a 3.3V source the SDA line behaves as it is suppose to(most of the time). My assumption is that the lower voltage combined with the 10k pullups is limited too much current and the slave device is missing a clock pulse(s) (an acknowledge from the slave, relating back to Barry's post). I wonder if this could be the cause of random problems even at 3.3V?  The voltage source is a regulated 3.3V and the slave device is also filtered with caps.

    Thanks

  • blb said:
    It sounds like arbitration is being lost and the slave address is not being acknowledged.

    Arbitration loss and slave NACK are two different things.

    Arbitration is lost if during slave address transmission the master detects a low SDA line when it should be high by its own output. Then the master cancels the start process and the other master (which is assumed to cause SDA low) continues. No harm is done because up to this point, both masters were sending the same signals. (thanks to clock stretching, they were even using the same clock speed implicitely - the slower one was 'dictating' the speed)

    If arbitration is lost, this is flagged and you must try again. If you ar ehte only master in the system, an arbitration loss can only happen if there is no pullup ion the data line or the pullup is too weak and the clock frequency is too high.

    The other situation is that no slave answers. If the slave is physically there, this has two possible reasons:
    first, the slave does not sense the signals because of different VCC, weak pullups (note: on many MSPs the internal pullups are unavailable if the port pin is in output mode - this includes the OC output mdoe for the USCI), too-high clock or high parasitic capacitances which keep the signal low for too long.
    The second possibility is a wrong slave address. Keep in mind that the slave address to write into the USCI slave address register is 7 bit. The LSB, that switches between read and write mode, is NOT included into the slave address. So if the slave manual says 0x20/0x21 for write/read, then the 'real' slave address to be used is 0x10.

    In both cases, if a NACK is detected or if bus arbitration is lost, the TXIFG bit is reset and/or a previously to TXBUF written content is discarded. At the same time UCSTT is cleared. So once UCSTT is clear, it should be hecked for NACKIFG or arbitration loss before proceeding.

    If UCSTT does not clear at all, this usually means that the SCL line doesn't come up. Usually a missing/weak pullup. so the master cannot complete the strt sequence since the clock is hold. The master cannot recover from this situation, since it cannot pull a low line high. And it should never happen normally. Usually, it is a sign of a bad board design or similar problems.
    It may also be possible that the master has no clock (e.g. SMCLK is off, the crystal failed, whatever). Of course without a clock, the master cannot continue too.

     

  • JMG thanks for the detailed answer, a couple of questions for you..

    Jens-Michael Gross said:

    Keep in mind that the slave address to write into the USCI slave address register is 7 bit. The LSB, that switches between read and write mode, is NOT included into the slave address. So if the slave manual says 0x20/0x21 for write/read, then the 'real' slave address to be used is 0x10.

    Should the real slave address be 0x20? I don't understand where 0x10 comes from.

    Jens-Michael Gross said:

    In both cases, if a NACK is detected or if bus arbitration is lost, the TXIFG bit is reset and/or a previously to TXBUF written content is discarded. At the same time UCSTT is cleared. So once UCSTT is clear, it should be hecked for NACKIFG or arbitration loss before proceeding.

    So the UCTXSTT flag is cleared when the slave address is ACKD, NAKD or if bus arbitration is lost. Is that correct?  If the NACKIFG flag is not set then to check for arbitration loss you would wait for the UCBxTXIFG flag to be set? Is there a better way to check for arbitration loss?

     

    Thanks,

    Barry

     

  • Hi, I also saw some issues with the MSP430's I2C module. It seems that there are some time critical code sequences. Especially your comment about that most of the time it is working let me think that there is some timing issues here... Is it possible that there are other interrupt sources used in your code and that those from time to time causes some delays in the I2C code?

    I made good experience with the I2C code that is offered by TI. Maybe this one helps you too. Here is the link to the app note:

    http://focus.ti.com/general/docs/litabsmultiplefilelist.tsp?literatureNumber=slaa382

    Regards.

  • blb said:
    Should the real slave address be 0x20? I don't understand where 0x10 comes from.

    Slave manuals usually give you two 8 bit addresses, one for reading and one for writing. Only the upper 7 bits are really the address while th eLSB is the R/W bit.
    The MSP takes only the upper 7 bits, but right-justified (so the datasheet address is divided by 2) and adds the 8th bit later, based on the UCTR bit, when sending the start condition and slave address.
    So if the slave address is 00100000/00100001 (0x20/0x21) the address to write into the slave address register is 0010000 (0x10) only. And there is no need to change the slave address when switching from write to read or back. Same slave, same address for both directions.

    I agree it would have been way more intuitive if the slave address register would just take the 8 bit value and ignore the LSB.

  • Thanks for the input. The i2c functionality seems to be running very well now. I have added my code as a reference below. I believe the problem was that I had a timer that caused an interrupt on a regular basis and this was causing the i2c bus to halt and lose communication. I have added code to disable and then re-enable the interrupts while the i2c bus is needed.

    The slaa382 application report was helpful in verifying this.

    Thanks for the help everyone. I appreciate it very much.

     

    void C_Dgyro_I2C_write(char reg_addr, char parameters)
    {
        __disable_interrupt();
          while(UCB0STAT & UCBUSY);
          UCB0CTL1 |= UCTXSTT + UCTR;          //Start Condition sends address of slave
         
           while (!(IFG2 & UCB0TXIFG));      
          UCB0TXBUF = reg_addr;                //Load address of slave register
       
          while (!(IFG2 & UCB0TXIFG));
          UCB0TXBUF = parameters;            //Load address of slave register
          while (!(IFG2 & UCB0TXIFG));      
                
          UCB0CTL1 |= UCTXSTP;              //Send stop message   
          while (UCB0CTL1 & UCTXSTP);         //Wait until the stop message is sent 
         
         __enable_interrupt();
    }

    signed int C_Dgyro_I2C_read(char reg_addr)
    {
          signed int gyroData = 0;
         
          __disable_interrupt();
         
          while(UCB0STAT & UCBUSY);
          UCB0CTL1 |= UCTXSTT + UCTR;          //Start Condition sends address of slave

          while (!(IFG2 & UCB0TXIFG));       //Wait until the data has been shifted out
          UCB0TXBUF = reg_addr;                //Load address of slave register

          while (UCB0CTL1 & UCTXSTT);        //Wait until the acknowlege message has been received     
          while(UCB0STAT & UCBUSY);
          UCB0CTL1 &= ~UCTR;                //Sets the master as a receiver
          UCB0CTL1 |= UCTXSTT;                 //Start Condition sends address of slave
          
          while(UCB0CTL1 & UCTXSTT);
         
          UCB0CTL1 |= UCTXSTP;              //Send stop message
          
          while (!(IFG2 & UCB0RXIFG));
          gyroData = UCB0RXBUF;                //Save data to memory
            
           while((UCB0CTL1 & UCTXSTP));
          
           __enable_interrupt();
          return gyroData;
    }

  • cody lohse said:
    I had a timer that caused an interrupt on a regular basis and this was causing the i2c bus to halt and lose communication.

    Normally, this isn't a problem. If your tiemr ISR keeps the processor busy for some time, the I2C master will simply delay the start of the next byte transfer until you stuffed TXBUF/read RXBUF. And the I2C slave will hold the clock line low until you do, stretchign the clock.
    However, there might be a problem with certain slaves which require a minimum I2C clock or have a connection timeout (even if the I2C protocol doesn't define a minimum clock or the idea of a timeout). Also, there might be masters which do not support clock stretching (when the MSP runs as slave). In these cases teh communication can indeed break.
    But normally, it shouldn't do any harm if an I2C transfer is interrupted for some time (even mid-byte). This is why it is a synchronous transfer with an explicitely provided clock signal.

    Your timer ISRs shouldn't be lengthy and time-comsuming anyway.

    However, you r code will fail if the slave doesn't respond.

    After settign UCSTT, you should wait for UCSTT to clear (you should even implement an emergency timeout, in case the clock vanishes or someone keeps the CLK line low)
    Once ICSTTis clear, check for UCNACKIFG. If it is set, the slave didn't answer and you should set UCSTP and bail out.
    THEN you can continue waiting for UCTXIFG and stuffing UCTXBUF. Or wait for UCRXIFG.

**Attention** This is a public forum