MSP432P401R: I2C - Receiving 2 Bytes when I am supposed to be receiving 1 - Using Driverlib masterReceiveSingleByte

Part Number: MSP432P401R

I am trying to communicate with a BQ27441 Battery Fuel Guage. My I2C communication is working fine for the most part, but there is a part in the code where I need to receive a single byte checksum (anything more and errors occur). When I use the driverlib function to receive a single byte, I get 2 bytes!

I have attached the driverlib setup code, the problem code, and my interrupt code.

I also included the logic analyzer output of what is happening on the I2C lines. As you can see below, I write 0x60 for the register, then I try and read a single byte and I get back 0x00+ACK AND 0x00+NACK. 

Setup:

  • I2C Pins 6.6 & 6.7
  • 1K pullup
  • 100kbps
  • Only the BQ27441 is on the bus
  • The code was originally from a driver lib example, but has been modified heavily to suit our needs

Problem code:

    //send block checksum command to check the checksum
    EUSCI_TXBuffer &= 0x00;                                                        // Clear Transmit Buffer
    MAP_I2C_masterSendSingleByte(EUSCI_module, BQ27441_EXTENDED_CHECKSUM);

    NUM_OF_REC_BYTES = 1;
    MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_RECEIVE_MODE);                       // Set to Receive mode
    MAP_I2C_enableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);         // Enable Receive Interrupt
    uint8_t OLD_Csum = MAP_I2C_masterReceiveSingleByte(EUSCI_module);
    rxFlag = 0;
    MAP_I2C_disableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);     // Disable receive interrupt
    
    
    MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_TRANSMIT_MODE);                   // Set I2C mode to transmit
    xferIndex = 0;                                                              // Clear the RXData index

Setup code:

void FuelGauge_Setup()
{
    // Select Port 6 for I2C - Set Pin 4, 5 to input Primary Module Function, (UCB1SIMO/UCB1SDA, UCB1SOMI/UCB1SCL).
#if SDApin == GPIO_PIN6 && I2Cport == GPIO_PORT_P6
    MAP_GPIO_setAsPeripheralModuleFunctionInputPin(I2Cport, SDApin + SCLpin, GPIO_SECONDARY_MODULE_FUNCTION);
#else
    MAP_GPIO_setAsPeripheralModuleFunctionInputPin(I2Cport, SDApin + SCLpin, GPIO_PRIMARY_MODULE_FUNCTION);
#endif
    // Initializing I2C Master to SMCLK at 100kbs with no auto-stop
    MAP_I2C_initMaster(EUSCI_module, &i2cConfig);

    // Specify slave address
    MAP_I2C_setSlaveAddress(EUSCI_module, BQ27441_I2C_ADDRESS);

    // Set Master in transmit mode
    MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_TRANSMIT_MODE);

    // Enable I2C Module to start operations
    MAP_I2C_enableModule(EUSCI_module);

    // Enable and clear the interrupt flags todo make transmit interrupts in the define page
    MAP_I2C_clearInterruptFlag(EUSCI_module, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_RECEIVE_INTERRUPT0);

    //Enable master Receive interrupt
    MAP_Interrupt_enableInterrupt(EUSCI_moduleINT);
}

Interrupt code:

void EUSCIB3_Receive(void)
{
  uint_fast16_t status;                                                                   // Flag status

  status = MAP_I2C_getEnabledInterruptStatus(EUSCI_module);                               // Grab any flags that are triggered
  MAP_I2C_clearInterruptFlag(EUSCI_module, status);                                       // Clear interrupt flags that are triggered

    if (status & EUSCI_B_I2C_RECEIVE_INTERRUPT0)                                            // If the interrupt flag for receive has been triggered
    {
      if(NUM_OF_REC_BYTES > 1)                                                            // If number of receive bytes has been set to a value over 1
      {
          if(xferIndex == NUM_OF_REC_BYTES - 2)                                           // When xferIndex is on the third to last byte
          {
              MAP_I2C_masterReceiveMultiByteStop(EUSCI_module);
              RXData[xferIndex++] = MAP_I2C_masterReceiveMultiByteNext(EUSCI_module);     // Receive and store byte into RXData
          }
          else if(xferIndex == NUM_OF_REC_BYTES - 1)                                      // When xferIndex is on the second to last byte
          {
              RXData[xferIndex++] = MAP_I2C_masterReceiveMultiByteNext(EUSCI_module);     // Receive and store byte into RXData
              MAP_I2C_disableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);     // Disable receive interrupt
              MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_TRANSMIT_MODE);                   // Set I2C mode to transmit
              xferIndex = 0;                                                              // Clear the RXData index
              rxFlag = 1;                                                                 // Set receive interrupt flag high
              NUM_OF_REC_BYTES = 0;                                                       // Clear NUM_OF_REC_BYTES
          }
          else                                                                            // Executed until xferIndex is within 2 from the length of NUM_OF_REC_BYTES
          {
              RXData[xferIndex++] = MAP_I2C_masterReceiveMultiByteNext(EUSCI_module);
          }
      }else{
            rxFlag = 1;                                                                 // Set receive interrupt flag high
            NUM_OF_REC_BYTES = 0;                                                       // Clear NUM_OF_REC_BYTES
      }
    }


}

