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.

MSP430FR5969: I2C with HDC2080 stuck in an infinite loop

Part Number: MSP430FR5969
Other Parts Discussed in Thread: HDC2080

I'm trying to connect a board that uses the MSP430FR5969 (the WISP 5.1) with a HDC2080 temperature/humidity sensor through I2C, using the driverlib HAL, based on this example. I'm using a 2k ohm pull-up, if that plays a part. The sensor was confirmed working fine with an Arduino, but now I wanted to replicate the functionality in the MSP.

Here's a code snippet I'm currently using (other unrelated parts omitted for brevity. If it helps, this is built on top of the WISP firmware in CCS 11):

#define SENSOR_ADDRESS 0x40             // HDC2080 address
#define SENSOR_CONFIG_REGISTER 0x0F     // HDC2080 configuration register

void initI2C(){
        WDT_A_hold(WDT_A_BASE);

        //Set DCO frequency to 1MHz
        CS_setDCOFreq(CS_DCORSEL_0,CS_DCOFSEL_0);
        //Set ACLK = VLO with frequency divider of 1
        CS_initClockSignal(CS_ACLK,CS_VLOCLK_SELECT,CS_CLOCK_DIVIDER_1);
        //Set SMCLK = DCO with frequency divider of 1
        CS_initClockSignal(CS_SMCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);
        //Set MCLK = DCO with frequency divider of 1
        CS_initClockSignal(CS_MCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);

        // Configure Pins for I2C
        //Set P1.6 and P1.7 as Secondary Module Function Input.
        /*

        * Select Port 1
        * Set Pin 6, 7 to input Secondary Module Function, (UCB0SIMO/UCB0SDA, UCB0SOMI/UCB0SCL).
        */
        GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P1,
            GPIO_PIN6 + GPIO_PIN7,
            GPIO_SECONDARY_MODULE_FUNCTION
        );

        /*
         * Disable the GPIO power-on default high-impedance mode to activate
         * previously configured port settings
         */
        PMM_unlockLPM5();

        //Initialize Master
        EUSCI_B_I2C_initMasterParam param = {0};
        param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
        param.i2cClk = CS_getSMCLK();
        param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
        param.byteCounterThreshold = 1;
        param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
        EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);

        //Specify slave address
        EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE, SENSOR_ADDRESS);

        //Set in transmit mode
        EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE);

        // Set timeout to prevent infinite loops
        EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE, 1000);

        //Enable I2C Module to start operations
        EUSCI_B_I2C_enable(EUSCI_B0_BASE);
}

uint8_t readI2CRegister(uint8_t registerAddress){

    // Send single byte as address
    EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);

    // Delay until transmission completes
    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

    // Switch to read mode
    EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_RECEIVE_MODE);

    // Receive start
    EUSCI_B_I2C_masterReceiveStart(SENSOR_ADDRESS);

    // Read data from register (and then send STOP)
    uint8_t data = EUSCI_B_I2C_masterReceiveSingle(SENSOR_ADDRESS);

    // Return data
    return data;
}

void writeI2CRegister(uint8_t registerAddress, uint8_t data){

    // Go to transmit mode
    EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);

    // Send single byte as address
    EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);

    // Delay until transmission completes
    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

    // Send data
    EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, data);

    // Delay until transmission completes
    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

}

void main(void) {

    ...

    // Init I2C
    initI2C();
    
    // Enable measurements on the HDC2080 with mostly default settings
    writeI2CRegister(SENSOR_CONFIG_REGISTER, 0x01);
    
    uint8_t temp1;
    while(true){
        ...
        temp1 = readI2CRegister(0x00);
        ...
    }
}

