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.

MSP432P401R: Removing while loop makes I2C read different values

Part Number: MSP432P401R


 

Hi! I'm trying to use the Fuel Tank MKII Battery BoosterPack Plug-In Module with a MSP-EXP432P401R Rev. 2.0 Launchpad. I'm using the example provided in MSP432 Simplelink SDK 1.40.00.28 (boostxl_batpakmkii_fuelgauge_MSP_EXP432P401R_nortos_ccs), and I want to optimize it by getting the CPU to sleep instead of waiting for received bytes in a while loop. 

The problem is that when using my code it reads a different response from BQ27441 (it reads 0xB000 when it should be reading 0x0108). The code I'm using:

volatile bool i2cRxFlag = false;

void I2C_init(void)
{
        /* Initialize USCI_B0 and I2C Master to communicate with slave devices*/
    I2C_initMaster(EUSCI_B1_BASE, &i2cConfig);

    /* Disable I2C module to make changes */
    I2C_disableModule(EUSCI_B1_BASE);

    /* Enable I2C Module to start operations */
    I2C_enableModule(EUSCI_B1_BASE);

    MAP_I2C_clearInterruptFlag(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
    MAP_I2C_enableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
    MAP_Interrupt_enableInterrupt(INT_EUSCIB1);

    return;
}

void EUSCIB1_IRQHandler(void)
{
    unsigned int status;
    status = MAP_I2C_getEnabledInterruptStatus(EUSCI_B1_BASE);
    MAP_I2C_clearInterruptFlag(EUSCI_B1_BASE, status);

    if (status & EUSCI_B_I2C_TRANSMIT_INTERRUPT0)
    {
        i2cTxFlag = true;
    }
    if (status & EUSCI_B_I2C_STOP_INTERRUPT)
    {
        i2cStopFlag = true;
    }
    if (status & EUSCI_B_I2C_RECEIVE_INTERRUPT0)
    {
        i2cRxFlag = true;
    }
    MAP_Interrupt_disableSleepOnIsrExit();
}

bool I2C_read16(unsigned char pointer, short * result, unsigned int timeout)
{
    uint8_t val = 0;
    uint8_t valScratch = 0;
    short r = 0;

    /* Set master to transmit mode PL */
    I2C_setMode(EUSCI_B1_BASE,
        EUSCI_B_I2C_TRANSMIT_MODE);

    /* Clear any existing interrupt flag PL */
    I2C_clearInterruptFlag(EUSCI_B1_BASE,
        EUSCI_B_I2C_TRANSMIT_INTERRUPT0);

    /* Initiate start and send first character */
    if (!I2C_masterSendSingleByteWithTimeout(EUSCI_B1_BASE, pointer, timeout))
    	return 0;

    /*
     * Generate Start condition and set it to receive mode.
     * This sends out the slave address and continues to read
     * until you issue a STOP
     */
    I2C_masterReceiveStart(EUSCI_B1_BASE);

    /* Wait for RX buffer to fill */
    //while(!(I2C_getInterruptStatus(EUSCI_B1_BASE,
    //    EUSCI_B_I2C_RECEIVE_INTERRUPT0)));
    if (!(MAP_I2C_getInterruptStatus(EUSCI_B1_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0)))
    {
        while (i2cRxFlag == false)
        {
            MAP_PCM_gotoLPM0InterruptSafe();
            MAP_Interrupt_enableMaster();
        }
        i2cRxFlag = false;
    }

    /* Read from I2C RX register */
    valScratch = I2C_masterReceiveMultiByteNext(EUSCI_B1_BASE);

    /* Receive second byte then send STOP condition */
    if (!I2C_masterReceiveMultiByteFinishWithTimeout(EUSCI_B1_BASE, &val, timeout))
    	return 0;

    /* Shift val to top MSB */
    r = (val << 8);

    /* Read from I2C RX Register and write to LSB of r */
    r |= valScratch;

    /* Return temperature value */
    *result = r;

    return 1;
}

As you can see, I only added a few lines of code. After waiting for the RX buffer to fill, if I use the original while() loop (which is commented above) it reads 0x0108, but if I use my code (the if statement above), it reads 0xB000.

What could be the problem?

  • Cristian,
    I do not see an obvious reason why the data would be corrupted and will need to try and recreate on my side. Looking at the code snippet that you have provided, I do not think that you need the if statement on lines 70,71,and 78. The while loop on i2cRxFlag will not proceed until the ISR has set the flag to true. I am concerned that the status check of the receive interrupt may be redundant since you check it again in the ISR. I will try to have a more definitive answer tomorrow.

    Regards,
    Chris
  • Hi, Chris! Yes, you are right. The if statement is redundant, however it should not influence the program. I will remove it. Please test the code and let me know what you will find out. Thank you!

  • /* --COPYRIGHT--,BSD
     * Copyright (c) 2015, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    //****************************************************************************
    //
    // HAL_I2C.c - Hardware abstraction layer for I2C with MSP432P401R
    //
    //****************************************************************************
    
    #include <ti/devices/msp432p4xx/driverlib/driverlib.h>
    #include <HAL_I2C.h>
    
    volatile bool i2cRxFlag = false;
    volatile bool i2cTxFlag = false;
    volatile bool i2cStopFlag = false;
    
    /* I2C Master Configuration Parameter */
    const eUSCI_I2C_MasterConfig i2cConfig =
    {
            EUSCI_B_I2C_CLOCKSOURCE_SMCLK,          // SMCLK Clock Source
    		48000000,                                // SMCLK = 3MHz
            EUSCI_B_I2C_SET_DATA_RATE_400KBPS,      // Desired I2C Clock of 400khz
            0,                                      // No byte counter threshold
            EUSCI_B_I2C_NO_AUTO_STOP                // No Autostop
    };
    
    void I2C_initGPIO()
    {
        /* Select I2C function for I2C_SCL(P6.5) & I2C_SDA(P6.4) */
        GPIO_setAsPeripheralModuleFunctionOutputPin(
                GPIO_PORT_P6,
                GPIO_PIN5,
    			GPIO_PRIMARY_MODULE_FUNCTION);
    
        GPIO_setAsPeripheralModuleFunctionOutputPin(
                GPIO_PORT_P6,
                GPIO_PIN4,
    			GPIO_PRIMARY_MODULE_FUNCTION);
    }
    
    
    /***************************************************************************//**
     * @brief  Configures I2C
     * @param  none
     * @return none
     ******************************************************************************/
    
    void I2C_init(void)
    {
            /* Initialize USCI_B0 and I2C Master to communicate with slave devices*/
        I2C_initMaster(EUSCI_B1_BASE, &i2cConfig);
    
        /* Disable I2C module to make changes */
        //I2C_disableModule(EUSCI_B1_BASE);   // CTS this is not needed because the previous api puts eUSCI in reset
    
        /* Enable I2C Module to start operations */
        I2C_enableModule(EUSCI_B1_BASE);      // CTS clear reset of eUSCI, tx ifg automatically set
    
    
        /*
         * ADDED BY CUSTOMER
         */
        MAP_I2C_clearInterruptFlag(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
    //    MAP_I2C_enableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
        MAP_I2C_enableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
    
        MAP_Interrupt_enableInterrupt(INT_EUSCIB1);
    
        return;
    }
    
    
    /***************************************************************************//**
     * @brief  Writes data to the sensor
     * @param  pointer  Address of register you want to modify
     * @param  writeByte Data to be written to the specified register
     * @return none
     ******************************************************************************/
    
    bool I2C_write8 (unsigned char pointer, unsigned char writeByte, unsigned int timeout)
    {
        /* Set master to transmit mode PL */
    	I2C_setMode(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_MODE);
    
        /* Clear any existing interrupt flag PL */
    	I2C_clearInterruptFlag(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_INTERRUPT0);
    
        /* Initiate start and send first character */
        if (!I2C_masterSendMultiByteStartWithTimeout(EUSCI_B1_BASE,
            pointer, timeout))
        	return 0;
    
        if (!I2C_masterSendMultiByteFinishWithTimeout(EUSCI_B1_BASE,
            writeByte, timeout))
        	return 0;
    
        return 1;
    }
    
    
    /***************************************************************************//**
     * @brief  Writes data to the sensor
     * @param  pointer  Address of register you want to modify
     * @param  writeWord Data to be written to the specified register
     * @return none
     ******************************************************************************/
    
    bool I2C_write16 (unsigned char pointer, unsigned short writeWord, unsigned int timeout)
    {
        /* Set master to transmit mode PL */
    	I2C_setMode(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_MODE);
    
        /* Clear any existing interrupt flag PL */
    	I2C_clearInterruptFlag(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_INTERRUPT0);
    
        /*
         * Initiate start and send first character
         */
        if (!I2C_masterSendMultiByteStartWithTimeout(EUSCI_B1_BASE,
            pointer, timeout))
        	return 0;
    
        /* Send the MSB of writeByte to SENSOR */
        if (!I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE,
            (unsigned char)(writeWord&0xFF), timeout))
        	return 0;
    
        if (!I2C_masterSendMultiByteFinishWithTimeout(EUSCI_B1_BASE,
            (unsigned char)(writeWord>>8), timeout))
        	return 0;
    
        return 1;
    }
    
    
    /***************************************************************************//**
     * @brief  Reads data from the sensor
     * @param  pointer Address of register to read from
     * @return Register contents
     ******************************************************************************/
    
    bool I2C_read8(unsigned char pointer, char * result, unsigned int timeout)
    {
        /* Set master to transmit mode PL */
    	I2C_setMode(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_MODE);
    
        /* Clear any existing interrupt flag PL */
    	I2C_clearInterruptFlag(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_INTERRUPT0);
    
        /* Initiate start and send first character */
        if (!I2C_masterSendSingleByteWithTimeout(EUSCI_B1_BASE,
            pointer, timeout))
        	return 0;
    
        /*
         * Generate Start condition and set it to receive mode.
         * This sends out the slave address and continues to read
         * until you issue a STOP
         */
    //    I2C_masterReceiveStart(EUSCI_B1_BASE);
    //
    //    /* Read from I2C RX register */
    //    if(!I2C_masterReceiveMultiByteFinishWithTimeout(EUSCI_B1_BASE, &val, timeout))
    //    	return 0;
    //
    //    /* Return temperature value */
    //    *result = val;
    
        *result = I2C_masterReceiveSingleByte(EUSCI_B1_BASE);
    
        return 1;
    }
    
    
    /***************************************************************************//**
     * @brief  Reads data from the sensor
     * @param  pointer Address of register to read from
     * @return Register contents
     ******************************************************************************/
    
    /*
     * CTS
     * Original Flow
     *
     * SetMode
     * Clear Interrupt Status
     * Send Single Byte with Timeout
     * Wait for RXIFG
     * Receive multi-byte next
     * Recieve multi-byte finish with timeout
     *
     */
    
    bool I2C_read16(unsigned char pointer, short * result, unsigned int timeout)
    {
        uint8_t val = 0;
        uint8_t valScratch = 0;
        short r = 0;
    
        /* Set master to transmit mode PL */
        I2C_setMode(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_MODE);
    
        /* Clear any existing interrupt flag PL */
        I2C_clearInterruptFlag(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_INTERRUPT0);
    
        /* Initiate start and send first character */
        if (!I2C_masterSendSingleByteWithTimeout(EUSCI_B1_BASE, pointer, timeout))
            return 0;
    
        /*
         * Generate Start condition and set it to receive mode.
         * This sends out the slave address and continues to read
         * until you issue a STOP
         */
        I2C_masterReceiveStart(EUSCI_B1_BASE);
    
        /*
         * Original mechanism to wait for RX event
         * Wait for RX buffer to fill
         */
    //    while(!(I2C_getInterruptStatus(EUSCI_B1_BASE,
    //        EUSCI_B_I2C_RECEIVE_INTERRUPT0)));
        /*
         * exchange while loop for lpm0
         */
    //    if (!(MAP_I2C_getInterruptStatus(EUSCI_B1_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0)))
    //    {
            while (i2cRxFlag == false)
            {
                MAP_PCM_gotoLPM0InterruptSafe();
                MAP_Interrupt_enableMaster();
            }
            i2cRxFlag = false;
    //    }
        /* Read from I2C RX register */
        valScratch = MAP_I2C_masterReceiveMultiByteNext(EUSCI_B1_BASE);
    
        /* Receive second byte then send STOP condition */
        if (!MAP_I2C_masterReceiveMultiByteFinishWithTimeout(EUSCI_B1_BASE, &val, timeout))
        	return 0;
    
        /* Shift val to top MSB */
        r = (val << 8);
    
        /* Read from I2C RX Register and write to LSB of r */
        r |= valScratch;
    
        /* Return temperature value */
        *result = r;
    
        return 1;
    }
    
    
    void I2C_setslave(unsigned short slaveAdr)
    {
        /* Specify slave address for I2C */
    	EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE,
            slaveAdr);
    
        /* Enable and clear the interrupt flag */
    	EUSCI_B_I2C_clearInterruptFlag(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0);
        return;
    }
    
    /*
     * ADDED BY CUSTOMER
     */
    void EUSCIB1_IRQHandler(void)
    {
        unsigned int status;
    
        MAP_Interrupt_disableSleepOnIsrExit();
        status = MAP_I2C_getEnabledInterruptStatus(EUSCI_B1_BASE);
        MAP_I2C_clearInterruptFlag(EUSCI_B1_BASE, status);
    
        if (status & EUSCI_B_I2C_TRANSMIT_INTERRUPT0)
        {
            i2cTxFlag = true;
        }
        if (status & EUSCI_B_I2C_STOP_INTERRUPT)
        {
            i2cStopFlag = true;
        }
        if (status & EUSCI_B_I2C_RECEIVE_INTERRUPT0)
        {
    //        if(i2cRxFlag == true)
    //        {
    //            while(1);
    //        }
            MAP_I2C_disableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
            i2cRxFlag = true;
        }
    }
    

    Cristian,

       I believe the issue is with the use of the I2C_masterReceiveMultiByteFinishWithTimeout API.  Since you have enabled the interrupt for the stop condition  the polling function within the API that is checking for the stop IFG does not see that the flag is set before the ISR clears it.  

       I have made some changes to the code and this appears to be working.  Can you please confirm?

    Chris

  • I've tested the code, but it does not work as expected. It does read the correct values, but the problem is that the program sometimes hangs in I2C_masterReceiveSingleByte(), in while (!BITBAND_PERI(EUSCI_B_CMSIS(moduleInstance)->IFG, EUSCI_B_IFG_RXIFG_OFS));, and sometimes the I2C_masterSendSingleByteWithTimeout() timeouts. It's interesting that every time I run the program it hangs in a different function, e.g.:
    /* Read Block Data Checksum */
    if (!BQ27441_readChecksum(&old_chksum, 1000))
    or
    /* Read old design capacity */
    if (!BQ27441_read16(0x4A, &old_designCapacity, 1000))
    return 0;

    I also took care to power cycle the battery booster pack module before each run of the program. Did you test the entire initialization sequence or just the BQ27441_read16() function?
  • Cristian,
    I simply ran the code with the above modifications and verified both the operation in the terminal window and the I2C bus communication. I am going to ask a colleague to confirm.

    Regards,
    Chris
  • I've tested the code again today. Now it only hangs in:

    uint8_t I2C_masterReceiveSingleByte(uint32_t moduleInstance)

    //Poll for receive interrupt flag.
    while (!BITBAND_PERI(EUSCI_B_CMSIS(moduleInstance)->IFG,
    EUSCI_B_IFG_RXIFG_OFS))
    ;

    If I use the original code it works. I hope we are using the same SDK (I'm using the boostxl_batpakmkii_fuelgauge demo from SimpleLink MSP432 SDK - v1.40.00.28).
  • I've verified the code on a Rev. 2.1 board, but it behaves the same, unfortunately.

  • I've tested the code again and now it appears to work fine. I don't know what was the problem then.

**Attention** This is a public forum