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: How to read one byte over I2C?

Part Number: MSP432P401R


Hello! I'm trying to read BMA280 accelerometer over I2C using MSP432. This is the sequence I'm trying to achieve in order to read the chip ID:

1. Send start and register address;

2. Send (re)start and read one byte;

3. Send STOP.

The problem is that STOP is not sent after the receiving the first (and only) byte, as I want. Instead, it is sent after receiving a second byte.

This is my code:

eUSCI_I2C_MasterConfig i2cConfig =
{
        EUSCI_B_I2C_CLOCKSOURCE_SMCLK,          // SMCLK Clock Source
#ifdef  USE_CPU_CLOCK_DCO_24MHz
         12000000,                              // SMCLK
#endif
#ifdef USE_CPU_CLOCK_XTAL_48MHz
         24000000,                              // SMCLK
#endif
        EUSCI_B_I2C_SET_DATA_RATE_100KBPS,      // I2C Clock
        0,                                      // No byte counter threshold
        EUSCI_B_I2C_NO_AUTO_STOP                // No Autostop
};

bool I2C_init(uint32_t moduleInstance)
{
 /* Select I2C function for I2C pins */
    switch (moduleInstance)
    {
        case EUSCI_B0_BASE:
            MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
            break;
        case EUSCI_B1_BASE:
            MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P6, GPIO_PIN4 | GPIO_PIN5, GPIO_PRIMARY_MODULE_FUNCTION);
            break;
        case EUSCI_B2_BASE:
            MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
            break;
        case EUSCI_B3_BASE:
            MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P6, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
            break;
    }

    /* Initializing I2C Master */
    MAP_I2C_initMaster(moduleInstance, &i2cConfig);
    /* Enable I2C Module to start operations */
    MAP_I2C_enableModule(moduleInstance);
    MAP_I2C_clearInterruptFlag(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0);
    MAP_I2C_enableInterrupt(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0);
    TimerA2_delayMiliseconds(100);
    return true;
   }

int I2C_sendReadBytes(uint32_t moduleInstance, unsigned char* txBuffer, unsigned char* rxBuffer, int noBytesTx, int noBytesRx, int timeout)
{
    /* Making sure the last transaction has been completely sent out */
    int timeout2 = timeout;
    while (MAP_I2C_masterIsStopSent(moduleInstance) && --timeout2);

    if (timeout2 == 0)
        return -1;

    //MAP_Interrupt_enableSleepOnIsrExit();

    /* Initiate start and send first character */
    if (!MAP_I2C_masterSendMultiByteStartWithTimeout(moduleInstance, txBuffer[0], timeout))
        return -1;

    int i;
    for (i = 1; i < noBytesTx - 1; i++)
    {
        if (!MAP_I2C_masterSendMultiByteNextWithTimeout(moduleInstance, txBuffer[i], timeout))
            return -1;
    }

    if (noBytesTx > 1)
    {
        if (!MAP_I2C_masterSendSingleByteWithTimeout(moduleInstance, txBuffer[noBytesTx - 1], timeout)) // If for is from i..noBytes, it does not transmit the last byte; do not know why
            return -1;
    }

    timeout2 = timeout;
    while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_TRANSMIT_INTERRUPT0)) && --timeout2);
    if (timeout2 == 0)
        return -1;


    MAP_I2C_masterReceiveStart(moduleInstance);


    if (noBytesRx == 1)
    {
        i = 0;
        timeout2 = timeout;
        while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0)) && --timeout2);
        if (timeout2 == 0)
            return -1;

        MAP_I2C_masterReceiveMultiByteStop(moduleInstance);
        rxBuffer[i] = MAP_I2C_masterReceiveMultiByteNext(moduleInstance);
        //rxBuffer[i] = MAP_I2C_masterReceiveSingleByte(moduleInstance); // Does not work
        //rxBuffer[i] = MAP_I2C_masterReceiveMultiByteFinish(moduleInstance); // Hangs
    }
    else
    {
        i = 0;
        for(i = 0 ; i < noBytesRx - 1; i++)
        {
            timeout2 = timeout;
            while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0)) && --timeout2);
            if (timeout2 == 0)
                return -1;

            rxBuffer[i] = MAP_I2C_masterReceiveMultiByteNext(moduleInstance);
        }

        timeout2 = timeout;
        while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0)) && --timeout2);
        if (timeout2 == 0)
            return -1;
        MAP_I2C_masterReceiveMultiByteStop(moduleInstance);

        /*timeout2 = timeout;
        while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0)) && --timeout2);
        if (timeout2 == 0)
            return -1;*/
        rxBuffer[i] = MAP_I2C_masterReceiveMultiByteFinish(moduleInstance);
    }
    return 0;
}

Logic analyzer capture:

As you can see, I need to issue the stop after 0xFB is received. As stated at p. 103 of the accelerometer's datasheet, the master should send NACK + STOP to end the transfer, but this happens at the second byte, not at the first one as I needed.

I've tried the code presented in this topic, but it does not work as expected. This is the logic analyzer capture:

