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.

CCS/MSP430FR5994: Using I2C - NACK or Useless Data

Part Number: MSP430FR5994

Tool/software: Code Composer Studio

Hi all,

I've been struggling for the past couple of weeks to get basic I2C working using the MSP430FR5994 and the VEML6070. My code is below.

#include <msp430.h> 
#include <stdio.h>

/*
 * Starting I2C with UV sensor.
 */

int MSB;
int LSB;
int test;
long UV_data;
int main(void) {
    WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
	
    // Configure one FRAM waitstate as required by the device datasheet for MCLK
    // operation beyond 8MHz _before_ configuring the clock system.
    FRCTL0 = FRCTLPW | NWAITS_1; // Change the NACCESS_x value to add the right amount of waitstates (1 per datasheet)

    // Set up XT1, 32.768 kHz external crystal
    PJSEL0 = BIT4 | BIT5;                   // For XT1

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;

    // Clock System Setup
    CSCTL0_H = CSKEY_H;                     // Unlock CS registers
    CSCTL1 = DCOFSEL_0;                     // Set DCO to 1MHz
    // Set SMCLK = MCLK = DCO, ACLK = VLOCLK
    CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK;
    // Per Device Errata set divider to 4 before changing frequency to
    // prevent out of spec operation from overshoot transient
    CSCTL3 = DIVA__4 | DIVS__4 | DIVM__4;   // Set all corresponding clk sources to divide by 4 for errata
    CSCTL1 = DCOFSEL_4 | DCORSEL;           // Set DCO to 16MHz
    // Delay by ~10us to let DCO settle. 60 cycles = 20 cycles buffer + (10us / (1/4MHz))
    __delay_cycles(60);
    CSCTL3 = DIVA__32 | DIVS__1 | DIVM__1;   // Clock dividers (divide max clock by these)
    CSCTL4 &= ~LFXTOFF;
        do
        {
            CSCTL5 &= ~LFXTOFFG;                // Clear XT1 fault flag
            SFRIFG1 &= ~OFIFG;
        } while (SFRIFG1 & OFIFG);              // Test oscillator fault flag
    CSCTL0_H = 0;                           // Lock CS registers

    // Configure USCI_B1 for I2C mode, transmitter
	UCB1CTLW0 |= UCSWRST;                   // Software reset enabled
	UCB1CTLW0 |= UCMODE_3 | UCMST | UCSYNC | UCSSEL__SMCLK; // I2C mode, Master mode, sync, UCTR = 0, SMCLK
	UCB1CTLW1 |= UCASTP_2;                  // Stop is auto generated

    // Set up UCB1 (SDA and SCL function, per datasheet)
	P5DIR &= ~(BIT0 | BIT1);
	P5REN &= ~(BIT0 | BIT1);
    P5SEL1 &= ~(BIT0 | BIT1);
    P5SEL0 = BIT0 | BIT1; 
											// after UCB1TBCNT is reached
	UCB1BRW = UCBR9;                       // baudrate = SMCLK / (0x0200h = 512 decimal)
	UCB1TBCNT = 0x0001;                     // number of bytes to be received (5)
	UCB1I2CSA = 0x18;                     // Initialization address
	UCB1CTLW0 &= ~UCSWRST;
	UCB1IE |= UCNACKIE | UCTXIFG0 | UCRXIFG0;
	//UCB1CTLW0 |= UCTXSTT;
	//__bis_SR_register(LPM3_bits | GIE);

	//test = 10;
	//UCB1CTLW0 |=

	while(1) {
		UCB1CTLW0 |= UCSWRST; // Change registers for LSB
		UCB1I2CSA = 0x39; // Slave address, read MSB (39h)
		UCB1CTLW0 &= ~UCSWRST; // Stop changing registers
		UCB1IE |= UCNACKIE | UCTXIFG0 | UCRXIFG0;
		test = 1; // get MSB
		UCB1CTLW0 |= UCTXSTT; // Start command
		__bis_SR_register(LPM3_bits | GIE);

		UCB1CTLW0 |= UCSWRST; // Change registers for LSB
		UCB1I2CSA = 0x38; // Slave address, read LSB (38h)
		UCB1CTL1 &= ~UCSWRST; // Stop changing registers
		UCB1IE |= UCNACKIE | UCTXIFG0 | UCRXIFG0;
		test = 0;
		UCB1CTLW0 |= UCTXSTT; // Start command
		__bis_SR_register(LPM3_bits | GIE);
		printf("%d\n", MSB);
		printf("%d\n", LSB);
		UV_data = (MSB << 8) | LSB;
		printf("%ld\n", UV_data);
		__delay_cycles(16000000);
	}
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = EUSCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(EUSCI_B1_VECTOR))) USCI_B1_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG))
    {
        case USCI_NONE:          break;     // Vector 0: No interrupts
        case USCI_I2C_UCALIFG:   break;     // Vector 2: ALIFG
        case USCI_I2C_UCNACKIFG:            // Vector 4: NACKIFG
            UCB1CTL1 |= UCTXSTT;            // I2C start condition
            break;
        case USCI_I2C_UCSTTIFG:  break;     // Vector 6: STTIFG
        case USCI_I2C_UCSTPIFG:  break;     // Vector 8: STPIFG
        case USCI_I2C_UCRXIFG3:  break;     // Vector 10: RXIFG3
        case USCI_I2C_UCTXIFG3:  break;     // Vector 12: TXIFG3
        case USCI_I2C_UCRXIFG2:  break;     // Vector 14: RXIFG2
        case USCI_I2C_UCTXIFG2:  break;     // Vector 16: TXIFG2
        case USCI_I2C_UCRXIFG1:  break;     // Vector 18: RXIFG1
        case USCI_I2C_UCTXIFG1:  break;     // Vector 20: TXIFG1
        case USCI_I2C_UCRXIFG0:             // Vector 22: RXIFG0
        	if (test==1){
        		MSB = UCB1RXBUF; // Get MSB data
        	}
        	else if (test==0) {
        		LSB = UCB1RXBUF; // Get LSB
        	}
        	__bic_SR_register_on_exit(LPM3_bits);
            break;
        case USCI_I2C_UCTXIFG0:  	        // Vector 24: TXIFG0
        	//UCB1TXBUF = 0xE; // 00001110, longest integration time and ACK off, shut down off
        	__bic_SR_register_on_exit(LPM3_bits);
        	break;
        case USCI_I2C_UCBCNTIFG:            // Vector 26: BCNTIFG
            P1OUT ^= BIT0;                  // Toggle LED on P1.0
            break;
        case USCI_I2C_UCCLTOIFG: break;     // Vector 28: clock low timeout
        case USCI_I2C_UCBIT9IFG: break;     // Vector 30: 9th bit
        default: break;
    }
}

