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.

MSP430G2553: When to generate stop condition in i2c master mode?

Part Number: MSP430G2553


Hello all, 

I am trying to figure out why my code doesn't work when I try to receive 2 or more byte from MPU-9250 slave device. receiving a single byte works properly and the Tx functionality also works. 

If I comment out the:

if(i2c.length == 1)
{
	UCB0CTL1 |= UCTXSTP;
}

in the Master Receive section of the ISR, and set UCTXSTP after reading the last byte, it works. According to the user guide, UCTXSTP is supposed to be set while the byte is being received.

The example codes also do this in a similar way to what I am doing. Any Ideas?
/*
 * msp430_i2c.c
 *
 *  Created on: Nov 1, 2019
 *      Author: Wayne
 *
 *      Description: i2c driver for msp430g2553 using interrupts.
 */
#include <msp430.h>

#define	SCL_PIN		BIT6
#define	SDA_PIN		BIT7

typedef enum {
    STATE_WAITING,
    STATE_READING,
    STATE_WRITING
} msp430_i2c_state;

typedef struct {
    volatile msp430_i2c_state state;
    unsigned char slave_reg;			// First slave register.
    unsigned char slave_reg_written;	// 0 if slave register has not been written yet. */
    unsigned char *data;
    unsigned short length;
    unsigned char enabled;
} msp430_i2c_info;

static msp430_i2c_info i2c = {
    .enabled = 0
};

void i2c_enable()
{
	/*Initialize USCI I2C Module*/
	BCSCTL3 |= LFXT1S_2;						// VLOCLK (~12 KHz) feeding ACLK (Select ACLK source to be internal VLO)
	UCB0CTL1 |= UCSWRST | UCSSEL_1;				// Hold USCI_B in reset for initialization (Set UCSWRST) & Select ACLK
	UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;       // I2C Master Mode | I2C mode
	UCB0BR0 = 8;								// Set i2c frequency
	UCB0BR1 = 0;
	P1SEL  |= SDA_PIN | SCL_PIN;                // Configure ports
	P1SEL2 |= SDA_PIN | SCL_PIN;
	UCB0CTL1 &= ~UCSWRST;                       // Release USCI_B for operation (Clear UCSWRST)
	IE2 |= UCB0TXIE | UCB0RXIE;					// Enable interrupts (optional) via UCxRXIE and/or UCxTXIE
}

int i2c_write(unsigned char slv_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data)
{
    /* Populate struct. */
    i2c.state = STATE_WRITING;
    i2c.slave_reg = reg_addr;
    i2c.slave_reg_written = 0;
    i2c.data = (unsigned char*)data;
    i2c.length = length;

    UCB0I2CSA = slv_addr;           // write slave address to UCB0I2CSA register
    UCB0CTL0 &= ~UCSLA10;           // 7-Bit slave addressing mode
    UCB0CTL1 |= UCTR + UCTXSTT;     // set UCTR for tx mode + set UCTXSTT to generate start condition

    while(i2c.state != STATE_WAITING)
    	{	/* Enter LPM0 w/ interrupts */
    		__bis_SR_register(CPUOFF + GIE);
    		i2c.state = STATE_WAITING;
    	}

    return 0;  // this return is pointless. need to return some err or something
}


