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: I2C transmit interrupt is not triggered?

Part Number: MSP432P401R

Hello! I'm using the Fuel Tank MKII Battery BoosterPack Plug-In Module with a MSP-EXP432P401R Rev. 2.1 Launchpad.

I want to modify the MSP432 Simplelink SDK 1.40.1.00 (boostxl_batpakmkii_fuelgauge_MSP_EXP432P401R_nortos_ccs), to use I2C transmit interrupts in order for the CPU to sleep until the buffer is available for transmission. I've attached my project and I mention that it is a modified version of the example, according to this forum post.

The problem is that TX interrupt is never generated (although it is enabled), and thus my flag (i2cTxFlag) is not set and the program hangs (see I2C_read16() in HAL_I2C.c).

Does anybody know what I am doing wrong? Are I2C TX interrupts generated when the buffer is empty or after a transmission?

boostxl_batpakmkii_fuelgauge_MSP_EXP432P401R_nortos_ccs.zip

  • Christian,

    I will look into your post for you, please allow me some time to look into this.
  • Cristian,

        To the second question the TX interrupt is associated with the loading of the TXBUF into the shift register.  When the eUSCI (communication peripheral name) is released from reset the TX IFG is automatically set.  If you were to load the TX buffer the TX IFG would be cleared for an insignificant amount of time as the value is moved into the shift register and the TX buffer is now empty and another byte can be written.

    Regards,

    Chris 

  • Christian,

    To give you an update, it looks like your code is trying to send an interrupt on EUSCI_B_I2C_TRANSMIT_INTERRUPT1 when i look at what the status variable returns when you are in your IRQ Handler. I'm trying to figure out why exactly it behaves this way. I will give you an update as soon as i know more.
  • Christian,

    I think we've found the issue. To my understanding, inside of your I2C_read16 function, you are checking the TX Flag to be set, before you've sent a start command. The UCTXIFG0 bit is set when the START condition is generated and the first data to be transmitted can be written into UCBxTXBUF.

    In your case, you are never seeing your TX Flag being set because you are not sending a start condition. The driverlib function I2C_masterSendSingleByteWithTimeout handles all that for you. However, it looks like you're trying to move away from that and manually write your own driver?

    If so, you will need to trigger the start command by using the following line

        //Send start condition.
        EUSCI_B_CMSIS(moduleInstance)->CTLW0 |= EUSCI_B_CTLW0_TR
                + EUSCI_B_CTLW0_TXSTT;

    You also have your i2cTxFlag set to true by default. I believe you intended this to be false?


    within your while loop, you also need to set the MAP_Interrupt_enableMaster() before you go to sleep, or else it will never come out of sleep.

        while (i2cTxFlag == false)
        {
            MAP_Interrupt_enableMaster();
            MAP_PCM_gotoLPM0InterruptSafe();
        }

    From there, in order to move away from the I2C driverlib call, you will need to handle the rest of the conditions that I2C expects as well, such as transmitting the pointer byte and stop condition manually after the ISR handles TXIFG0.

    5315.HAL_I2C.c
    /* --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 i2cTxFlag = false; // i2cTxFlag false by default
    volatile bool i2cRxFlag = 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
    
    
        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;
    }
    
    
    /***************************************************************************//**
     * @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;
        while (i2cRxFlag == false)
        {
            MAP_PCM_gotoLPM0InterruptSafe();
            MAP_Interrupt_enableMaster();
        }
        i2cRxFlag = false;
    
        *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,
    
        // Sending start condition triggers the TXIFG0. This was previously handled inside masterSendSingleByteWithTimeout() automatically
        EUSCI_B_CMSIS(EUSCI_B1_BASE)->CTLW0 |= EUSCI_B_CTLW0_TR
                + EUSCI_B_CTLW0_TXSTT;
    
        //    EUSCI_B_I2C_TRANSMIT_INTERRUPT0);
        while (i2cTxFlag == false)
        {
            MAP_Interrupt_enableMaster(); // Enable interrupt before going to sleep
            MAP_PCM_gotoLPM0InterruptSafe();
        }
        i2cTxFlag = false;
    
        // Need to replace masterSendSingleByteWithTimeout() and send the TX pointer byte and stop condition manually
    
        /* 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;
        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_I2C_disableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_STOP_INTERRUPT);
        }
        MAP_Interrupt_disableSleepOnIsrExit();
    }
    

    A good reference would be the source for the I2C_masterSendSingleByteWithTimeout() function see below

    bool I2C_masterSendSingleByteWithTimeout(uint32_t moduleInstance,
            uint8_t txData, uint32_t timeout)
    {
        uint_fast16_t txieStatus;
        uint32_t timeout2 = timeout;
    
        ASSERT(timeout > 0);
    
        //Store current TXIE status
        txieStatus = EUSCI_B_CMSIS(moduleInstance)->IE & EUSCI_B_IE_TXIE0;
    
        //Disable transmit interrupt enable
        BITBAND_PERI(EUSCI_B_CMSIS(moduleInstance)->IE, EUSCI_B_IE_TXIE0_OFS) = 0;
    
        //Send start condition.
        EUSCI_B_CMSIS(moduleInstance)->CTLW0 |= EUSCI_B_CTLW0_TR
                + EUSCI_B_CTLW0_TXSTT;
    
        //Poll for transmit interrupt flag.
        while ((!(EUSCI_B_CMSIS(moduleInstance)->IFG & EUSCI_B_IFG_TXIFG))
                && --timeout)
            ;
    
        //Check if transfer timed out
        if (timeout == 0)
            return false;
    
        //Send single byte data.
        EUSCI_B_CMSIS(moduleInstance)->TXBUF = txData;
    
        //Poll for transmit interrupt flag.
        while ((!BITBAND_PERI(EUSCI_B_CMSIS(moduleInstance)->IFG,
                EUSCI_B_IFG_TXIFG0_OFS)) && --timeout2)
            ;
    
        //Check if transfer timed out
        if (timeout2 == 0)
            return false;
    
        //Send stop condition.
        BITBAND_PERI(EUSCI_B_CMSIS(moduleInstance)->CTLW0, EUSCI_B_CTLW0_TXSTP_OFS) =
                1;
    
        //Clear transmit interrupt flag before enabling interrupt again
        BITBAND_PERI(EUSCI_B_CMSIS(moduleInstance)->IFG, EUSCI_B_IFG_TXIFG0_OFS) =
                0;
    
        //Reinstate transmit interrupt enable
        EUSCI_B_CMSIS(moduleInstance)->IE |= txieStatus;
    
        return true;
    }

    You should also be careful to structure your code so that it follows the sequence defined in Section 24.3.4.2.1 of the MSP432 Technical Reference Manual.

    Attached you can see some of the changes we made to your code.

  • Thank you very much for your help. It is clearer for me now. I wanted to replace polling the I2C transmit flag with TX interrupt in order to optimize energy usage, by allowing the CPU to sleep until it is ready. I understand now that I can use the source code of I2C_masterSendSingleByteWithTimeout() and replace the while loops with loops where the CPU goes to sleep until a flag is cleared.

**Attention** This is a public forum