Because of the Thanksgiving holiday in the U.S., TI E2E™ design support forum responses may be delayed from November 25 through December 2. Thank you for your patience.

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 USCI Repeated Start Condition

Other Parts Discussed in Thread: MSP430G2553

Hello,

I am trying to solve a problem to get values from my Freescale MMA8453Q Accelerometer with my MSP430g2553 via USCI I2C, where i need to send a repeated start condition. 

The slau144j states: "Setting UCTXSTT will generate a repeated START condition. In this case, UCTR may be set or cleared to configure transmitter or receiver, and a different slave address may be written into UCBxI2CSA if desired."

I am using the TI_USCI_I2C_master library, which i wanted to adapt to send repeated start on a certain occasion. The Library is used in the following way:

_EINT();

TI_USCI_I2C_transmitinit(0x1C,I2C_CLKSPEED);   // init transmitting with
while ( TI_USCI_I2C_notready() );         // wait for bus to be free
TI_USCI_I2C_transmit(2,Data1);       	  // start transmitting

While ( TI_USCI_I2C_notready() );         // wait for bus to be free
TI_USCI_I2C_transmit(1,Data2);       	  // start transmitting

while (TI_USCI_I2C_notready()  );
TI_USCI_I2C_receiveinit(0x1C,I2C_CLKSPEED);   // init receiving with USCI
while ( TI_USCI_I2C_notready() );         // wait for bus to be free
TI_USCI_I2C_receive(3,Data3);
while ( TI_USCI_I2C_notready() );         // wait for bus to be free

I changed the library to not send a stop condition if the RSFlag is 3, which only happens the last time Data is transmitted:

#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void) {
	if (IFG2 & UCB0RXIFG) { //Receive
		if (RSFlag == 3) {
			if (byteCtr == 0) {
				UCB0CTL1 |= UCTXSTP; // I2C stop condition
				*TI_receive_field = UCB0RXBUF;
				TI_receive_field++;
			} else {
				*TI_receive_field = UCB0RXBUF;
				TI_receive_field++;
				byteCtr--;
			}
		} else {
			RSFlag++; // Increase Flag to secure 3rd start is a RS
			if (byteCtr == 0) {
				//UCB0CTL1 |= UCTXSTP; // I2C stop condition removed
				*TI_receive_field = UCB0RXBUF;
				TI_receive_field++;
			} else {
				*TI_receive_field = UCB0RXBUF;
				TI_receive_field++;
				byteCtr--;
			}
		}
	} else { // Transmit
		if (RSFlag == 3) {
			if (byteCtr == 0) {
				UCB0CTL1 |= UCTXSTP; // I2C stop condition
				IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
			} else {
				UCB0TXBUF = *TI_transmit_field;
				TI_transmit_field++;
				byteCtr--;
			}
		} else {
			RSFlag++; // Increase RSFlag to make sure 3rd Start is RS
			if (byteCtr == 0) {
				//UCB0CTL1 |= UCTXSTP;                    // I2C stop condition removed
				IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
			} else {
				UCB0TXBUF = *TI_transmit_field;
				TI_transmit_field++;
				byteCtr--;
			}
		}
	}
}

However, when debugging, the device gets stuck in TI_USCI_I2C_notready() even at the first transmit already, which i can't explain.

Thanks for the help,

Florian

