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.

ez430-RF2500 I2C Start Condition issues

Other Parts Discussed in Thread: MSP430G2553, CC2500, MSP430F2274

Hey everyone,

I'm trying to use the MSP430 on this development board to communicate with another piece of hardware using the I2C bus. I've looked through a variety of old posts and example code and I've come up with the software below; however mine does not work. There seems to be a problem transmitting the START condition to my external device, and the code remains stuck in the while loop waiting for the UCTXSTT bit to be cleared (after confirming the address has been with the external device.

Can anyone offer a suggestion as to why I'm not moving pass the while loop? 

Thanks.

 

#include "msp430x22x4.h"


char count, status, hr_data;

//int blah, test;

  

int main( void )

{

  // Stop watchdog timer to prevent time out reset

  WDTCTL = WDTPW + WDTHOLD;


  //P1.0 (RED) and P1.1 (GREEN) are LEDs

  P1DIR = 0x03;                 // P1.0 and P1.1 as outputs

  P1OUT = 0x00;                 // All P1 output pins to 0

  

  //I2C Initialization

  UCB0CTL1 |= UCSWRST; // Enable SW Reset

  P3DIR |= 0x0F;

  P3SEL |= 0x06; // Assign I2C pins to USCI_B0

  UCB0CTL0 = UCMST+UCMODE_3+UCSYNC;         // I2C Master, synchronous mode

  UCB0CTL1 = UCSSEL_2+UCSWRST;              // Use SMCLK, keep SW reset

  UCB0BR0 = 24;                             // fSCL = SMCLK/24 = ~50kHz

  UCB0BR1 = 0;

  UCB0I2CSA = 0x7F; // Set Slave Address - 127 (is it 128?)

  UCB0I2COA = 0x01; // Set master address (1)

  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation


  while(1){

  

   UCB0CTL1 |= UCTR + UCTXSTT;   // I2C start condition

   while (UCB0CTL1 & UCTXSTT);             // Loop until I2C STT is sent

   while(UCB0TXIFG == 0); // Wait until data can be written to UCB0TXBUF

   UCB0TXBUF = 0x47; // Ask for the heart rate

   while(UCB0TXIFG == 0); // Wait until data can be written to UCB0TXBUF

   UCB0TXBUF = 0x01; // Ask for one heart rate data

   while(UCB0TXIFG == 0); // Wait until the stop bit can be set

   UCB0CTL1 |= UCTXSTP; // Generate a STOP Condition

  • Hi Nicholas

     

    Could you find a solution to your problem.Because I am facing a similar behaviour with my setup.

    Please let me know if you could solve it and how ?

     

    Regards

    Tony

     

     

     

  • Nicholas Burgwin said:
      UCB0I2COA = 0x01; // Set master address (1)

    You're running in single master mode. So there is no own address. Or do you expect calling yourself?
    Nicholas Burgwin said:
      UCB0I2CSA = 0x7F; // Set Slave Address - 127 (is it 128?)

    Normally, the slave address is 7 bit. And some addresses are reserved (e.g. general call etc.)
    You should KNOW which is the address of your slave. :)
    Nicholas Burgwin said:
    while (UCB0CTL1 & UCTXSTT);            
    If your code is indeed hanging at this point, this might have several reasons:
    1) your bitclock is not clocking. Are you sure that your SMCLK is running at all (and with 1.2MHz)? If not, the start condition will take forever.
    2) your ourput line is held low. This will effectively prevent the hardware from doing anything, as this 'clock stretching' indicates that either a different master is occupying the bus or the selected slave is busy with startup and holds the line low before acknowledging. In any case, the UCTXSTT bit will not clear as the start process is not finished.

    The second condition is often caused because nobody pulls the data and clock lines high. I2C is an open collector bus. All members, master and slave, are only pulling the lines low if required. Nobody is driving its output pins high, or things like multimaster or clock stretching or device enumeration won't work. There must be pullup resistors on both lines or it won't work.
    The internal pullups of the MSP are inactive, even if configured, since they are only active when the pins are input pins. Yet at least the clock line is always in output mode (with deactivated high-driver). The direction is controlled by the USCI module and independent of the PxOUT register.
    Without pullup resistors on SCL and SDA, the I2C bus is stalled and no communication is possible.

    Edit: in transmit mode, the USCI won't complete the start byte (and therefore clear UCTXSTT) until you have written something to send into TXBUF. So write your first byte to TXBUF  before you wait for TXSTT to clear. No need to wait for UCTXIFG for this, as it is immediately and implicitly set as soon as you set UCTR and UCTXSTT.

  • I am facing a similar problem. I am using the eZ430 RF2500 as the master and an IMU 6DOF gyro board  as the slave. I have run an application in attempt to read a register from the IMU and display the value on my laptop with the UART functions used in the thermistor demp application. I have monitored the data and clock lines, here are the events I saw take place:

                                             Data       Clock

    1. Beginning of app        HI             LO

    2. Initialize I2C                 HI              HI

    3. Set Start condition      LO             HI

    4. unknown cause          HI              HI

    5. Application restarts    HI              LO  , which brings me back to 1

    I am using someone else's code for the I2C configuration and communication developed for the same IMU and an MSP430G2553. I use the UART initialization and transmission from the the thermistor demo provided by TI. Here is the code for the master (the slave doesn't require programming):

    void STOPCHECK(void){        // Regulate app via on-board button
    BSP_TURN_ON_LED1();
    while(!(BSP_BUTTON1()));
    while(BSP_BUTTON1());
    BSP_TURN_OFF_LED1();}

    void main(void)
    {
    //from ITG3200_MSP430G2553
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    BCSCTL1 = (CALBC1_1MHZ);        // Set DCO to 1 MHz
    DCOCTL = CALDCO_1MHZ;



    // from demo application
    BSP_Init(); // initialize board specific hardware
    COM_Init(); // initialize UART interface w/ computer

    BSP_TURN_ON_LED2();
    STOPCHECK();
    BSP_TURN_OFF_LED2();

    //from TI Wiki for EZ430-RF2500
    P3DIR |= 0x0F; //Disable P3.2 from periferal function w/ CC2500 wireless chip
    P3SEL |= 0x06; // Assign I2C pins to USCI_B0

    //from ITG3200_MSP430G2553, initialize i2c on MSP430F2274
    UCB0CTL1 = 0;                                                            //Clear register
    UCB0CTL0 = 0;                                                            //Clear register
    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 = 1Mhz/10 = ~100kHz
    UCB0BR1 = 0;
    UCB0I2CSA = 0x68;                                                     // Slave Address is 068h
    UCB0CTL1 &= ~UCSWRST;                                     // **Initialize USCI state machine**
    IE2 |= UCB0RXIE + UCB0TXIE;                                // Enable RX and TX interrupt

    while ( i2c_notready() );      // wait for bus to be free
    STOPCHECK();

    int8_t receivedByte;
           TXString(" checkpoint 1 \r\n",16);
           STOPCHECK();
    while (UCB0CTL1 & UCTXSTP);              // Ensure stop condition got sent
           TXString(" stop sent \r\n",16);
           STOPCHECK();
    UCB0CTL1 |= UCTR;                                  // Clear transmit flag
           TXString(" UCTR set \r\n",16);
           STOPCHECK();
    UCB0CTL1 |= UCTXSTT;                           // I2C start condition
           TXString(" start sent \r\n",16);
           STOPCHECK();
    while((IFG2 & UCB0TXIFG) == 0);            //UCB0TXIFG is set immidiately
    UCB0TXBUF = 0x00;                                   //write registerAddr in TX buffer
           TXString(" checkpoint 2 \r\n",16);
           STOPCHECK();
    while((IFG2 & UCB0TXIFG) == 0);             // wait until TX buffer is empty and transmitted
    UCB0CTL1 &= ~UCTR ;                             // Clear I2C TX flag for receive
    UCB0CTL1 |= UCTXSTT + UCTXNACK; // I2C start condition with NACK for single byte reading
           TXString(" checkpoint 3 \r\n",16);
    STOPCHECK();
    while (UCB0CTL1 & UCTXSTT);              // Start condition sent? RXBuffer full?
    receivedByte = UCB0RXBUF;
    UCB0CTL1 |= UCTXSTP;                          // I2C stop condition

            char gimmeAddress[] = {"The address is x \r\n"};
            gimmeAddress[15] = receivedByte;
            TXString(gimmeAddress, sizeof gimmeAddress);

    }

    I noticed that this behavior occurs without the IMU board attached to the I2C bus. I ran this application with all of the while() statements after the start condition commented out, without the IMU or the oscilloscope, and the application ran until the end. I am at a loss of the cause of the hold-up and restart, especially how the application restarts with the watchdog timer disabled. I plan on running the prior mentioned test with an oscilloscope to see if anything is happening on the bus. Does anyone have a suggested approach to further troubleshooting, or perhaps a solution based on this information?

  • Zack Koontz said:
    1. Beginning of app        HI             LO

    This is strange. At power-on or app start, the port pins of the MSP should be high-Z inputs, and the lines should be high by the external pull-ups. Unless, of course, the slave does for some reason pull the clock low. This could (but shouldn't) happen when the slave is unpowered or powering up, But why should it when the app restarts?

    Zack Koontz said:
    4. unknown cause          HI              HI

    SDA going high while SCL is high is a stop condition. Either intentional, or because the MSP reset and the LO output goes high-Z.

    Zack Koontz said:
    P3DIR |= 0x0F; //Disable P3.2 from periferal function w/ CC2500 wireless chip
    P3SEL |= 0x06; // Assign I2C pins to USCI_B0


    Setting P3DIR.1/2 to output (likely with P3OUT.1/2=0) will cause a low pulse on the port pins which may confuse the slave.
    It is not necessary to set th edirection of the USCI pins at all (the USCI does that depending on operation mode). So just leave them untouched (high-Z)

    Zack Koontz said:
    UCB0CTL1 = 0;                                                            //Clear register
    UCB0CTL1 |= UCSWRST;                                         // Enable SW reset

    Bad idea to clear UCB0CTL1, as it releases the USCI from reset and starts operation, while you didn't configure it. This may cause unwanted behaviour until you go into reset again with the next instruction.

    Simply do an UCB0CTL1 = UCSWRST; if you want to initialize the registers instead of relying on the default power-on content.

    Zack Koontz said:
    UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode

    Don't use a numerical operator when joining bits. Under certain circumstances this can lead to totally unpredictable results and is difficult to track down. Bits should be joined with the 'bitwise OR' operator '|'.
    (The C compiler doesn't differentiate between bits and values, so it accepts both operators, but performs different actions then with only coincidentally identical results - if identical at all)

    Zack Koontz said:
    without the IMU or the oscilloscope, and the application ran until the end.

    IMU and scope add capacitance as well as pull-down load to the line. This affects behaviour when signal outputs are high-z.

    Observation affects the observed.

    However, do you have the mandatory external pull-ups on the SDA and SCL lines? What about a common GND between MSP and slave device?

  • Thanks for the response and critique. I do have the 10k pull-up resistors on the data and serial lines. The ground for the IMU is the connected to pin-out 1 on the eZ430. Pin-out 2 is used to supply power to the IMU and to pull the data and clock lines up through the resistors. 

  • I have re-run the test without an oscilloscope. I've counted the number of checkpoints before the application restarts itself, and it is a the same point as using the oscilloscope. My biggest concern right now is that I don't understand why the MSP resets after sending the start condition. Could someone explain why this happens?

    Also, the SDA and SCL lines from the slave are always high (as measured disconnected from the buses). I've never used I2C before, but it seems like it would be atypical of a inactive slave. I'm suspicious that the board may be compromised. Could this be the source of the problem?

    Here's the link to the slave board: https://www.sparkfun.com/products/10121

  • The USCI cannot, under no circumstances, cause a device reset internally.
    Possible reasons are:
    - supply voltage breakdown due to the I2C output draining the supply through the pullup resistors
    - watchdog reset while your code waits in the transfer function
    - stack overflow due to nested interrupts or recursion or whatever.

    I don't know your code, so I can't do any further analysis.

    Zack Koontz said:
    Also, the SDA and SCL lines from the slave are always high (as measured disconnected from the buses).

    This is indeed strange. If there is no external pullup on these lines, the lines should be high-impedance/open - or low (if active low for some reason).

    However, it might be that the slave has internal pullups or has TTL-style port pins (which imply weak pullups) rather than todays usual CMOS ones.

    Well a look at the board schematics (available form the page you linked to) shows, that the board contains two 4k7 pullups from SDA/SCL to 3.3V
    This is surprising, as it is usually left to the master (or the super system) to provide the pullups. and not to a slave component module.
    However, under these circumstances, your observation is to be expected. And you don't need additional pullups on the MSP side.
     (or you remove R1 and R2 from the slave board)

  • Jens-Michael Gross said:
    I don't know your code, so I can't do any further analysis.

    I posted the code earlier in this thread

  • I have since tried to run another demo from the slac123 folder. It uses and eZ430 slave to transmit single bytes to an eZ430 master. The master's code is getting hung up at a similar point that the prior-mentioned code did, which I have highlighted below:

    int main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      P1OUT |= 0x03;                            // P1.0 = 11, both LEDs on
      P1DIR |= 0x03;                            // P1.0 output
      P3SEL |= 0x06;                            // 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 = 0x048;                        // Slave Address is 048h
      UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
      IE2 |= UCB0RXIE;                          // Enable RX interrupt
      RXCompare = 0;                            // Used to check incoming data
    
      while (1)
      { while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
        UCB0CTL1 |= UCTXSTT;                    // I2C start condition
        while (UCB0CTL1 & UCTXSTT);             // Start condition sent?
        UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
        __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
    
        if (RXData != RXCompare)                // Trap CPU if wrong
        {
          P1OUT &= ~0x01;                       // turn off red LED
          while (1);                            // Trap CPU
        }
    
        RXCompare++;                            // Increment correct RX value
      }
    }
    
    // USCI_B0 Data ISR
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void)
    {
      RXData = UCB0RXBUF;                       // Get RX data
      __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0
    }
  • You mean the code is eternally waiting for UCTXSTT to clear? Two possible reasons come in mind. First, it may be that the clock line is held low. By missing pull-up or by the slave. In this case the USCI won't continue (actually, it can't, as the clock cline is locked). You can check this with UCCLLOW bit.

    The other reason is that it doesn't lock there, it only appears so, because it runs through the other code and only spends significant time in this waiting loop. So chances are that when you break, you'll break there.

    Did you try looking at the signal lines with a scope or a logic analyzer? (For microcontroller work, these are the #1 tools you must have for serious development)

    I posted the code earlier in this thread

    Ah, right. Well, you disable the WDT, so this is no reason.

    STOPCHECK will probably not work as intended die to button bouncing. Whether you press or release the button, the port pin might switch between high and low for several ms until it settles. Unless you have an external R/C low-pass filter or do some internal de-bouncing. A simple check like yours only works on digitally generated signals or if you use a special bounce-free pushbutton.

    However, in the code you posted, the ISR is missing. I though you didn't post it but you don't use an ISR at all, as you directly check for TXIFG etc and write to TXBUF etc.
    But you have enabled UCB0RXIE and UBC0TXIE, which makes the CPU jump to the ISR when an RX or X event happens. And since there is no ISR, the CPU jumps into the void (the interrupt vector points to 0xFFFE if there is no ISR), causing a reset.

  • I removed the line that enables UCB0RXIE and UCB0TXIE, and now the code runs to the end. Thank you for pointing that out :).

    The code now runs through, only to return 0x00 instead of the anticipated 0x68, which the the IMU's I2C address. I have removed STOPCHECK and it performs the same. 

    Jens-Michael Gross said:
    Did you try looking at the signal lines with a scope or a logic analyzer? (For microcontroller work, these are the #1 tools you must have for serious development)

    I used a Tektronix digital oscilloscope to monitor the clock and data lines

    I just realized how to use the debugger. Until now I have been downloading the code and monitoring the board using TXString and STOPCHECK. Noob mistake. I'll come back once I've explored this capability

  • Problem solved. I stopped working with the code I had posted on this thread and started working with the code I received from Barışcan Kayaoğlu. Once I found out how to read the registers while debugging, I found that the TXString function I was using to report events via UART set an interrupt flag that conflicted with one of condition statements used to regulate the progression of the I2C. I removed all instances of TXString, and was able to see gyro readings in the memory while debugging. 

    Jens-Michael Gross said:
    Did you try looking at the signal lines with a scope or a logic analyzer? (For microcontroller work, these are the #1 tools you must have for serious development)

    The lab I am working in has an oscilloscope, but no logic analyzer. Is there a go-to brand or model? For instance, I've only seen and used Tektronix digital oscilloscopes on campus.

  • Zack Koontz said:
    but no logic analyzer. Is there a go-to brand or model?

    I use the "Logic" from Saleae (www.saleae.com). You can test the software from free with randomly generated data. The price is $120 or so for 8 channels. The hardware isn't an enigma, but does the job and is quite robust (small metal case) and complete (cables and 9 clips). Additional clipps and cables are available for cheap. And the software is excellent. I've seen much worse software for expensive 'quality' hardware.

**Attention** This is a public forum