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.

MSP430FR5730: I2C issue

Part Number: MSP430FR5730

Hi,

I'm trying to implement an I2C driver as following for a MMA8452 (accelerometer)

I wrote following driver. However, I got some very strange output from the uC. When I tried to retrieve a single byte data from the bus, it always tried to get two bytes. And the I2C bus got locked up after the uC sent out clocks for the second byte.  The only way to get out of the lockup is to power cycle the board. Why did the uC try to get the second byte? I even specifically used  EUSCI_B_I2C_masterReceiveSingleByte, but the uC still tried to get the second byte.

uint8_t I2C_read_byte(uint8_t slave_address, uint8_t reg_address, uint8_t len, uint8_t *data){
uint8_t i;
uint16_t retry=0x1000;

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

EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE,slave_address);

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

EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE,EUSCI_B_I2C_TRANSMIT_INTERRUPT0);

/* Send the register address */
EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE,reg_address);

//Poll for transmit interrupt flag.
while(!(HWREG16(EUSCI_B0_BASE + OFS_UCBxIFG) & UCTXIFG) && retry)
{
retry--;
}

if(!retry) {

return I2C_TIME_OUT;
}


/* Set eUSCI_B0 in receive mode */
EUSCI_B_I2C_setMode(EUSCI_B0_BASE,EUSCI_B_I2C_RECEIVE_MODE);

data[0]=EUSCI_B_I2C_masterReceiveSingleByte(EUSCI_B0_BASE);

/*if(len==1){
data[0]=EUSCI_B_I2C_masterReceiveSingleByte(EUSCI_B0_BASE);
}
else
{
EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
for(i=0;i<len-1;i++)
data[i] = EUSCI_B_I2C_masterReceiveMultiByteNext(EUSCI_B0_BASE);
data[len-1]=EUSCI_B_I2C_masterReceiveMultiByteFinish(EUSCI_B0_BASE);
}*/

return 0;
}

  • You do not need to call EUSCI_B_I2C_setMode(); the Send/Receive functions automatically set this.

    Why are you calling EUSCI_B_I2C_clearInterrupt()? Are you actually using interrupts, or not?

    Why are you polling the TXIFG flag?
  • I saw all those sample codes use EUSCI_B_I2C_setmode to change the mode before writing/reading.

    I did enable the interrupt on the I2C.

    The reason that I poll TXIFG is that EUSCI_B_I2C_masterSendMultiByteStart does not wait the transmit to be done before return. I have to make sure that the writing data is done before reading.

    However, I don't think all of those are related to the the uC reading two bytes when it is told specific to read one byte. Did anyone else see this issue before?

    Regards

    Peng

  • All those sample codes are wrong (well, they contain superfluous code).

    If you are using interrupts, show the interrupt handler, and which ones are enabled. And you then cannot poll the interrupt flags because the handler gets called before the main program sees it.

    Why do you think that you need to wait for the transmit being done?

    I'm not sure why there is a second byte; in theory, the EUSCI_B_I2C_masterReceiveSingleByte() sets the UCTXSTP flag, which should result in a NACK. It's possible that the interrupt handler does something.
  • Looks like my last reply was lost.

    I did couple more tests. I found that if an I2C write is sent without stop bit right before the I2C read. It will trigger the 2 byte read issue. And the stop bit in the I2C read is lost, therefore the I2C bus is locked up. Another stop bit has to be sent separately so that the I2C bus will not be locked up.

    Nothing is in the interrupt handler yet. I disabled all interrupt, and still got the same issue.

    If I don't wait the transmit to being done and call the receiving function, it will cause the I2C controller to switch mode during the transmit. The transmit will be messed up. I captured that case already. If there's no wait, the transmit sends out two 0xFF right after sending out the slave address. You do need to wait for that to switch mode.

    I ended up re-write the driver without calling any of the function. Basically I copied all the codes in the functions, but separated the start bit and the stop bit in the end. I still get the extra one byte read. But the bus does not get locked up.
  • All right, I figured out  everything.  Sharing here in case others have the same issue to implement the following I2C transaction for devices, such as PMBus device or this accelerometer sensor (MMA8452).

    The root cause is that the I2C master module in the MSP430 micro controller always expects a start bit or a stop bit right after transmit a byte. Even it is switched from transmit mode to receive mode.  And the I2C module treat the stop bit at a higher priority than start bit. (I personally think this is a hardware bug.  And I did not see this issue with the same driver in the Tiva processor)

    So when I switched from transmit to receive, the EUSCI_B_I2C_masterReceiveSingleByte sets the start bit and the stop bit at the same time. The I2C module sends out the stop bit first (captured on the oscilloscope). Then it sends out the start bit to trigger the receiving process. Since the stop bit is used and is cleared. The receiving process just keep going until the receive buffer (2 bytes) is full. Now the I2C bus is locked up because it is waiting for a stop bit or a start bit at the end of the receiving.

    To fix this, the start bit and stop bit has to be set separately and timely. Set the start bit first, then poll the start bit. The stop bit has to be set right after the start bit is cleared. Otherwise the I2C module will keep going after the first byte is sent, you will get a second byte read. The EUSCI_B_I2C_masterReceiveSingleByte need to be modified to accommodate all cases. 

    With the modification, my driver works great.

    Peng

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    uint8_t EUSCI_B_I2C_masterReceiveSingleByte(uint16_t baseAddress)

    {

       //Set USCI in Receive mode

       HWREG16(baseAddress + OFS_UCBxCTLW0) &= ~UCTR;

       //Send start

       HWREG16(baseAddress + OFS_UCBxCTLW0) |= UCTXSTT;

      // Poll the start bit

       while(HWREG16(EUSCI_B0_BASE + OFS_UCBxCTLW0) & UCTXSTT) ;

      // Set the stop bit

     HWREG16(EUSCI_B0_BASE + OFS_UCBxCTLW0) |= UCTXSTP;

       //Poll for receive interrupt flag.

       while(!(HWREG16(baseAddress + OFS_UCBxIFG) & UCRXIFG))

       {

           ;

       }

       //Send single byte data.

       return (HWREG16(baseAddress + OFS_UCBxRXBUF));

    }

**Attention** This is a public forum