Can anybody help me out with this issue?

  • Section 26.3.4.2.1 of the MSP432 Technical reference manual (SLAU356) has a paragraph that states:

    When transmitting a single byte of data, the UCTXSTP bit must be set while the byte is being transmitted or any time after transmission begins, without writing new data into UCBxTXBUF. Otherwise, only the address is transmitted. When the data is transferred from the buffer to the shift register, UCTXIFG0 is set, indicating data transmission has begun, and the UCTXSTP bit may be set. When UCASTPx = 10 is set, the byte counter is used for STOP generation and the user does not need to set the UCTXSTP. This is recommended when transmitting only one byte.

    The current MSP432 I2C examples do not demonstrate a single-byte receive after restart, so it's difficult to figure this out based on those examples. The code below will write some bytes (i.e. an address to an I2C EEPROM device) and then read back a single byte.  This is a preview- this example will be added to the MSP432 SDK in the next release...

    Regards,

      - Bob L.

    int main(void)
    {
      /* Disabling the Watchdog */
      MAP_WDT_A_holdTimer();

      /* Select Port 1 for I2C - Set Pin 6, 7 to input Primary Module Function,
      * (UCB0SIMO/UCB0SDA, UCB0SOMI/UCB0SCL).
      */
      MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
      GPIO_PIN6 + GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
      MAP_GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN0);
      MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0);

      MAP_GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN1);
      MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN1);

      /* Initializing I2C Master to SMCLK at 100khz with no autostop */
      MAP_I2C_initMaster(EUSCI_B0_BASE, &i2cConfig);

      /* Specify slave address */
      MAP_I2C_setSlaveAddress(EUSCI_B0_BASE, SLAVE_ADDRESS);

      /* Enable I2C Module to start operations */
      MAP_I2C_enableModule(EUSCI_B0_BASE);
      MAP_Interrupt_enableInterrupt(INT_EUSCIB0);

      // enable RX interrupts
      MAP_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0);

      while (1) 
      {
        RXData=0;

        /* Making sure the last transaction has been completely sent out */
        while (MAP_I2C_masterIsStopSent(EUSCI_B0_BASE));

        /* Send out EEPROM Mock Read Cmd (2 databytes) */
        MAP_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, TXData[1]); // Start + 1Byte
        MAP_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData[0]); // Poll for TXINT,Send 1Byte
        /*---------------------------------------------*/
        /* Now we need to initiate the read */
        /* Wait until 2nd Byte has been output to shift register */
        while(!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG0));

        // Send the restart condition, read one byte, send the stop condition right away
        EUSCI_B0->CTLW0 &= ~(EUSCI_B_CTLW0_TR);
        EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTT;
        while(MAP_I2C_masterIsStartSent(EUSCI_B0_BASE));
        EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTP;

        //---------------------------------
        MAP_PCM_gotoLPM0InterruptSafe();

        // Slave should send a single 'R' bac
        if(RXData != 'R'){
          // Error- set P1.0 high
          MAP_GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN0);
          while(1);
        }
        __delay_cycles(300000); // ~100ms pause between transmissions
      }
    }

    /*******************************************************************************
    * eUSCIB0 ISR. The repeated start and transmit/receive operations happen
    * within this ISR.
    *******************************************************************************/
    void EUSCIB0_IRQHandler(void)
    {
      uint_fast16_t status;

      status = MAP_I2C_getEnabledInterruptStatus(EUSCI_B0_BASE);
      MAP_I2C_clearInterruptFlag(EUSCI_B0_BASE, status);

      /* Receives bytes into the receive buffer. If we have received all bytes,
      * send a STOP condition */
      if (status & EUSCI_B_I2C_RECEIVE_INTERRUPT0)
      {
        // One-byte Read
        RXData = MAP_I2C_masterReceiveSingle(EUSCI_B0_BASE);
        MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN1);
      }
    }

     

  • Thank you, it worked! This is the code modified to work in polling mode, if it is helps anybody:

    int I2C_sendReadBytes(uint32_t moduleInstance, unsigned char* txBuffer, unsigned char* rxBuffer, int noBytesTx, int noBytesRx, int timeout)
    {
        /* Making sure the last transaction has been completely sent out */
        int timeout2 = timeout;
        while (MAP_I2C_masterIsStopSent(moduleInstance) && --timeout2);
    
        if (timeout2 == 0)
            return -1;
    
        //MAP_Interrupt_enableSleepOnIsrExit();
    
        /* Initiate start and send first character */
        if (!MAP_I2C_masterSendMultiByteStartWithTimeout(moduleInstance, txBuffer[0], timeout))
            return -1;
    
        int i;
        for (i = 1; i < noBytesTx - 1; i++)
        {
            if (!MAP_I2C_masterSendMultiByteNextWithTimeout(moduleInstance, txBuffer[i], timeout))
                return -1;
        }
    
        if (noBytesTx > 1)
        {
            if (!MAP_I2C_masterSendSingleByteWithTimeout(moduleInstance, txBuffer[noBytesTx - 1], timeout))
    return -1; } /* Wait until the last byte has been output to shift register */ timeout2 = timeout; while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_TRANSMIT_INTERRUPT0)) && --timeout2); if (timeout2 == 0) return -1; // Send the restart condition, read bytes, send the stop condition right away EUSCI_B0->CTLW0 &= ~(EUSCI_B_CTLW0_TR); EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTT; timeout2 = timeout; while(MAP_I2C_masterIsStartSent(EUSCI_B0_BASE) && --timeout2); if (timeout2 == 0) return -1; for(i = 0 ; i < noBytesRx; i++) { if (i == noBytesRx - 1) // Last byte reached EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTP; // Send stop timeout2 = timeout; while(!(MAP_I2C_getInterruptStatus(moduleInstance, EUSCI_B_I2C_RECEIVE_INTERRUPT0)) && --timeout2); if (timeout2 == 0) return -1; rxBuffer[i] = MAP_I2C_masterReceiveSingle(moduleInstance); } return 0; }

  • Glad I could help. PS- There is a Polling version of my example that's also slated for release in the next SDK, so both methods will be available.
    -Bob L.

**Attention** This is a public forum