For now, ignore the 0x18 address. According to the VEML6070 datasheet, this is an address that should be read for initialization purposes, but any attempt I make to read it results in a NACK.

The other two addresses, 0x39 and 0x38, are read-only registers according to the datasheet of the VEML6070, and provide UV data in MSB, LSB format. 

The response of the MSP to this code is the following:

The response of an Arduino polling the same addresses is the following:

Upon sending the 0x38 address, the Arduino receives an ACK while the MSP430 receives a NACK.

Here are my thoughts on this:

  • Why does the Arduino appear to pause for a cycle periodically?
  • Do I have to operate at 100kHz as the Arduino is? I am currently operating at closer to 32kHz because I thought this would increase my chances of success.
  • Can I change the mode of the MSP430FR5994 from standard to fast and vice versa? I didn't see anything about changing the mode in the user's guide, so I assume that the mode is only a number and no change in code need be made.
  • Both the MSP430FR5994 and the VEML6070 can operate on 3.3V logic. With that in mind, the fact that the Arduino uses 5V logic should make no difference, right?
  • Could the voltage spike be a problem in the MSP plot? If so, how do I deal with that?
  • I don't see a stop set in the MSP plot; is that due to the fact that I got a NACK?
  • Based on the datasheet of the VEML6070, I don't think that I need to initialize the device to access data registers. Would you agree?
  • I don't see the master (Arduino) sending an ACK to the VEML6070, which the VEML6070's datasheet says should happen. This doesn't appear to be a problem.
  • Can the master receiver ACK be enabled and disabled?
  • Does anything look bad about my MSP I2C signal versus the Arduino signal?

A member of my group is similarly having trouble integrating SPI with the MSP430. We're using the breakout for the VEML6070, which appears to use 10k resistors instead of the 2.2k recommended in the VEML6070 datasheet. I've tried a resistive divider to bring down the value of the pullup resistors, but that doesn't appear to work.

When running code, I've made sure that all registers are using the correct values as much as possible. I've check pin configuration, clock speed, and eUSCI_B1 configuration extensively.

My test setup is as pictured below.

Yellow goes to ground, blue to P5.1, and green to P5.0. I recognize that this isn't the best setup, but wires of the exact same length and the same pullup resistors work for the Arduino.

For reference, the Arduino is a SparkFun RedBoard.

Basically, I need help getting an ACK from the slave VEML6070 using I2C. I've tried so much, but can't seem to get a positive result.

