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.

I2C Write Failure causes Error Flag to not clear when followed by Successful Read

Other Parts Discussed in Thread: TM4C123GH6PM

I've been testing some I2C read/write code and come across an unexpected behavior. On my I2C bus I have a single slave at address 0x28 and I can read from it rapidly and repeatable without issue.

The issue that I've come across relates to the Error flag - Bit 1 in the I2CMCS (read) register:

When I READ a single byte from the non-existent I2C slave (0x27), then the Error bit (Bit 1) is set. A successful follow up READ or WRITE from the existing slave (0x28) then clears the Error bit. This is expected behavior.

However, when I WRITE a byte to the non-existent I2C slave (0x27) and the Error bit is set, then a successful READ will not clear the bit. Only a successful WRITE will clear the bit. This essentially indicates an error for every successful READing that follows a single unsuccessful WRITE.

The only way that I've found to clear the Error-bit is by resetting the I2C peripheral with "SysCtlPeripheralReset(SYSCTL_PERIPH_I2C1);" and then reconfiguring the peripheral, or by successfully writing to an I2C slave

This behavior of the Error-bit seems out of place at least because I haven't seen anywhere that says a failed write will jam the status register. First off, is there any evidence that this behavior is expected? And secondly, besides resetting the peripheral or writing to an existing I2C slave, is there something that I can do that will allow for a successful read to clear the Error-bit?

Code Excerpts:

// --------- Setup Pins and Peripherals --------------

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C1);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

//
// Enable pin PA6 for I2C1 I2C1SCL
//
MAP_GPIOPinConfigure(GPIO_PA6_I2C1SCL);
MAP_GPIOPinTypeI2CSCL(GPIO_PORTA_BASE, GPIO_PIN_6);

//
// Enable pin PA7 for I2C1 I2C1SDA
//
MAP_GPIOPinConfigure(GPIO_PA7_I2C1SDA);
MAP_GPIOPinTypeI2C(GPIO_PORTA_BASE, GPIO_PIN_7);

// Setup Master clock speed
MAP_I2CMasterInitExpClk(I2C_BUS_PORT_BASE, SysCtlClockGet(), FALSE);

// Test the Read/Write error handling
TempSimpleRead(0x27);
TempSimpleRead(0x28);

TempSimpleRead(0x27);
TempSimpleWrite(0x28);

TempSimpleWrite(0x27);
TempSimpleWrite(0x28);

TempSimpleWrite(0x27);
TempSimpleRead(0x28);

------------------------------------------

void TempSimpleRead(uint8_t bSlaveAddr)
{
    uint8_t tmpRx = 0U;
    
    MAP_I2CMasterSlaveAddrSet(I2C1_BASE, bSlaveAddr, TRUE);  // set address & read
    
    MAP_I2CMasterControl(I2C1_BASE,      I2C_MASTER_CMD_SINGLE_RECEIVE); // call for read - single

    // wait for read to finish
    while(I2CMasterBusy(I2C1_BASE) == FALSE);
    while(I2CMasterBusy(I2C1_BASE) == TRUE);
    
    __NOP(); // Add a little delay

    tmpRx = I2CMasterDataGet(I2C1_BASE);
}

void TempSimpleWrite(uint8_t bSlaveAddr)
{
    uint8_t tmpRx = 0U;
    
    MAP_I2CMasterSlaveAddrSet(I2C_BUS_PORT_BASE, bSlaveAddr, FALSE);  // set address & write
 
    MAP_I2CMasterDataPut(I2C_BUS_PORT_BASE, 0xA5);
 
    MAP_I2CMasterControl(I2C_BUS_PORT_BASE,      I2C_MASTER_CMD_SINGLE_SEND); // call for write - single

    // wait for read to finish
    while(I2CMasterBusy(I2C_BUS_PORT_BASE) == FALSE);
    while(I2CMasterBusy(I2C_BUS_PORT_BASE) == TRUE);
}

