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: Failed I2C Communication with BH1750FVI

Part Number: MSP430F5529

Tool/software:

Hello guys im trying to communicate with the sensor BH1750FVI with a MSP430F5529 but im facing an issue of NACK from part of the slave when im trying to read data. And getting back null data.

The address pin of the sensor is connected to vcc -> 0x5C

Also inside the while loop my code is getting stuck in: 

        __bis_SR_register(LPM0_bits + GIE);

And not moving on for the next message.

The code is shown bellow, im also using pull-up resistors of 5k ohm and a frequency of 10KHz just to try to get it to work right now.

#include "driverlib.h"
#include "intrinsics.h"
#include "msp430f5529.h"
#include "msp430f5xx_6xxgeneric.h"
//#include <cstdint>

uint8_t TransmitBuffer;
uint8_t* on = 0x01; // turn on
uint8_t* data = 0x20; // get data
uint8_t Data_In[2] = {0, 0};
int bytes = 0;

typedef enum I2C_ModeEnum{
    SENSOR_ACTIVATE,
    TX_DATA_MODE
} I2C_Mode;

I2C_Mode MasterMode;

void sensorON(void);

void main (void)
{
    WDTCTL = WDTPW | WDTHOLD;

    P4DIR |= BIT7;
    P1DIR |= BIT0;

    UCB1CTL1 |= UCSWRST; // put in SW reset

    UCB1CTL1 |= UCSSEL_3;
    UCB1BR0 = 100;                            // fSCL = SMCLK/10 = ~100kHz
    UCB1BR1 = 0;

    UCB1CTL0 |= UCMODE_3 + UCSYNC; // I2C mode
    UCB1CTL0 |= UCMST;    // Master mode
    UCB1I2CSA = 0x5C;                           // Slave Address is 5C -> address pin connected to VCC

    P4SEL &= ~BIT1;
    P4SEL &= ~BIT2;

    P4SEL |= BIT1 + BIT2; // PIN 4.1, 4.2 for communication

    UCB1CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
    UCB1IE |= UCNACKIE + UCTXIE + UCRXIE;

    // Communication
    while (1)
    {
        sensorON();

        // Transmit register Address with WRITE message
        MasterMode = TX_DATA_MODE;
        UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
        UCB1IE &= ~UCRXIE;                       // Disable RX interrupt
        UCB1IE |= UCTXIE;                        // Enable TX interrupt
        UCB1CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
        bytes = 2;
        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();

        if (Data_In[0] != 0 | Data_In[1] != 0)
        {
            P4OUT |= BIT7;
        }

    }


}

void sensorON(void)
{
    // Transmit register Address with WRITE message
    UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
    UCB1IE &= ~UCRXIE;                       // Disable RX interrupt
    UCB1IE |= UCTXIE;                        // Enable TX interrupt
    UCB1CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
    MasterMode = SENSOR_ACTIVATE;
    __bis_SR_register(LPM0_bits + GIE);
    __no_operation();
}

//-------------------------------------------------------------------------------
//-- ISR
#pragma vector=USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
{
    switch(UCB1IV)
    {       
        case USCI_NONE:break;                             // Vector 0 - no interrupt
        case USCI_I2C_UCALIFG:break;                      // Interrupt Vector: I2C Mode: UCALIFG
        case USCI_I2C_UCNACKIFG:
            P1OUT |= BIT0;
            break;                    // Interrupt Vector: I2C Mode: UCNACKIFG
        case USCI_I2C_UCSTTIFG:break;                     // Interrupt Vector: I2C Mode: UCSTTIFG
        case USCI_I2C_UCSTPIFG:break;                     // Interrupt Vector: I2C Mode: UCSTPIFG
        case USCI_I2C_UCRXIFG:
            if (bytes)
            {
                Data_In[bytes-1] = UCB1RXBUF;
                bytes--;
            }

            if (bytes == 1)
            {
                UCB1CTL1 |= UCTXSTP;
            }

            else if (bytes == 0)
            {
                UCB1IE &= ~UCRXIE;
                __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
            }
            break;
        case USCI_I2C_UCTXIFG:
            switch (MasterMode)
            {    
                case SENSOR_ACTIVATE:
                //UCB1CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
                UCB1TXBUF = 0x01; // turn on sensor
                UCB1CTL1 |= UCTXSTP;
                __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                //while (!(UCB1IFG & UCSTPIFG));  // Wait until STOP condition interrupt flag is set
                //UCB1IFG &= ~UCSTPIFG;           // Clear it
                break;

                case TX_DATA_MODE:
                UCB1TXBUF = 0x10; // read command
                UCB1IE |= UCRXIE;              // Enable RX interrupt
                UCB1IE &= ~UCTXIE;             // Disable TX interrupt
                UCB1CTL1 &= ~UCTR;            // Switch to receiver
                UCB1CTL1 |= UCTXSTT;          // Send repeated start
                if (bytes == 1)
                {
                    //Must send stop since this is the N-1 byte
                    while((UCB1CTL1 & UCTXSTT));
                    UCB1CTL1 |= UCTXSTP;      // Send stop condition
                }
                __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                break;


        default:
              __no_operation();
              break;
            }
            break;
        default:
            break;
    }
}