Any help is greatly appreciated!

  • The three pictures appear not to have come through.

    In order:

  • Pauses are of no concern; master and slave are synchronized by the clock signal.

    There is no minimum frequency. Fast mode would just be a higher frequency (and tighter electrical specifications to ensure that the signals go through).

    You do not see a stop because you did not tell the MSP to send a stop. (The automatic stop does not happen because you did not receive one byte.)

    After the last read byte, the master sends a NACK. (This must then be followed by a restart or a stop.)

    You do not need to enter reset state to change the slave address register.

    I see no obvious reason why the slave would not answer. Possibly it got confused by the missing previous stop. You could try connecting its power to a GPIO output to be able to reset it.
  • Hi Clemens,

    Thank you for the quick response!

    I think the pauses could be something from the Arduino I2C library, and they are one of the most notable differences I can see between the MSP430's output (not working) and the Arduino's (working). It's like it waits an SCL cycle.

    The Arduino also operates on 5V logic. Other than that...the signal from the MSP looks very strong to me and it is indeed confusing.

    That MSP communication scope picture is the very first thing I send. I wonder if using the GPIO to power the VEML6070, as you mentioned, could be used to only have it be on after all the configuration is done. Where in the code I supplied would it likely be best to turn on the sensor, do you think? I'm thinking that it is possible the VEML6070 reads some changing in SCL and SDA during configuration/ powerup of the MSP as a signal. It would be odd, however, because the Arduino similarly is always powering the sensor.

    Thanks again.

  • VEML6070 datasheet (www.vishay.com/.../veml6070.pdf) talks about an initialization which "is recommended to be completed within 150 ms" (from power-on, I guess).
    It could be that the device doesn't reply anymore if that initialization isn't provided.

    Please note also that you are playing with fire not making volatile MSB and LSB variables.

    Regards,

    Peppe
  • Peppe,

    Yes, that is a good point. I've considered this, but the Arduino test code (github.com/.../Adafruit_VEML6070

  • It removed the rest of what I said.

    The datasheet is a bit vague, but I haven't gotten a response from an email to Vishay yet (~3 days).

    I will try using GPIO to turn it on then initialize quickly. However, the arduino code (at the github address) doesn't appear to ever follow the initialization procedure, but works. I'm a bit lost.
  • You are wrong, the Adafruit driver is performing the initialization calling Adafruit_VEML6070::begin (as a consequence of uv.begin).
    Regards,
    P.
  • Hi,

    I see that it is performing the 0x06 write, but it does not appear to first address 0x18, per the datasheet. If it is, could you point to where it accesses the Alert register?

    I will try sending a write command shortly after power-on.
  • Address 0x18 isn't needed in Initialization, it is related to Acknowledge Activity task.

    As you can see, value 0x06 has to be written to the same 0x38 register that holds LSB of readings.

    Regards,

    P.

  • Hi,

    I think you've probably answered my original question, and will mark it as such soon.

    However, I2C isn't working consistently still.

    The bus is almost always (not always) busy when I try to send my first communication. This is odd, considering that the UV sensor and the MSP430 are currently the only things on the bus.

    I've read that it could be improper grounding, and so I checked that. But it did not fix the issue.

    Does anyone know why my code shows the bus as busy? Here are the lines surrounding the point when the bus is busy.

    // Initialization function
    void UV_I2C_INIT(void) {
    	// Power for I2C UV
    	P8DIR |= BIT2;
    	P8OUT &= ~(BIT2);
    
    	// Configure USCI_B1 for I2C mode, transmitter
    	UCB1CTLW0 |= UCSWRST;                   // Software reset enabled
    	UCB1CTLW0 |= UCMODE_3 | UCMST | UCSYNC | UCTR | UCSSEL__SMCLK; // I2C mode, Master mode, sync, UCTR = 1, SMCLK
    	UCB1CTLW1 |= UCASTP_2;                  // Stop is manually generated
    
    	// Set up UCB1 (SDA and SCL function, per datasheet)
    	P5DIR &= ~(BIT0 | BIT1);
    	P5REN &= ~(BIT0 | BIT1);
    	P5SEL1 &= ~(BIT0 | BIT1);
    	P5SEL0 = BIT0 | BIT1;
    	// after UCB1TBCNT is reached
    	UCB1BRW = UCBR9;           // baudrate = SMCLK / (0x0200h = 512 decimal)
    	UCB1TBCNT = 0x0001;                // number of bytes to be received (5)
    	UCB1I2CSA = 0x38;                     // Write initialize address
    	UCB1CTLW0 &= ~UCSWRST;
    	__delay_cycles(10000);
    
    	// Disable the GPIO power-on default high-impedance mode to activate
    	// previously configured port settings
    	PM5CTL0 &= ~LOCKLPM5;
    
    	// Initialize
    	P8OUT |= BIT2; // Turn on power
    	UCB1CTLW0 |= UCTXSTP; // clear busy
    	UCB1CTLW0 |= UCTXSTT;
    	while ((UCTXIFG0 & UCB1IFG) == 0);
    	UCB1TXBUF = 0x06;
    	UCB1IFG &= ~UCTXIFG0; // Clear flag
    	while ((UCB1IFG & UCSTPIFG) == 0);
    	UCB1IFG &= ~UCSTPIFG;
    }

    Thanks.

  • I found that my issue is hardware now -- the resistance from SCL to ground is 5-44k while it is 1.5M for SDA and ACK. Either I find the short, or buy another.

    Thanks, I verified your solution!

**Attention** This is a public forum