cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 I2C Hal Driver issue HAL_I2C_Mem_Read and HAL_I2C_Master_Receive

Sebastiaan
Senior

There's an issue in the STM32 I2C Hal Driver, observed in HAL_I2C_Mem_Read but the same issue might exist in other read commands.

I observed the issue that sometimes the I2C master fails to acknowledge and send a stop condition, after which the SDA is stuck low and HAL_BUSY is returned on consecutive I2C reads. I can reproduce this issue while stressing I2C read (reading 7 values of an accelero sensor) in combination with running a httpd and refreshing the webpage in my browser every 5 seconds.

I have noticed that the issue occurs when I get in the case where only 2 remaining bytes are left

/* Two bytes */
        else if (hi2c->XferSize == 2U)

The problem is that ACK is not disabled in this case (CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); )

Typically, when reading multiple data, the MCU will read 1 byte at a time from hi2c->Instance->DR, and eventually get in the case where 3 bytes are remaining (where the behavior is correct). However, if "I2C_FLAG_BTF" is set, the MCU will read out an extra byte. If after that one, only 2 bytes are remaining, the ACK is never disabled.

As a solution, something like below works for me: (check the "//NEW" comment)

{
        /* Wait until RXNE flag is set */
        if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
        {
          return HAL_ERROR;
        }
 
        /* Read data from DR */
        *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR;
 
        /* Increment Buffer pointer */
        hi2c->pBuffPtr++;
 
        /* Update counter */
        hi2c->XferSize--;
        hi2c->XferCount--;
 
        if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET)
        {
          if(hi2c->XferSize == 3U) {
            //NEW -> clear ACK (must be done before reading DR). After this read, only 2 bytes are remaining.
            CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK);
          }
          /* Read data from DR */
          *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR;
 
          /* Increment Buffer pointer */
          hi2c->pBuffPtr++;
 
          /* Update counter */
          hi2c->XferSize--;
          hi2c->XferCount--;
        }
      }
    }

However, I don't like this "I2C_FLAG_BTF" checking at all, there is no need for it since it doesn't really speed up the process and only (as you noticed) can create more bugs and more complex code. I think the function can work perfectly without the IF case but I might be wrong. Maybe it was added for another reason?

The same issue probably exists in the "HAL_I2C_Master_Receive" function, but I have not verified it. The code looks all the same.

I'm having even another doubt if this code (HAL_I2C_Mem_Read) is 100% correct, since I'm not sure if the "I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BTF, RESET, Timeout, tickstart)" (when only 2 bytes remaining) guarantees that effectively that exactly the last 2 bytes have been received. It seems possible to me that this condition could be raised also when the 2 first of the 3 last bytes have been received. But I might be wrong in that and it's harder to explain.

1 ACCEPTED SOLUTION

Accepted Solutions
Amira
Associate III

Hello,

Thanks for your contribution.

Referring to the reference manual RM0090 for STM32F4 "Figure 244. Transfer sequence diagram for master receiver "

The case you described it's due to :

"4. EV7-1 software sequence is not completed before the ACK pulse of the current byte transfer".

Although, this case is not supported in the HAL I2C driver and actually no plan to support this specific case.

The current HAL I2C driver manage the case when EV7-1 is completed before the ACK pulse of the current byte transfer.

We recommend to use I2C in Interrupt or DMA mode. However for polling mode, it's up to user to protect the following APIs HAL_I2C_Mem_Read() and HAL_I2C_Master_Receive() by disabling the global interrupt( __disable_irq();) then re-enabling it later ( __enable_irq();).

Regards,

Amira

View solution in original post

7 REPLIES 7
SofLit
ST Employee

Dear @Sebastiaan​ ,

Could you please provide which STM32 part number you are using?

STM32

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Sebastiaan
Senior

Hi,

Part number is STM32F407ZETx.

SofLit
ST Employee

Hello,

Thank you.

Your issue description will be escalated internally in order to be analyzed and fixed or enhanced if there is an issue.

STM32

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Amira
Associate III

Hello,

Thanks for your contribution.

Referring to the reference manual RM0090 for STM32F4 "Figure 244. Transfer sequence diagram for master receiver "

The case you described it's due to :

"4. EV7-1 software sequence is not completed before the ACK pulse of the current byte transfer".

Although, this case is not supported in the HAL I2C driver and actually no plan to support this specific case.

The current HAL I2C driver manage the case when EV7-1 is completed before the ACK pulse of the current byte transfer.

We recommend to use I2C in Interrupt or DMA mode. However for polling mode, it's up to user to protect the following APIs HAL_I2C_Mem_Read() and HAL_I2C_Master_Receive() by disabling the global interrupt( __disable_irq();) then re-enabling it later ( __enable_irq();).

Regards,

Amira

Sebastiaan
Senior

Hello Amira,

Thanks for your feedback.

I understand that the ideal scenario is not followed. However, there's a clear difference between scenarios that can perfectly be covered and scenarios that cannot be covered. I'm no expert on the HW peripheral, but it seems to me that the peripheral can perfectly handle the scenario (with clock stretching), even if the CPU is under load or interrupted. Disabling interrupts is not recommended since this impact the responsiveness.

I also understand that DMA / IRQ are recommended in some cases, but it makes application development more complex in certain situations (e.g. multiple threads requiring I2C access, error recovery, ...). Yes, sometimes this might be the way to go, but a direct interface is still much easier.

Amira
Associate III

Hello SMerc.1,

The issue is already described inside STM32F1 and the workarounds are there too to manage EV7_1, fail$:

2.14.1 Some software events must be managed before the current byte is being transferred

https://www.st.com/resource/en/errata_sheet/cd00190234-stm32f101x8b-stm32f102x8b-and-stm32f103x8b-mediumdensity-device-limitations-stmicroelectronics.pdf

I will ask for updating the Erratasheet of STM32F4 series too as it the same IP version as F1.

PS: Once your question is answered, please click on "Select as Best" for the comment containing the answer to your initial question.

I would this help you !

Thanks & Regards,

Amira

Amira

> I will ask for updating the Erratasheet of STM32F4 series too as it the same IP version as F1.

Hi Amira,

This is unsettling, to put it mildly. How many such problems have been already discovered, but did not make it to the respective ES? Some ES suspiciously did not change for quite a long time.

Can you please make sure this issue appears in *all* affected STM32's ES, not just the 'F407?

Thanks,

Jan

@Amira​