EDIT: Code formatted and adapted some text for better readability

  • Florian,

    Please use the [</>] icon in the post editor to post code so it is formatted in a readable format.

    I am not familiar with the TI library you say you are using, but I would suspect that you are missing some interrupt that's necessary for the "software state machine" to advance. (Ok, it's not exactly a state machine, but hopefully you get the point).

    I do know that this functionality does work, as we use it in our own internally-developed I2C drivers for MSP430 with a STM accelerometer.

    I think the problem may include the ISR that handles the start/stop/flags interrupt. What does that code look like? Also, you don't show where/when/how RSFlag is initialized/set/cleared etc.

  • I edited the post for better readability, thanks for the hint.

    It's the standard library i guess, as there is also a Datasheet for it. The function it gets stuck in is this:

    unsigned char TI_USCI_I2C_notready(){
      return (UCB0STAT & UCBBUSY);
    }
    

    UCBBUSY is set when there is a start condition, and a stop condition clears it, right? However, i get stuck in that function before i ever send a start condition. It even won't proceed, if i clear the flag manually by either clearing it or sending a stop condition in the transmitinit function.

    Thanks for the help!

  • There are a lot of threads already, search the forum for the term "Repeated Start".

    One in particular is: http://e2e.ti.com/support/microcontrollers/msp430/f/166/t/169259.aspx

  • I don't wanna sound rude, but at the one hand i've done my research and as this is a very specific problem and the need for a repeated start condition is pretty non-existant, so there is nothing to find at TI's datasheets/forums regarding my specific problem. On the other hand, the thread you linked me is about another problem. I guess i don't have a problem with repeated start itself, but on the way there.

    But i'll see if i can get their example running. EDIT: This is somewhat incomplete, the useful parts (for me) are just left out :(

    Any other suggestions? 

  • Florian Windolf said:
    I don't wanna sound rude, but at the one hand i've done my research

    Certainly, but on the other hand, the help here is all volunteer, and my boss expects me to work. I was trying to point to a variety of other posts with similar titles in the absence of downloading and reverse-engineering a library I've never used related to hardware that I don't have.

    What I would recommend perhaps, is modifying the API functions to take a parameter that indicates whether or not a stop should occur. That's how my driver works. I am not sure with such a quick glance why the USCI would be busy before you do anything.

  • And i have to tell you, that my first 3 google pages are red, i've tried multiple ways like polling, interrupts, etc. but i don't have the deep understanding of how that would work and put a lot of effort into the file that i have right now. i don't really see the point in working myself into an incomplete code with spanish/portugese commentary just to find out i cannot reverse engineer it.

    If you complain to me that you invest your precious time, don't invest. I know it was meant nice, but no help doesnt get me anywhere, especially if you have nothing to do with it like you stated, regardless what your boss wants you to do.

    But thanks for your help, i really appreciate it. 

    Modifying the API functions wouldn't help, because the problem is in the TX ISR. Only the initialization and parameterization is done with functions, the TX and RX part takes place in the ISR. But like i stated in my first post, the change i made destroys all functionality. And THIS is the point where i need help. It shouldn't do so, and i don't get why. Even if i insert a if(false){...}, into that specific part at the ISR, the BUS will remain busy.

  • I'm not too familiar with the lib too, but did you try checking for bus free before doing the transmit/receive init?

    Are the port pins configured properly?

    Also, do you have the required pull-up resistors on the lines? Else SCL is low all time and the USCI will consider the bus busy.

    About your change: if you don't set UCTXSTP in your RX function, the USCI will continue receiving bytes until you finally set UCTXSTT, leading to unpredictable results (buffer overflow etc.).
    In the TX function, the ISR properly clears the IFG bit, so no more interrupts follow.

    However, besides saving the time for the stop condition, the sole purpose of a repeated start is to not have a different master on the bus trying to take over the bus between your stop and the next start (with possible arbitration loss). If you are the only master, it should make no difference whether you use a repeated start or not. At least for all slaves I ever met.

    Also, a repeated start is usually only used when writing a start address then reading data from that address (to ensure that nobody else is accessing the slave in the meantime, making the start address write void).

    Last thing: Your altered code increases RSFLAG after each single transfer for the first three transfers, then nevermore. So after the 3rd byte sent or received, nor more stop will generated ever. Which is surely not your intention. You'd better set a global flag (mark it volatile!) which directly decides whether to send a stop or not (no need to duplicate the whole code block in the ISR, just frame the setting if STP.) and set it right before calling transmit or receive.

  • Hello,

    thanks for your answer.

    I check the bus to be free before every transmit/recieve with the function provided by the lib (checking UCBUSY Flag). Also the ports are configured properly, i don't use internal pull-ups, but 4.7k external at the accelerometer.

    Am i right that not sending stop doesn't affect the TX, unless i loop TX, RX, TX etc.?

    The MMA8453Q seems to need it, when i try to read WHO_AM_I register, with a factory value in it, it still returns 0x00. So i guess in this particular case i really need a repeated start condition. The Routine for adressing the sensor is Initialize with writing 0x01 to the 0x2A register, then stating which register i want to read (address 0x01 register on the sensor) and finally reading 6 registers, which the sensor internally provides on SDA. 

    I've altered the code a bit, and i am able to send a repeated start condition, BUT i cannot send anything between start and repeated start. the code for the lib now is (the interesting part is the TX ISR, the rest is just in case you need to know something else):

    signed char byteCtr;
    unsigned char RSFlag = 0;
    unsigned char tosend = 0;
    unsigned char *TI_receive_field;
    unsigned char *TI_transmit_field;
    
    //------------------------------------------------------------------------------
    // void TI_USCI_I2C_receiveinit(unsigned char slave_address, 
    //                              unsigned char prescale)
    //
    // This function initializes the USCI module for master-receive operation. 
    //
    // IN:   unsigned char slave_address   =>  Slave Address
    //       unsigned char prescale        =>  SCL clock adjustment 
    //-----------------------------------------------------------------------------
    void TI_USCI_I2C_receiveinit(unsigned char slave_address,
    		unsigned char prescale) {
    	P1SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0
    	P1SEL2 |= SDA_PIN + SCL_PIN;
    	UCB0CTL1 = UCSWRST; // Enable SW reset
    	UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
    	UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
    	UCB0BR0 = prescale; // set prescaler
    	UCB0BR1 = 0;
    	UCB0I2CSA = slave_address; // set slave address
    	UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
    	UCB0I2CIE = UCNACKIE;
    	IE2 = UCB0RXIE; // Enable RX interrupt
    }
    
    
    //------------------------------------------------------------------------------
    // void TI_USCI_I2C_transmitinit(unsigned char slave_address, 
    //                               unsigned char prescale)
    //
    // This function initializes the USCI module for master-transmit operation. 
    //
    // IN:   unsigned char slave_address   =>  Slave Address
    //       unsigned char prescale        =>  SCL clock adjustment 
    //------------------------------------------------------------------------------
    void TI_USCI_I2C_transmitinit(unsigned char slave_address,
    		unsigned char prescale) {
    	P1SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0
    	P1SEL2 |= SDA_PIN + SCL_PIN; //added
    	RSFlag = 0;
    	UCB0CTL1 = UCSWRST; // Enable SW reset
    	UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
    	UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
    	UCB0BR0 = prescale; // set prescaler
    	UCB0BR1 = 0;
    	UCB0I2CSA = slave_address; // Set slave address
    	UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
    	UCB0I2CIE = UCNACKIE;	// Enable NACK ISR
    	IE2 = UCB0TXIE; // Enable TX ready interrupt
    }
    void TI_USCI_I2C_transmitinit_RS(unsigned char slave_address,
    		unsigned char prescale) {
    	UCB0CTL1 = UCSWRST; // Enable SW reset
    	UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
    	UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
    	UCB0BR0 = prescale; // set prescaler
    	UCB0BR1 = 0;
    	UCB0I2CSA = slave_address; // Set slave address
    	UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
    	UCB0I2CIE = UCNACKIE;	// Enable NACK ISR
    	IE2 = UCB0TXIE; // Enable TX ready interrupt
    }
    
    //------------------------------------------------------------------------------
    // void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field)
    //
    // This function is used to start an I2C commuincation in master-receiver mode. 
    //
    // IN:   unsigned char byteCount  =>  number of bytes that should be read
    //       unsigned char *field     =>  array variable used to store received data
    //------------------------------------------------------------------------------
    void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field) {
    	TI_receive_field = field;
    	if (byteCount == 1) {
    		byteCtr = 0;
    		__disable_interrupt();
    
    		UCB0CTL1 |= UCTXSTT; // I2C start condition
    		while (UCB0CTL1 & UCTXSTT); // Start condition sent?
    		UCB0CTL1 |= UCTXSTP; // I2C stop condition
    		__enable_interrupt();
    	} else if (byteCount > 1) {
    		byteCtr = byteCount - 2;
    		UCB0CTL1 |= UCTXSTT; // I2C start condition
    	} else
    		while (1)
    			; // illegal parameter
    }
    
    //------------------------------------------------------------------------------
    // void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field)
    //
    // This function is used to start an I2C commuincation in master-transmit mode. 
    //
    // IN:   unsigned char byteCount  =>  number of bytes that should be transmitted
    //       unsigned char *field     =>  array variable. Its content will be sent.
    //------------------------------------------------------------------------------
    void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field) {
    	TI_transmit_field = field;
    	byteCtr = byteCount;
    	UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
    }
    
    //------------------------------------------------------------------------------
    // unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address)
    //
    // This function is used to look for a slave address on the I2C bus.  
    //
    // IN:   unsigned char slave_address  =>  Slave Address
    // OUT:  unsigned char                =>  0: address was not found, 
    //                                        1: address found
    //------------------------------------------------------------------------------
    unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address) {
    	unsigned char ie2_bak, slaveadr_bak, ucb0i2cie, returnValue;
    	ucb0i2cie = UCB0I2CIE; // restore old UCB0I2CIE
    	ie2_bak = IE2; // store IE2 register
    	slaveadr_bak = UCB0I2CSA; // store old slave address
    	UCB0I2CIE &= ~UCNACKIE; // no NACK interrupt
    	UCB0I2CSA = slave_address; // set slave address
    	IE2 &= ~(UCB0TXIE + UCB0RXIE); // no RX or TX interrupts
    	__disable_interrupt();
    	UCB0CTL1 |= UCTR + UCTXSTT + UCTXSTP; // I2C TX, start condition
    	while (UCB0CTL1 & UCTXSTP)
    		; // wait for STOP condition
    
    	returnValue = !(UCB0STAT & UCNACKIFG);
    	__enable_interrupt();
    	IE2 = ie2_bak; // restore IE2
    	UCB0I2CSA = slaveadr_bak; // restore old slave address
    	UCB0I2CIE = ucb0i2cie; // restore old UCB0CTL1
    	return returnValue; // return whether or not
    						// a NACK occured
    }
    
    //------------------------------------------------------------------------------
    // unsigned char TI_USCI_I2C_notready()
    //
    // This function is used to check if there is commuincation in progress. 
    //
    // OUT:  unsigned char  =>  0: I2C bus is idle, 
    //                          1: communication is in progress
    //------------------------------------------------------------------------------
    unsigned char TI_USCI_I2C_notready() {
    	return (UCB0STAT & UCBBUSY);
    }
    
    void SR_next(){
    	RSFlag = 1;
    }
    
    #pragma vector = USCIAB0RX_VECTOR
    __interrupt void USCIAB0RX_ISR(void) {
    	if (UCB0STAT & UCNACKIFG) { // send STOP if slave sends NACK
    		UCB0CTL1 |= UCTXSTP;
    	}
    }
    
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void) {
    	if (IFG2 & UCB0RXIFG) { //Receive
    		if (byteCtr == 0) {
    			UCB0CTL1 |= UCTXSTP; // I2C stop condition
    			*TI_receive_field = UCB0RXBUF;
    			TI_receive_field++;
    		} else {
    			*TI_receive_field = UCB0RXBUF;
    			TI_receive_field++;
    			byteCtr--;
    		}
    	}
    	else {
    
    		if (byteCtr == 0) {
    
    			if (!RSFlag ) {
    				UCB0CTL1 |= UCTXSTP; // Send I2C stop condition
    				RSFlag = 1;	// Increase RSFlag to not send STP next time
    			} else {
    				UCB0CTL1 &= ~UCTR; // Master Recieves
    			}
    			IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX flag
    		} else { // Transmit
    			UCB0TXBUF = *TI_transmit_field;
    			tosend = *TI_transmit_field;
    			TI_transmit_field++;
    			byteCtr--;
    		}
    	}
    }

    i use it this way right now:

      _EINT();
    
    	TI_USCI_I2C_transmitinit(0x1C,I2C_CLKSPEED);   // init transmitting with
        while ( TI_USCI_I2C_notready() );         // wait for bus to be free
        TI_USCI_I2C_transmit(2,Data1);       	  // start transmitting
        while ( TI_USCI_I2C_notready() );         // wait for bus to be free
    
        TI_USCI_I2C_transmitinit_RS(0x1C,I2C_CLKSPEED);
        TI_USCI_I2C_transmit(1,Data2);       	  // start transmitting
    //    while (TI_USCI_I2C_notready()  );           // won't start because no STP to clear busy flag
    
        TI_USCI_I2C_receiveinit(0x1C,I2C_CLKSPEED);   // init receiving with USCI
    
        TI_USCI_I2C_receive(4,Data3);
    	while ( TI_USCI_I2C_notready() );         // wait for bus to be free

    When i set a delay between the STT and the repeated STT, i will eventually send some bytes, but in fact not the bytes i want to send.

    Thanks for your help! If you need more information, i am glad to provide it.

  • Not sending STOP doesn't affect any transmission. You may omit it totally. It does, however, keep the bus considered busy.

    The bus is busy from the moment a start Is sent to the moment a stop is sent. Receiving a rising edge on data while the clock is high is a start and marks the bus busy. The USCI will consider the bus busy until it ever sees a falling edge on data while the clock is high. No master may start a transfer while the bus is busy (unless it was himself who marked the bus busy).

    That's why a repeated start prevents a different master to take over the bus. However, the USCI won't prohibit starting a transfer while it considers the bus busy. This is something your code has to obey.

    Why do you have two transmitinit functions that are almost identical? Well, one will clear RSFlag and initialize the ports while the other doesn't touch RSflag and doesn't init the ports. But that's jsut a marginal difference and does not justify two functions. You could pass RSFLAG as parameter - or just clear/set it in before calling the function.

    Or, better, set/clear it right before calling the transmit.

    BTW: you should declare RSFlag as volatile.

    I see that the ISR is setting RSFlag. Why? It should check it, but not set it. The way it Is now, it may work, but only for this exact situation (one transmit with stop, then all future without)

    Your second transmission has a big lem. Transmit returns immediately. So your code continues. But because you have disabled the stop, you don't check for bus busy (as it stays busy after the transfer). Unfortunately this means that you are instantly reconfiguring the I2C for receive. 

    You need to wait until the transmit is done. You can do this by entering LPM and let the TX ISR exit from LPM after receiving a TX interrupt for which no more bytes exist, meaning that the last byte has been moved from TXBUF to the output shift register.
    If at this point a new start is triggered, the generation for the start condition will wait until the byte currently in the output shift register has been sent.

    However, you must not reconfigure the USCI (not set SWRST!) between this point and the start of the new transfer, or else you will reset the USCI mid-operation and the last byte is broken.
    But reinit does reset the USCI.

    That's why I don't like using library functions - they too often simply are not flexible enough to support the exact situation you are in.

    What is Data1/Data2/Data3? I hope they are unsigned char * (Arrays or references to a char, I hope?)

**Attention** This is a public forum