14 Replies

  • I2C_masterReceiveSingleByte() does not work with interrupts.

    There is no driverlib function to receive a single byte with interrupts. In theory, you could call I2C_masterReceiveStart() and then I2C_masterReceiveMultiByteStop() immediately afterwards; the interrupt handler should then use I2C_masterReceiveSingle().

    But it might be easier to keep using I2C_masterReceiveSingleByte() and just not enable the interrupt.
  • In reply to Clemens Ladisch:

    Clemens, thank you for the reply. I have tried both the following with the same results (2 bytes received). What am I doing wrong?

    //send block checksum command to check the checksum
        EUSCI_TXBuffer &= 0x00;                                                        // Clear Transmit Buffer
        MAP_I2C_masterSendSingleByte(EUSCI_module, BQ27441_EXTENDED_CHECKSUM);
        MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_RECEIVE_MODE);                       // Set to Receive mode
        MAP_I2C_disableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);         // disable Receive Interrupt
        uint8_t OLD_Csum = MAP_I2C_masterReceiveSingleByte(EUSCI_module);

        //send block checksum command to check the checksum
        EUSCI_TXBuffer &= 0x00;
        MAP_I2C_masterSendSingleByte(EUSCI_module,0x60);
    
        NUM_OF_REC_BYTES = 1;
        MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_RECEIVE_MODE);                       // Set to Receive mode
        MAP_I2C_enableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);         // Enable Receive Interrupt
        MAP_I2C_masterReceiveStart(EUSCI_module);                                      // Start Receiving
        MAP_I2C_masterReceiveMultiByteStop(EUSCI_module);
        xferIndex = 0;                                                                 // Set RXData index to 0
        while(rxFlag != 1);                                                            // Wait until receive interrupt flag is received
        rxFlag = 0;
        uint8_t OLD_Csum = RXData[0];

    The interrupt code for the 2nd trial looks like this:

    void EUSCIB3_Receive(void)
    {
      uint_fast16_t status;                                                                   // Flag status
    
      status = MAP_I2C_getEnabledInterruptStatus(EUSCI_module);                               // Grab any flags that are triggered
      MAP_I2C_clearInterruptFlag(EUSCI_module, status);                                       // Clear interrupt flags that are triggered
    
        if (status & EUSCI_B_I2C_RECEIVE_INTERRUPT0)                                            // If the interrupt flag for receive has been triggered
        {
          if(NUM_OF_REC_BYTES > 1)                                                            // If number of receive bytes has been set to a value over 1
          {
              if(xferIndex == NUM_OF_REC_BYTES - 2)                                           // When xferIndex is on the third to last byte
              {
                  MAP_I2C_masterReceiveMultiByteStop(EUSCI_module);
                  RXData[xferIndex++] = MAP_I2C_masterReceiveMultiByteNext(EUSCI_module);     // Receive and store byte into RXData
              }
              else if(xferIndex == NUM_OF_REC_BYTES - 1)                                      // When xferIndex is on the second to last byte
              {
                  RXData[xferIndex++] = MAP_I2C_masterReceiveMultiByteNext(EUSCI_module);     // Receive and store byte into RXData
                  MAP_I2C_disableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);     // Disable receive interrupt
                  MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_TRANSMIT_MODE);                   // Set I2C mode to transmit
                  xferIndex = 0;                                                              // Clear the RXData index
                  rxFlag = 1;                                                                 // Set receive interrupt flag high
                  NUM_OF_REC_BYTES = 0;                                                       // Clear NUM_OF_REC_BYTES
              }
              else                                                                            // Executed until xferIndex is within 2 from the length of NUM_OF_REC_BYTES
              {
                  RXData[xferIndex++] = MAP_I2C_masterReceiveMultiByteNext(EUSCI_module);
              }
          }else{
              RXData[0] =  EUSCI_B3->RXBUF;
              rxFlag = 1;                                                                 // Set receive interrupt flag high
              NUM_OF_REC_BYTES = 0;                                                       // Clear NUM_OF_REC_BYTES
          }
        }
    
    
    }
    

  • In reply to Reid Kersey:

    EUSCI_TXBuffer &= 0x00;                         // Clear Transmit Buffer

    This does not clear anything; any write access to the TX buffer sends a byte.

  • In reply to Clemens Ladisch:

    Clemens, thanks for working with me on this.

    If I do not write that line the masterSendSingleByte (and sometimes multibyte send) will send 0xFF.

    The highlighted part (green and yellow) corresponds to where I try to send 0x60 (BQ27441_EXTENDED_CHECKSUM) and read it.

        //Set the datablock to be accessed
        EUSCI_TXBuffer &= 0x00;                                                        // this block only works if EISCI+TXBuffer is & with 0
        MAP_I2C_masterSendMultiByteStart(EUSCI_module, BQ27441_EXTENDED_DATABLOCK);
        MAP_I2C_masterSendMultiByteNext(EUSCI_module, 0x00);
        MAP_I2C_masterSendMultiByteStop(EUSCI_module);
    
    
    
        //send block checksum command to check the checksum
        //EUSCI_TXBuffer &= 0x00;                                                        // Clear Transmit Buffer
        MAP_I2C_masterSendSingleByte(EUSCI_module, BQ27441_EXTENDED_CHECKSUM);
        MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_RECEIVE_MODE);                       // Set to Receive mode
        MAP_I2C_disableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);         // disable Receive Interrupt
        uint8_t OLD_Csum = MAP_I2C_masterReceiveSingleByte(EUSCI_module);

  • In reply to Reid Kersey:

    It is impossible to see whether there is a correct start condition; please zoom in more, and add a wait before the transaction.
  • In reply to Clemens Ladisch:

    Here is the part of code (included below) with the delay before and after. The SDA and SCL Lines Stay High for 1s then do this (Figure 2) before the next bit of code. I have also included the Saleae logic capture.

    Figure 2

        delay(8000000);
        //send block checksum command to check the checksum
        //EUSCI_TXBuffer &= 0x00;                                                        // Clear Transmit Buffer
        MAP_I2C_masterSendSingleByte(EUSCI_module, BQ27441_EXTENDED_CHECKSUM);
        MAP_I2C_setMode(EUSCI_module, EUSCI_B_I2C_RECEIVE_MODE);                       // Set to Receive mode
        MAP_I2C_disableInterrupt(EUSCI_module, EUSCI_B_I2C_RECEIVE_INTERRUPT0);         // disable Receive Interrupt
        uint8_t OLD_Csum = MAP_I2C_masterReceiveSingleByte(EUSCI_module);
        delay(8000000);

    Saleae Logic Capture.zip

  • In reply to Reid Kersey:

    The images show two writes.

    This is strange. To rule out the various USCI errata, can you try using (a clock synchronous with) MCLK as clock source?
  • In reply to Clemens Ladisch:

    How do I do that? I am not very good with figuring out clocks...

    This is the config I have currently:

    // I2C Master Configuration Parameter
    const eUSCI_I2C_MasterConfig i2cConfig =
    {
            EUSCI_B_I2C_CLOCKSOURCE_SMCLK,          // SMCLK Clock Source
            8000000,                                // SMCLK = 8MHz
            EUSCI_B_I2C_SET_DATA_RATE_100KBPS,      // Desired I2C Clock of 100khz
            0,                                      // No byte counter threshold
            EUSCI_B_I2C_NO_AUTO_STOP                // No Auto-stop
    };

  • In reply to Reid Kersey:

    How did you configure SMCLK?
  • In reply to Clemens Ladisch:

    This is how we have our clocks setup

        // Configure clocks
        MAP_CS_setDCOFrequency(CLK_FQ);    // Setting DCO (clock) to the specified clock speed
    
        MAP_CS_initClockSignal(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);  	// Tie SMCLK to DCO
        MAP_CS_initClockSignal (CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1);		// Tie ACLK to VLO
    	#define CLK_FQ							8000000							///< Clock frequency @note YOU MUST CHANGE UART BAUD RATE SCALERS MANUALLY!