I provide some pictures i took from the oscilloscope.
In the start of the code:
When sending 0x10 in UCB1TXBUF (Read command):
In this i think im getting a NACK? After the read command:
  • Update: I tried the same circuit setup and the same sensor address on a arduino, with the example provided by the sensor's manufacturers and it worked so its definitely something in the MSP code if anyone could help me out i would appreciate it very much

  • Datasheet (2011.11 Rev D) p. 10 says "BH1750FVI is not able to accept plural command without stop condition. Please insert SP every 1 Opecode." I wonder if that means it doesn't accept a Repeated-Start. Anyway it seems as though you would normally separate the opcode and the data (result) retrieval by enough time for the sampling which would make Repeated-Start moot.

    [Edit: Do you see the NACK indicator (P1.0) light up?]

  • One problem I see is that your function sensorOn() returns without waiting for the transaction to complete. It waits for the transmit interrupt only. So there is a race between the transaction completing and starting the next transaction by calling for a start condition.

    The transaction never completes because it is slow. (10KHz clock)

  • I changed the frequency to 500 Khz

        UCB1BR0 = 2;                            // fSCL = SMCLK/2 = ~500kHz
        UCB1BR1 = 0;

    Still facing the same problem i did try to add:
    while ( (UCB1IFG & UCSTPIFG) == 0);
    UCB1IFG &= ~UCSTPIFG; // clear stop flag

    But to no effect it just got stuck in that any clues?

    Thank you very much for your help
  • In the datasheet it dosen't say nothing of repeated start i just presumed it needed so im gonna give it a try, gonna comment the repeated start.

    So im thinking I just need to straight up read the UCB1RXBUF after the message is sent right?

    So you think I dont need a repeated start? Its wierd beacuse on the wave being received i get a null value and i think that is the MSB byte of the data.

    Answering your question i never get back a NACK indicator, the LED never gets light up 

    Thank you very much for your help

  • The sensing model (datasheet p. 7) seems to be (1) Write "Continuously H-res mode" (0x10) (2) wait 180ms (3) Read result; then repeat (2)-(3) as long as you want. One-time seems to be similar except you use one of the "One-Time" opcodes in (1) and there's a step (0) Write "power-on".

    The only NACK I see in your scope traces is in the second, where it follows a byte of 0x00, but I can't tell which direction it's going. If the MSP430 is the Master-Receiver it will NACK the final byte before issuing the Stop, so maybe that's what that is?

  • You didn't say where you added that. 

    UCTXSTP is cleared automatically by the hardware after the stop condition is generated. So wait for that bit to clear.

    Also, a check of UCBBUSY is generally a good idea before attempting to start a transaction. 

    Another problem I just spotted. You wait in low power mode for an event and then assume that only one event could do that. When that isn't the case. For example, you start the read and then wait for the interrupt service routine to complete and cause an exit from LPM0. But there are two things that will do that. 

    There is an exit right after the restart to go into read mode. You don't wait for the data. (See case TX_DATA_MODE.)

  • Sorry i added here, and it gets stuck in that while:

            case USCI_I2C_UCTXIFG:
                switch (MasterMode)
                {    
                    case SENSOR_ACTIVATE:
                    UCB1TXBUF = 0x01;
                    UCB1CTL1 |= UCTXSTP;
                    __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                    while (!(UCB1IFG & UCSTPIFG));  // Wait until STOP condition interrupt flag is set
                    UCB1IFG &= ~UCSTPIFG;           // Clear it
                    break;
    So instead of  while(!(UCB1IFG & UCSTPIFG)); i should check while(UCB1CTL1 & UCTXSTP) ?
    Ok will add that!

    Answering your last point, i shouldnt exit straight away the TX_DATA_MODE? Could you please point out what are the other cases that will cause the interrupt service? Sorry im a little lost, i should wait for the data comming from the sensor in the TX_DATA_MODE? Add a delay to read it?

    Thanks for your help and patience
  • So i should add a delay in the TX_DATA_MODE to read the result (in the continously h-res mode)? 

    Sorry do you mean the second image or the last?

    So im receiving data but not seing it because of my code?

    Thank you for your help and patince, im a little lost

  • I don't recommend putting a 180ms delay in the middle of your transaction, since that just hangs the bus for no value.

    My thought is to go with what the datasheet suggests on p. 7: Write 2x I2C functions -- "Write1(byte)" which just does a Start/Write[1 byte]/Stop, and "Read2(result)" which just does a Start/Read[2 bytes]/Stop. Then your main program looks like:

    Write1(0x01); // Power Up
    Write1(0x10); // Continuous High-Res
    while(1) {
        delay(180); // Wait for some data
        result = Read2(); // Fetch latest reading
    }

    If you decided to go with One-Shot operations it would be a small manipulation of your main program.

    Each function would be rather simple (no modes) with very short transactions and so easy to debug. 

    Since you have access to an Arduino driver you could also look at what they do.

  • Ok that makes sense thank you so much for your help!

    If i want to do the One-Shot operation i would do something like this, right?


    while(1) {
        Write1(0x01); // Power on
        Write1(0x20); // One Time H-Res Mode
        delay(180);
        result = Read2();
    }


    Or what would you suggest I change?

    Once again thank you very much and i will look into that!

  • Yes, that's the idea.

    It may be that you'll need some kind of power-up delay -- at the beginning and/or after each Power-on opcode, it's hard for me to get that from the datasheet -- but that's also just an adjustment to main(), your functions wouldn't need to care.

  • Answering your last point, i shouldnt exit straight away the TX_DATA_MODE?

    It just did a restart so it could begin reading data. The code for that in the ISR calls for a low power mode exit once the data is read.

  • Following question: could my code being stuck in __bis__SR_register(LMP0_bits + GIE); be because of me trying to fire an interrupt but the earlier transaction wasnt completed and the bus stays on hold and cant finish the transaction and move to the RX_DATA_MODE case?

  • Thank you very much will try your suggestions tommorrow! And will give feedback on how it went!

  • Thank you very much for your pointers i added some lines to the original code , and now it ensures communication with the slave now and waits for the messages to be sent and resolved the issues i had with the interrupts your help was really necessary thank you very much! Now i ensure the communication like this, like you were suggesting:

     case SENSOR_ACTIVATE:
                    UCB1TXBUF = 0x01;

                    while (!(UCB1IFG & UCTXIFG)); // novo antes tinha o while do stop e o stop na 1a linha
                    UCB1CTL1 |= UCTXSTP;

                    UCB1IE &= ~UCTXIE;            

                    while(UCB1CTL1 & UCTXSTP); // esperar que a comunicação seja concluida
                    __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                    break;

    Thank you very much for your time and help!
  • Thank you for your help bruce i only need to figure out now the UART situation because im only seeing 05 when i cover my sensor, but sensor data looks ok now!

    Thank you very much for your time and help!

  • Thank you guys for your help i got it to work! The sensor is now sending data, i leave my code here for anyone who needs it!

    #include "driverlib.h"
    #include "intrinsics.h"
    #include "msp430f5529.h"
    #include "msp430f5xx_6xxgeneric.h"
    //#include <cstdint>
    //#include <cstdint>

    //******************************************************************************
    //!
    //!   Empty Project that includes driverlib
    //!
    //******************************************************************************

    uint8_t TransmitBuffer;
    uint8_t TX_Buf;
    uint8_t* on = 0x01; // turn on
    uint8_t* data = 0x20; // get data
    uint8_t Data_In[2] = {0, 0};
    int bytes = 0;
    uint16_t lux = 0;

    typedef enum I2C_ModeEnum{
        SENSOR_ACTIVATE,
        TX_DATA_MODE
    } I2C_Mode;

    I2C_Mode MasterMode;

    void sensorON(void);
    void writeCMD(void);
    void readRegister(void);

    void main (void)
    {
        WDTCTL = WDTPW | WDTHOLD;

        P4DIR |= BIT7;
        P1DIR |= BIT0;

        UCB1CTL1 |= UCSWRST; // put in SW reset

        UCB1CTL1 |= UCSSEL_3;
        UCB1BR0 = 4;                            // fSCL = SMCLK/10 = ~200kHz
        UCB1BR1 = 0;

        UCB1CTL0 |= UCMODE_3 + UCSYNC; // I2C mode
        UCB1CTL0 |= UCMST;    // Master mode
        UCB1I2CSA = 0x5C;                           // Slave Address is 023h

        P4SEL &= ~BIT1;
        P4SEL &= ~BIT2;

        P4SEL |= BIT1 + BIT2; // PIN 4.1, 4.2 for communication

        //PM5CTL0 &= ~LOCKLPM5;
       
        UCB1CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation

        // UART INIT

        P4SEL |= BIT4+BIT5;                       // P3.3,4 = USCI_A0 TXD/RXD
        UCA1CTL1 |= UCSWRST;                      // **Put state machine in reset**
        UCA1CTL1 |= UCSSEL_2;                     // SMCLK
        UCA1BR0 = 9;                              // 1MHz 115200 (see User's Guide)
        UCA1BR1 = 0;                              // 1MHz 115200
        UCA1MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
        UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
        UCA1IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt

        MasterMode = SENSOR_ACTIVATE;

        // Sensor ON
        writeCMD();
       
        MasterMode = TX_DATA_MODE; // switch to Transmit mode

        //while(UCB1CTL1 & UCTXSTP); // esperar que a comunicação seja concluida

        // Light continuous reading
        writeCMD();

        //while(UCB1CTL1 & UCTXSTP); // esperar que a comunicação seja concluida

        // Communication
        while (1)
        {
            //sensorON();
            __delay_cycles(130000); // 180ms delay
            readRegister();

            lux =  ( ( (uint16_t)  ( Data_In[1] ) << 8)  | (uint16_t) (Data_In[0]) );

            // Transmitir lux
            while (!(UCA1IFG & UCTXIFG));             // USCI_A0 TX buffer ready?
            UCA1TXBUF = lux;    

            if (Data_In[0] != 0 | Data_In[1] != 0)
            {
                P4OUT |= BIT7;
            }

        }



    }

    void sensorON(void)
    {
        // Transmit register Address with WRITE message
        UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
        UCB1IE &= ~UCRXIE;                       // Disable RX interrupt
        UCB1IE |= UCTXIE;                        // Enable TX interrupt
        UCB1CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
        MasterMode = SENSOR_ACTIVATE;
        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();
    }

    void writeCMD(void)
    {
        // Transmit register Address with WRITE message
        UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
        UCB1IE &= ~UCRXIE;                       // Disable RX interrupt
        UCB1IE |= UCTXIE;                        // Enable TX interrupt
        UCB1CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
       
        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();
    }

    void readRegister(void)
    {
        bytes = 2;

        UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts

        UCB1IE |= UCRXIE;              // Enable RX interrupt
        UCB1IE &= ~UCTXIE;             // Disable TX interrupt
        UCB1CTL1 &= ~UCTR;            // Switch to receiver
        UCB1CTL1 |= UCTXSTT;          // Send repeated start

        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();
    }
    //-------------------------------------------------------------------------------
    //-- ISR
    #pragma vector=USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    {
        switch(UCB1IV)
        {      
            case USCI_NONE:break;                             // Vector 0 - no interrupt
            case USCI_I2C_UCALIFG:break;                      // Interrupt Vector: I2C Mode: UCALIFG
            case USCI_I2C_UCNACKIFG:
                P1OUT |= BIT0;
                break;                    // Interrupt Vector: I2C Mode: UCNACKIFG
            case USCI_I2C_UCSTTIFG:break;                     // Interrupt Vector: I2C Mode: UCSTTIFG
            case USCI_I2C_UCSTPIFG:break;                     // Interrupt Vector: I2C Mode: UCSTPIFG
            case USCI_I2C_UCRXIFG:
                if (bytes)
                {
                    Data_In[bytes-1] = UCB1RXBUF;
                    bytes--;
                }

                if (bytes == 1)
                {
                    UCB1CTL1 |= UCTXSTP;
                }

                else if (bytes == 0)
                {
                    UCB1IE &= ~UCRXIE;
                    while(UCB1CTL1 & UCTXSTP); // esperar que a comunicação seja concluida
                    __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                }
                break;
            case USCI_I2C_UCTXIFG:
                switch (MasterMode)
                {    
                    case SENSOR_ACTIVATE:
                    UCB1TXBUF = 0x01;

                    while (!(UCB1IFG & UCTXIFG)); // novo antes tinha o while do stop e o stop na 1a linha
                    UCB1CTL1 |= UCTXSTP;

                    UCB1IE &= ~UCTXIE;            

                    while(UCB1CTL1 & UCTXSTP); // esperar que a comunicação seja concluida
                    __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                    break;

                    case TX_DATA_MODE:
                    UCB1TXBUF = 0x10; // read command
                   
                    while (!(UCB1IFG & UCTXIFG));
                    UCB1CTL1 |= UCTXSTP;

                    UCB1IE &= ~UCTXIE;  
                   
                    while(UCB1CTL1 & UCTXSTP); // esperar que a comunicação seja concluida
                    __bic_SR_register_on_exit(LPM0_bits);      // Exit LPM0
                    break;



            default:
                  __no_operation();
                  break;
                }
                break;
            default:
                break;
        }
    }




**Attention** This is a public forum