fsl_i2c I2C_MasterStop stuck in while loop

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

fsl_i2c I2C_MasterStop stuck in while loop

1,803 Views
aberger
Contributor V

Intermittently, my fsl_i2c driver code is getting stuck in a forever while loop while trying to read bytes from an external I2C EEPROM chip.

I'm using the fsl_i2c driver for the MK64F and am trying to read 4 bytes via I2C read using I2C_MasterTransferBlocking(). At the end of reading the 4 bytes in I2C_MasterReadBlocking(), when (rxSize == 0U), the driver code executes I2C_MasterStop().

This is supposed to issue a STOP command with the following:

/* Issue the STOP command on the bus. */
base->C1 &= ~(uint8_t)(I2C_C1_MST_MASK | I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);

However, the code then gets stuck in the while loop:

while(0U != (base->S & (uint8_t)kI2C_BusBusyFlag)) 
{
}

By my understanding the BUSY bit of base->S should be cleared when a STOP signal is detected. Why did the base->C1 write fail to issue the STOP command?

It also appears that the NACK that is supposed to be issued after the 4th byte is read is not actually being issued:

if (rxSize == 1U)
{
    base->C1 |= I2C_C1_TXAK_MASK;
}

aberger_0-1691795399005.png

As mentioned at the top of the post, the I2C_MasterReadBlocking works most of the time (I can read out all of the 4096 bytes from this EEPROM many times before occasionally running into this issue). 

 

 

0 Kudos
Reply
7 Replies

1,737 Views
aberger
Contributor V

Is it possible that there is a bug in the I2C_MasterReadBlocking() code from the fsl_i2c.c driver (copied below with #ifdef's removed)

 

while (0U != (rxSize--))
    {
        if (rxSize == 0U)
        {
            if (0U == (flags & (uint32_t)kI2C_TransferNoStopFlag))
            {
                /* Issue STOP command before reading last byte. */
                result = I2C_MasterStop(base);
            }
            else
            {
                /* Change direction to Tx to avoid extra clocks. */
                base->C1 |= I2C_C1_TX_MASK;
            }
        }

        /* Clear the IICIF flag. */
        base->S = (uint8_t)kI2C_IntPendingFlag;

        /* Read from the data register. */
        *rxBuff++ = base->D;

        if (rxSize == 1U)
        {
            /* Issue NACK on read. */
            base->C1 |= I2C_C1_TXAK_MASK;
        }
    }

 

You can see that the data register is read before the NACK is configured for the last byte (if(rxSize == 1U)). From the K64 Reference Manual, reading the data register is responsible for initiating clocking-out of the next byte:

In master receive mode, reading this register initiates receiving of the next byte of data.

Therefore, if an interrupt occurs before C1 is configured for NACK but after the data register is read, it's possible that the last byte is clocked out with C1 still configured for ACK.

By comparison, look at lines 159 and 160 of https://github.com/jwr/kinetis_i2c/blob/main/i2c.c, which inverts the order of the C1 configurating and the data register read.

0 Kudos
Reply

1,701 Views
Pavel_Hernandez
NXP TechSupport
NXP TechSupport

Hello, my name is Pavel, and I will be supporting your case, could you tell me what examples from the SDK are you based on?

Best regards,
Pavel

 

0 Kudos
Reply

1,696 Views
aberger
Contributor V

Hi @Pavel_Hernandez, thanks for following up. I am using the fsl_i2c.c and fsl_i2c.h drivers included with SDK_2_7_0_TWR_K64F120M (Version 2.7.0 (303 2019-12-19)).

You could look at the i2c_polling_b2b_transfer_master example.

0 Kudos
Reply

1,662 Views
Pavel_Hernandez
NXP TechSupport
NXP TechSupport

Hello, I´m using the SDK version 2.11 and the example works as expected.

Best regards,
Pavel

 

0 Kudos
Reply

1,646 Views
aberger
Contributor V

I also have no trouble running the example as is. The issue I believe I am having is that, when using the I2C blocking code from the fsl driver in a system where there are interrupts, a poorly timed interrupt can occur after the 

/*Read from the data register. */
*rxBuff++ = base->D

but before the

 /* Issue NACK on read. */
base->C1 |= I2C_C1_TXAK_MASK;

As a result, the C1 register is still configured for ACK when the I2C hardware begins clocking out the next byte (triggered by the data register read). That means that after what should be the final byte is clocked out, an ACK is issued, the I2C_MasterStop() is never actually able to issue the STOP command, and the bus gets stuck in the busy state indefinitely.

I found that in my system, inverting the order of these lines of code makes the I2C communication more reliable.

0 Kudos
Reply

1,558 Views
bobpaddock
Senior Contributor III
Be wary of any code clearing flags via using "|=" (Read OR Write). NXP example code is full of them where assignment "=" is meant to be used to clear "Write 1 To Clear" (W1C) bits.

The OR clears bits unintentionally and statuses get lost. Using |= creates hard to diagnose timing races when used with W1C bits.
0 Kudos
Reply

1,583 Views
Pavel_Hernandez
NXP TechSupport
NXP TechSupport

Hello, thanks for your information, unfortunately, the example blocking does not have the robustness to detect that kind of event, I suggest changing the I2C to the non-blocking mode to get a better performance.

Best regards,
Pavel

0 Kudos
Reply