When this is run, it looks like the code is getting stuck in "EUSCI_B_I2C_masterSendSingleByte" (in driverlib's eusci_b_i2c.c). More specifically, it's looping in here:

//Poll for transmit interrupt flag.
while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

How can I further debug this problem?

  • Can you use an oscilloscope or digital analyzer to catch the wave? It can helps you know what happens.

  • This program contains multiple calls to EUSCI_B_I2C_masterSendSingleByte, and that function contains two occurrences of the code you quoted.

    If you can specify  the precise code (including the call stack) where it hangs, that will provide clues.

    Unsolicited: WriteI2CRegister issues two separate 1-byte (write) transactions, which doesn't do anything (sets the register address twice). You probably want to make a single 2-byte transaction using EUSCI_B_I2C_masterSendMultiByteStart followed by EUSCI_B_I2C_masterSendMultiByteFinish. This is probably not the cause of your symptom.

  • It looks like the only thing that get sent on the wire, based on the code above, is the start condition. There's nothing else...

  • Sure. The debugger indicates it is getting stuck in "EUSCI_B_I2C_masterSendSingleByte" (line 172 of eusci_b_i2c.c)

    The call stack is as follows:

    TI MSP430 USB1/MSP430 (Suspended)
    	EUSCI_B_I2C_masterSendSIngleByte(unsigned int, unsinged char)() at eusci_b_i2c.c:186 0x005E96
    	writeI2CRegister(unsigned char, unsigned char)() at main.c:139 0x006028
    	main() at main.c:170 0x00556C
    	_c_int00_noargs() at boot.c:125 0x0062F4 (the entry point was reached)

    Here are the eUSCI I2C registers. There seems to be one error indication, although I'm not sure whether this contributes to the loop I'm seeing or not:

    Thanks for the pointer on using multi-byte transactions! I'll also update my code for that.

  • >EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE, 1000);

    This function is not intended to accept a number, rather a couple of specific bits. The result is that a bunch of unusual options are set in UCB0CTLW1 (I haven't tried to figure out how they interact).

    Try instead something like:

    >EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE, EUSCI_B_I2C_TIMEOUT_34_MS);  // 34ms bus timeout

  • That, and the code change seems to now enable some bits to be sent. However, it still looks incomplete. Now, I have this as my write code:

    void writeI2CRegister(uint8_t registerAddress, uint8_t data){
    
        // Go to transmit mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);
    
        // Send single byte as address
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, registerAddress);
    
        // Delay until transmission completes
        // Code loops infinitely here
        while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));
    
        // Send data
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, data);
    
        // Delay until transmission completes
        while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));
    
    }

    Now, when I try to call:

    writeI2CRegister(0x0F, 0x01);

    I'm getting this. Looks like the second byte isn't being sent at all:

    Now though, it seems that the infinite loop location has changed. It's now at:

    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

    Right after the first call to "EUSCI_B_I2C_masterSendMultiByteStart".

  • The bus is busy between the two bytes -- waiting for you to do something -- so you shouldn't wait for that to end -- just remove that line.

    There is also a sendMultiByteNext if you ever need it. The three are designed so you can just call Start/[N*]Next/Finish back-to back.

    MultiByteFinish (as well as SingleByte) finishes by requesting a Stop after the current/next byte, a sort of a write-behind. It's probably a good idea to wait for the Stop to complete after the transaction is done (or before the next one starts), using something like:

    >  while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE))/*EMPTY*/;

    which just tests to see if UCTXSTP is (still) set.

  • Ah, I see. I've changed the write code to this and now that part is working as expected:

    void writeI2CRegister(uint8_t registerAddress, uint8_t data){
    
        // Go to transmit mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);
    
        // Send single byte as address
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, registerAddress);
    
        // Send data
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, data);
    
        // Make sure I2C stop condition is sent
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
    }

    However, the read part is still getting stuck in "EUSCI_B_I2C_masterReceiveSingle", with the call stack:

    TI MSP430 USB1/MSP430 (Suspended)
    	EUSCI_B_I2C_masterReceiveSingle(unsigned int)() at eusci_b_i2c.c:540 0x006124
    	readI2CRegister(unsigned char)() at main.c:139 0x006072
    	main() at main.c:222 0x00584E
    	_c_int00_noargs() at boot.c:125 0x006166 (the entry point was reached)

    More specifically, it's looping infinitely inside here:

    //Polling RXIFG0 if RXIE is not enabled
    if(!(HWREG16(baseAddress + OFS_UCBxIE) & UCRXIE0)) {
        while(!(HWREG16(baseAddress + OFS_UCBxIFG) & UCRXIFG0));
    }

    If it helps, I have also updated my register read code slightly to check for the stop condition instead of bus busy (as well as switch back to transmit mode at the start of each read register call - is this essential?). It now looks like this:

    uint8_t readI2CRegister(uint8_t registerAddress){
    
        // Switch to transmit mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);
    
        // Send single byte as address
        EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);
    
        // Delay until I2C stop condition is sent
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
        // Switch to read mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_RECEIVE_MODE);
    
        // Receive start
        EUSCI_B_I2C_masterReceiveStart(SENSOR_ADDRESS);
    
        // Read data from register (and then send STOP)
        uint8_t data = EUSCI_B_I2C_masterReceiveSingle(SENSOR_ADDRESS);
    
        // Delay until I2C stop condition is sent
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
        // Return data
        return data;
    }

    Is there something else that's needed for the read operation to work?

  • I didn't notice this before:

    >EUSCI_B_I2C_masterReceiveStart(SENSOR_ADDRESS);
    >uint8_t data = EUSCI_B_I2C_masterReceiveSingle(SENSOR_ADDRESS);

    These need the I2C unit (EUSCI_B0) not the I2C address. Try:

    >EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
    >uint8_t data = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B0_BASE);

    The set of functions you're using manage the UCTR (read/write direction) bit themselves, so you don't actually have to call setMode(). There are other functions which would require calling it.

  • Hmm. Now it seems to get stuck within "EUSCI_B_I2C_masterReceiveStart". More specifically, in this part:

    //Poll for transmit interrupt flag.
    while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

    The call stack is as follows:

    TI MSP430 USB1/MSP430 (Suspended)
    	EUSCI_B_I2C_masterSendSingleByte(unsigned int, unsigned char)() at eusci_b_i2c.c:186 0x005DDE
    	readI2CRegister(unsigned char)() at main.c:135 0x006144
    	...

    Here is the latest read code:

    uint8_t readI2CRegister(uint8_t registerAddress){
        // Send single byte as address
        EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);
    
        // Delay until I2C stop condition is sent
        // while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
        // Receive start
        EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
    
        // Read data from register (and then send STOP)
        uint8_t data = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B0_BASE);
    
        // Return data
        return data;
    }

    I have commented out the stop condition wait call, as that seems to also loop infinitely.

  • Looking through the source, it appears EUSCI_B_I2C_masterReceiveSingle doesn't request a Stop. The counterpart for SendSingleByte (Start/Read-Byte/Stop) would be 

    >  uint8_t data = EUSCI_B_I2C_masterReceiveSingleByte(EUSCI_B0_BASE);

**Attention** This is a public forum