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.

MSP430F5529 I2C master TX stop condition

Other Parts Discussed in Thread: MSP430F5529

Hello,

I'm trying to control an I2C LED driver (IS31FL3199) as a slave using a MSP430F5529. I started by using one of the msp demos which continually sent bytes (MSP430F55xx_uscib0_i2c_07.c) and I was able to control the driver by changing the slave adress and the data to send without any problems. I also deactivated LPM0 and __no_operation) to have a better understanding of what's happening when the device stops responding. However, I'm struggling to convert this code from continually sending bytes to sending them on demand which requires me to stop transfers as the MSP is supposed to control another I2C device later on.

I've read the section about I2C in the MSP430Fx5xx family guide (slau208p) but I'm not sure where exactly I should put the STOP command during my transmission ISR. I've tried different possibilities but the results are always negative. I assume it has to do with the timing of the STOP command, whether it's set too early and a byte gets cut off during TX or the device hangs for too long before receiving the STOP.

Code is below. Everytime a start command is issued, 4 bytes which represent 2 commands are sent to the I2C device (first two bytes are for activating the device, other two is to power a LED). The data and slave address have been confirmed to be OK beforehand. After sending those 4 bytes, I want to stop the transfer and then restart it later. In the code below, I simply restart it a second later. In reality, I'll be using a GPIO interrupt or specific UART RX to send data to the I2C device. With this code, I manage to go thru the full TX ISR routine, jump back to main() and then restart the TX ISR routine without any problems. However, the LED stays shut down

Does anyone have an idea as to why I have this problem?

Thank you!

#include <msp430.h>

const unsigned char TxData[] =
{
0x00,0x01, // Start
0x0D,0xAF //OUT7
};

unsigned int i = 0;
int main(void)
{

WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P3SEL |= 0x03;                            // 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 = 0x64;                         // Slave Address is 054h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  UCB0IE |= UCTXIE;                         // Enable TX interrupt
  __enable_interrupt();
  while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
  UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition

  while (1)
  {
__delay_cycles(1000000);
    while (UCB0CTL1 & UCTXSTP);             
    UCB0CTL1 |= UCTR + UCTXSTT;             // Restart TX

  }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch(__even_in_range(UCB0IV,12))
  {
  case  0: break;                           // Vector  0: No interrupts
  case  2: break;                           // Vector  2: ALIFG
  case  4: break;                           // Vector  4: NACKIFG
  case  6:                                  // Vector  6: STTIFG
  {
    UCB0IFG &= ~UCSTTIFG;                   // Clear start condition int flag
    break;
  }
  case  8:                                  // Vector  8: STPIFG
    UCB0IFG &= ~UCSTPIFG;                   // Clear stop condition int flag
    __bic_SR_register_on_exit(LPM0_bits);   // Exit LPM0 if data was transmitted
    break;
  case 10: break;                           // Vector 10: RXIFG
  case 12:                                  // Vector 12: TXIFG
  {
	for (i =0;i<4;i++)
	{
		if(i==0 ||i==2)
			__delay_cycles(20000); // Used to let the IS31FL3199 apply the command
		UCB0TXBUF = TxData[i];

		__delay_cycles(10); // Delay between each byte sent
		if (i==3)
			UCB0CTL1 |= UCTXSTP; // Stop TX
	}


    break;
  }
  default: break;
  }
}


  • Please do not use magic numbers for the UCB0IV values; use proper symbols like USCI_I2C_UCTXIFG.

    The implementation of driverlib functions like USCI_B_I2C_masterSendSingleByte() or USCI_B_I2C_masterSendMultiByteFinish() shows that you set the TXSTP bit when you get the TXIFG interrupt but do not have a byte to send.

    Please note that the IFG bit is automatically cleared when reading the interrupt vector register.
  • Thank you for your quick answer. I've looked at the Driverlib code and tried to replicate it in my code (I'm trying to avoid using driverlib for the time being). I've replaced my wait cycle between each byte with the correct loop. I've also added the UCTXSTP command at the end of my ISR, but LEDs still stay off.

    In this case, is it safe to assume that the problem isn't from the ISR? I've written my new ISR below. It still sends the same 4 bytes before sending a STOP command.

    Thank you for your time.

    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCI_B0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCB0IV,12))
      {
      case  0: break;                           // Vector  0: No interrupts
      case  2: break;                           // Vector  2: ALIFG
      case  4: break;                           // Vector  4: NACKIFG
      case  6:                                  // Vector  6: STTIFG
      {
        UCB0IFG &= ~UCSTTIFG;                   // Clear start condition int flag
        break;
      }
      case  8:                                  // Vector  8: STPIFG
        UCB0IFG &= ~UCSTPIFG;                   // Clear stop condition int flag
        __bic_SR_register_on_exit(LPM0_bits);   // Exit LPM0 if data was transmitted
        break;
      case 10: break;                           // Vector 10: RXIFG
      case 12:                                  // Vector 12: TXIFG
      {
    	  UCB0IE &= ~UCTXIE;
    	for (i =0;i<4;i++)
    	{
    
    		if(i==0 ||i==2)
    			__delay_cycles(10000); // Pause between byte groups to see LEDs flicker
    
    		UCB0TXBUF = TxData[i];
    		while(!(UCB0IFG & UCTXIFG));
    	}
    	UCB0CTL1 |= UCTXSTP;
    	UCB0IFG &= ~UCTXIFG;
    	UCB0IE |= UCTXIE;
    
        break;
      }
      default: break;
      }
    }

  • Using waiting loops in an interrupt handler is a bad idea. Try writing the code without any interrupts first.
  • More specifically: You're stalling the bus for 10ms right after the device has accepted your original request. I'm wondering whether the device is giving up (timing out). What happens if you remove the delays?

    Did I read correctly that your device responded just fine back at the beginning, to a plain repetitive (unthrottled) request sequence? Maybe that's a good place to fall back to.
  • I hesitate to disagree with Clemens Ladisch, but my observation is that the I2C unit was designed with interrupts in mind, and trying to Not use interrupts is kind of a "square peg" exercise. (If I get to that situation I resort to bit-banging.)

    What you Do need to do is make sure to follow very carefully the state diagram in e.g. SLAU208P Figure 38-12.
  • My point is that the interrupt handler is supposed to be implemented as a state machine. But that would be more complex; as a first attempt to get the code to work, it's easier to just wait for the appropriate IFG bit when needed.

**Attention** This is a public forum