Development Environment Context:

  • Tiva™ C Series LaunchPad Evaluation Kit (TM4C123GH6PM)
  • IAR Embedded Workbench (7.1.7 Common Components, 7.30.4 for ARM component)
  • Using PA6 & PA7 with I2C1
  • Leveraging: TivaWare C Series 1.1

  • Hello Timothy,

    While this issue has not been seen (of course we did not do a read-write to a Slave Address that does not exist) and requires investigation, one other workaround may be to READ from the non-existent slave before doing a READ or WRITE to the correct slave address.

    Regards
    Amit
  • So you're suggesting a read of the peripheral to verify that it is responsive before writing to it? I agree that that would be a means of testing for the presence of a slave when it is consistently present or not. However if the slave responded to the read, then had a problem and couldn't acknowledge the write, then the problem would remain.
  • Hello Timothy,

    Does the slave in question does that? I do know some devices use ACK/NAK as a means of indicating readiness of the slave, so yes it may be an issue, but the WA should hold as doing the next slave access would involve preparing a write and then a read.

    Regards
    Amit
  • In virtually all situations the I2C slaves should be consistently responsive, so there should never be a failed *write* in the first place. What I'm considering is the unlikely but possible event where one of the slaves has a transient issue and fails to ACK to a *write*. Ideally what I'm looking for is a method to write a byte and regardless the success or failure, not have the Error bit get stuck.
  • Hello Timothy,

    I understand and I am looking into the I2C Controller to see if this is an issue and what are the WA's other than the one's you have listed

    Regards
    Amit
  • No worries. I appreciate your looking into it. I just wanted to confirm that I'm working to handle an edge-case.

    Thanks in advance,
    Timothy
  • Hello Timothy

    I checked the design and I see the issue with the Write frame to a undefined Slave. This is because after the ADDR NAK, the master will send one byte which will also create a NAK (DATA NAK). While the Address NAK gets cleared when a valid Slave Address is selected, the DATA NAK can only be cleared by a Write I2C Frame.

    The only "true" WA to this issue would be to Reset the Peripheral.

    Regards
    Amit
  • Okay, so this is expected from the design then. Thank you for looking into my questions, once again :-)

    Cheers,

    Timothy

  • When an error is flagged in the status register, does it affect the state of the I2C peripheral? Or is the error flag strictly for the application code to read and determine how to respond?

    ~Timothy
  • Hello Timothy

    No it does not affect the state of the peripheral. It is only meant for the application.

    Regards
    Amit
  • It just occurred to me that since the peripheral doesn't care about the error state, if I have a failed write followed by a successful read, could I simply ignore the DATA NAK error? Currently I halt a multibyte-read on any error being present on the peripheral, which is why this error bit being stuck causes a problem.

    ~Timothy
  • Hello Timothy,

    But the issue that will arise is there is an actual NAK then it will not be distinguishable from a previous NAK due to a faulty slave address. The only way I believe is to check for ADDR NAK and DATA NAK before accessing the next device.

    Regards
    Amit
  • In the case of faulty slave addresses, we're looking at the state of the Error flag (bit 1), AddressAck flag (bit 2), and the DataAck Flag (bit 3), right?

    When writing, we're writing both the slave address and a byte of data. The slave has to ACK/NACK each byte. If either are NACK'ed, then the corresponding flag is set to 1.

    When reading however, we still write the slave address which the slave will ACK/NACK. However when we read the following byte, it is up to the I2C master (the MCU) to signal the ACK/NACK, which won't affect the DataAck Flag, right?

    Because either a successful read or write will clear the AddressACK Flag, when reading can we tell if the slave ACK'ed the address by seeing the AddressAck Flag cleared to 0? If the preceding is correct, then when a read is done we don't care about the result of the DataAck since the MCU is the one creating the response. And when we write, then the DataAck will represent the results of the latest byte of data being written?

    Please let me know what I'm overlooking.

    ~Timothy
  • Hello Timothy

    You are correct. My idea is to ensure that when the read is done, the SW knows that it is a read via a variable so that the Error checking is not considered in this case.

    Regards
    Amit
  • Hi Amit,

    That sounds like a perfect WA. Thank you for your help!


    Cheers,

    Timothy