int i2c_read(unsigned char slv_addr,unsigned char reg_addr,unsigned char length,unsigned char *data)
{
	/* Populate struct. */
	i2c.state = STATE_READING;
	i2c.slave_reg = reg_addr;
	i2c.slave_reg_written = 0;
	i2c.data = data;
	i2c.length = length;

	UCB0I2CSA = slv_addr;           	// write slave address to UCB0I2CSA register
	UCB0CTL0 &= ~UCSLA10;           	// 7-Bit slave addressing mode
	while (UCB0CTL1 & UCTXSTP);         // Ensure stop condition got sent
	UCB0CTL1 |= UCTR | UCTXSTT;         // I2C start condition

	while(i2c.state != STATE_WAITING)
	{	/* Enter LPM0 w/ interrupts */
		__bis_SR_register(CPUOFF + GIE);
		i2c.state = STATE_WAITING;
	}

	return 0;
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCIAB0TX_VECTOR))) USCIAB0TX_ISR (void)
#else
#error Compiler not supported!
#endif
{
	if(IFG2 & UCB0RXIFG)							// Master Receive?
	{
		i2c.length--;
		if(i2c.length)
		{
			*i2c.data++ = UCB0RXBUF;
			if(i2c.length == 1)
			{
				UCB0CTL1 |= UCTXSTP;
			}
		}
		else
		{
			*i2c.data = UCB0RXBUF;
//			UCB0CTL1 |= UCTXSTP;
			IFG2 &= ~UCB0TXIFG;         			// Clear TX interrupt flag
			__bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
		}
	}

	else if(IFG2 & UCB0TXIFG)						// Master Transmit?
	{
		switch (i2c.state)
		{
			case STATE_WRITING:
				if (!i2c.slave_reg_written)
				{
					i2c.slave_reg_written = 1;			//
					UCB0TXBUF = i2c.slave_reg;			// Send slave address
				}
				else if (i2c.length)					// Data to transmit?
				{
					char next = *i2c.data;				//
					i2c.data++;							// increment byte
					i2c.length--;						// decrement the amount of data left to be sent
					/* Writing to TXBUF must always be the final operation. */
					UCB0TXBUF = next;
				}
				else									// No More data to send?
				{
					UCB0CTL1 |= UCTXSTP;				// Generate Stop condition.
					IFG2 &= ~UCB0TXIFG;					// Clear Tx interrupt flag
					__bic_SR_register_on_exit(CPUOFF + GIE);
				}
				break;
			case STATE_READING:
				if (!i2c.slave_reg_written)
				{
					i2c.slave_reg_written = 1;
					UCB0TXBUF = i2c.slave_reg;
				}
				else
				{
					/* Repeated start, switch to RX mode. */
					UCB0CTL1 &= ~UCTR;
					UCB0CTL1 |= UCTXSTT;

					/* If single byte, prepare stop signal immediately. */
					if (i2c.length == 1)
					{
						/* Well, not IMMEDIATELY. First we need to make sure
						 * the start signal got sent.
						 */
						while (UCB0CTL1 & UCTXSTT);
						UCB0CTL1 |= UCTXSTP;
						IFG2 &= ~UCB0TXIFG;         	// Clear TX interrupt flag
					}
				}
				break;
		}
	}
}

  • Sorry, I messed up with the syntax highlighter. The last part of my question is at the end of the first code snippet.

  • Hi Wayne,

    1. Can you post a whole code and comment this part of code in your whole code? It will make me easy to understand what happens.

    if(i2c.length == 1)
    {
        UCB0CTL1 |= UCTXSTP;
    }
    Eason
  • What do you mean ''why my code doesn't work when I try to receive 2 or more byte"? You can receive one or none?

    Can you post some debug information by yourself?

    Eason

  • I can post the whole code later when I get a chance if needed, but that piece of code starts on line 108 in the whole code above.

  • When I call the function "i2c_read" with the length parameter set to 1,  everything works fine.

    When I call the same function with the length parameter set to 2, the code doesnt work.

    I think I can post debugging info, what kind of info are you looking to see?

  • Hi Wayne,

    1. You mean "call the same function with the length parameter set to 2" is "i2c_read(length=2)"

    2. "The code doesnt work." has different means. Can you give more details what happens?

    3. The debug information I want is:1) the wave of I2C at different situation. 2)The register and array status in debug view.

    4. See your code:

        if(IFG2 & UCB0RXIFG)                            // Master Receive?
        {
            i2c.length--;
            if(i2c.length)
            {
                *i2c.data++ = UCB0RXBUF;
                if(i2c.length == 1)
                {
                    UCB0CTL1 |= UCTXSTP;
                }
            }
            else
            {
                *i2c.data = UCB0RXBUF;
    //          UCB0CTL1 |= UCTXSTP;
                IFG2 &= ~UCB0TXIFG;                     // Clear TX interrupt flag
                __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
            }
        }
    

    If you set i2c.length =2 , it will jump to "UCB0CTL1 |= UCTXSTP", then generate a stop, then the communication finish. You will receive only one data

    Eason

  • I solved the issue clearing the UCTXIFG bit before generating the repeat start condition. Below is the working code. By the way I have this working with MSP430G2553 and MPU-9250 if anyone was wondering.

    Main:

    /*
     * main.c
     *
     *  Created on: Nov 22, 2019
     *      Author: Wayne
     */
    
    #include <msp430.h>
    #include "msp430_i2c.h"
    
    
    void main()
    {
    	unsigned char *PRxData;						// Pointer for Rx data
    	volatile unsigned char RxBuffer[64];		// Allocate space for Rx data
    
    	unsigned char const *PTxData;               // Pointer to TX data
    	unsigned char const TxData[] =              // Table of data to transmit
    	{
    	  0x40,
    	  0x4A
    	};
    
    	PTxData = TxData;
    	PRxData = (unsigned char *)RxBuffer;		// Point to begining address of RxBuffer
    
    	WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    
    	i2c_enable();
    //	i2c_write(0x68, 0x6A, 1, PTxData);	// Enable fifo
    //	i2c_write(0x68, 0x77, 2, PTxData);
    
    
    	while(1)
    	{
    //		i2c_write(0x68, 0x13, 2, PTxData);	// Enable fifo
    		i2c_read(0x68, 0x75, 2, PRxData);	// Read whoAmI register.Next byte is '0' b/c 0x76 is not a reg
    	}
    }

    msp430_i2c.c:

    /*
     * msp430_i2c.c
     *
     *  Created on: Nov 24, 2019
     *      Author: Wayne
     *
     *      Description: i2c driver for msp430g2553 using interrupts.
     */
    #include <msp430.h>
    
    #define	SCL_PIN		BIT6
    #define	SDA_PIN		BIT7
    
    typedef enum {
        STATE_WAITING,
        STATE_READING,
        STATE_WRITING
    } msp430_i2c_state;
    
    typedef struct {
        volatile msp430_i2c_state state;
        unsigned char slave_reg;			// First slave register.
        unsigned char slave_reg_written;	// 0 if slave register has not been written yet. */
        unsigned char *data;
        unsigned short length;
        unsigned char enabled;
    } msp430_i2c_info;
    
    static msp430_i2c_info i2c = {
        .enabled = 0
    };
    
    void i2c_enable()
    {
    	/*Initialize USCI I2C Module*/
    	BCSCTL3 |= LFXT1S_2;						// VLOCLK (~12 KHz) feeding ACLK (Select ACLK source to be internal VLO)
    	UCB0CTL1 |= UCSWRST | UCSSEL_1;				// Hold USCI_B in reset for initialization (Set UCSWRST) & Select ACLK
    	UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;       // I2C Master Mode | I2C mode
    	UCB0BR0 = 8;								// Set i2c frequency
    	UCB0BR1 = 0;
    	P1SEL  |= SDA_PIN | SCL_PIN;                // Configure ports
    	P1SEL2 |= SDA_PIN | SCL_PIN;
    	UCB0CTL1 &= ~UCSWRST;                       // Release USCI_B for operation (Clear UCSWRST)
    	IE2 |= UCB0TXIE | UCB0RXIE;					// Enable interrupts (optional) via UCxRXIE and/or UCxTXIE
    }
    
    int i2c_write(unsigned char slv_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data)
    {
        /* Populate struct. */
        i2c.state = STATE_WRITING;
        i2c.slave_reg = reg_addr;
        i2c.slave_reg_written = 0;
        i2c.data = (unsigned char*)data;
        i2c.length = length;
    
        UCB0I2CSA = slv_addr;           // write slave address to UCB0I2CSA register
        UCB0CTL0 &= ~UCSLA10;           // 7-Bit slave addressing mode
        UCB0CTL1 |= UCTR + UCTXSTT;     // set UCTR for tx mode + set UCTXSTT to generate start condition
    
        while(i2c.state != STATE_WAITING)
        	{	/* Enter LPM0 w/ interrupts */
        		__bis_SR_register(CPUOFF + GIE);
        		i2c.state = STATE_WAITING;
        	}
    
        return 0;  // this return is pointless. need to return some err or something
    }
    
    
    int i2c_read(unsigned char slv_addr,unsigned char reg_addr,unsigned char length,unsigned char *data)
    {
    	/* Populate struct. */
    	i2c.state = STATE_READING;
    	i2c.slave_reg = reg_addr;
    	i2c.slave_reg_written = 0;
    	i2c.data = data;
    	i2c.length = length;
    
    	UCB0I2CSA = slv_addr;           	// write slave address to UCB0I2CSA register
    	UCB0CTL0 &= ~UCSLA10;           	// 7-Bit slave addressing mode
    	while (UCB0CTL1 & UCTXSTP);         // Ensure stop condition got sent
    	UCB0CTL1 |= UCTR | UCTXSTT;         // I2C start condition
    
    	while(i2c.state != STATE_WAITING)
    	{	/* Enter LPM0 w/ interrupts */
    		__bis_SR_register(CPUOFF + GIE);
    		i2c.state = STATE_WAITING;
    	}
    
    	return 0;
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCIAB0TX_VECTOR))) USCIAB0TX_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    	if(IFG2 & UCB0RXIFG)							// Master Receive?
    	{
    		i2c.length--;
    		if(i2c.length)
    		{
    			*i2c.data++ = UCB0RXBUF;
    			if(i2c.length == 1)
    			{
    				UCB0CTL1 |= UCTXSTP;
    			}
    		}
    		else
    		{
    			*i2c.data = UCB0RXBUF;
    //			UCB0CTL1 |= UCTXSTP;
    //			IFG2 &= ~UCB0TXIFG;         			// Clear TX interrupt flag
    			__bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
    		}
    	}
    
    	else if(IFG2 & UCB0TXIFG)						// Master Transmit?
    	{
    		switch (i2c.state)
    		{
    			case STATE_WRITING:
    				if (!i2c.slave_reg_written)
    				{
    					i2c.slave_reg_written = 1;			//
    					UCB0TXBUF = i2c.slave_reg;			// Send slave address
    				}
    				else if (i2c.length)					// Data to transmit?
    				{
    					char next = *i2c.data;				//
    					i2c.data++;							// increment byte
    					i2c.length--;						// decrement the amount of data left to be sent
    					/* Writing to TXBUF must always be the final operation. */
    					UCB0TXBUF = next;
    				}
    				else									// No More data to send?
    				{
    					UCB0CTL1 |= UCTXSTP;				// Generate Stop condition.
    					IFG2 &= ~UCB0TXIFG;					// Clear Tx interrupt flag
    					__bic_SR_register_on_exit(CPUOFF + GIE);
    				}
    				break;
    			case STATE_READING:
    				if (!i2c.slave_reg_written)
    				{
    					i2c.slave_reg_written = 1;
    					UCB0TXBUF = i2c.slave_reg;
    				}
    				else
    				{
    					/* Repeated start, switch to RX mode. */
    					IFG2 &= ~UCB0TXIFG;         			// Clear TX interrupt flag (Not sure if this is correct place to do so)
    					UCB0CTL1 &= ~UCTR;
    					UCB0CTL1 |= UCTXSTT;
    
    					/* If single byte, prepare stop signal immediately. */
    					if (i2c.length == 1)
    					{
    						/* Well, not IMMEDIATELY. First we need to make sure
    						 * the start signal got sent.
    						 */
    						while (UCB0CTL1 & UCTXSTT);
    						UCB0CTL1 |= UCTXSTP;
    						IFG2 &= ~UCB0TXIFG;         	// Clear TX interrupt flag
    					}
    				}
    				break;
    		}
    	}
    }
    

**Attention** This is a public forum