STM32CubeF4
STM32CubeF4 copied to clipboard
Problem in HAL_I2C_Mem_Read, no NAK generated in rare cases
Describe the set-up
- The board: custom board with STM32F407ZET6
- IDE: STM32CubeIDE 1.9.0
Describe the bug
In some cases HAL_I2C_Mem_Read will finish reading given number of bytes from I2C slave, but will not NAK the last byte. This may cause the slave to pull SDA low if its next byte's MSb is 0, thus freezing the I2C bus.
How to reproduce the bug
- My code is reading the data from slave via I2C bus using HAL_I2C_Mem_Read function.
- List the modules that you suspect to be the cause of the problem:
- stm32f4xx_hal_i2c.c
-
Describe the use case that generates the problem The problem appears when receiving >3 data bytes and HAL_I2C_Mem_Read function can be pre-empted. This could be also caused by an interrupt (I did not check this).
-
How we can reproduce the problem: Run HAL_I2C_Mem_Read from a task which can be pre-empted by FreeRTOS (or some interrupt) in order to read >3 data bytes via I2C.
Additional context
It seems that I localized the problem to lines 2812-2823 in file stm32f4xx_hal_i2c.c (looking at the latest commit 3d6be4b)
- The check of BTF flag and DR readout in these lines seems to be wrong and causing the erroneous behavior.
- Under normal circumstances (when task is not pre-empted by RTOS) BTF flag should be never high immediately after DR readout (line 2803). Even if BTF was high before DR read, according to reference manual it will be cleared when DR is read. So, this check seems unnecessary.
- Under normal circumstances last 3 data bytes will be handled by code in lines 2744-2792 which disables ACK for correct ending of data reception (with NAK).
- When there are exactly 4 bytes left to receive, the code will go to RXNE flag loop as usual in lines 2795-2824. If RTOS will switch to another task (or some interrupt happens) while code in lines 2806-2810 is being executed (after receiving 4th last byte) and some other task (or ISR) takes sufficient time for I2C DR and the shift register to be filled by incoming I2C data (3rd and 2nd last bytes), the problem will manifest itself:
- BTF flag will be set and the following check in line 2812 will yield TRUE, so the code inside IF clause gets executed.
- The code will read 3rd last data byte from I2C DR register and reduce the counter to 2.
- On the next loop the data reception will not be handled in lines 2744-2792 which disable ACK, but in lines 2711-2741 which will only generate STOP (if possible), but not disable ACK. This causes the described behavior on I2C bus when slave does not get NAK and prepares to send the next byte which can freeze the bus.
- My solution: remove the code in lines 2812-2823.
Screenshots
I used logic analyzer to drill down to the reason of the no-NAK behavior and used 4 extra GPIO outputs to understand what the code is doing. On screenshots, there is I2C bus SCL and SDA signals, as well as 4 GPIO lines:
- DBG0 (yellow) corresponds to execution of code in lines 2795-2824 (RXNE flag poll loop)
- DBG1 (green) corresponds to execution of code in lines 2692-2709 (1 byte left to receive)
- DBG2 (blue) corresponds to execution of code in lines 2711-2741 (2 bytes left to receive)
- DBG3 (purple) corresponds to execution of code in lines 2744-2792 (3 bytes left to receive)
On screenshot with normal data reception the 6 data bytes are received as expected: first three bytes with RXNE loop (DBG0 high), last three bytes in lines 2744-2792 (DBG3 high), NAK in the end.
On screenshot with the problematic behavior, the last bytes are handled in code lines 2711-2741 (DBG2 high), no NAK in the end.
Duplicate of stm32f4xx_hal_driver#17.
Hi @ks000,
Thank you for your contribution. Would it be possible to share the project you have used to reproduce the issue in order to allow a better analysis of the problem.
With regards,
Hi @ASELSTM,
Sorry, I can't share the whole project, but I can share a snippet of code from where HAL_I2C_Mem_Read is called. The software in my project works under the control of FreeRTOS and reads data from LSM303AGR STM accelerometer.
My code calls function lsm303agr_acceleration_raw_get() from lsm303agr_reg.c driver by STM which in turn calls data read callback (snippet below) in order to read 6 bytes from the accelerometer:
int32_t platform_read(void *handle, uint8_t Reg, uint8_t *Bufp, uint16_t len) {
uint32_t i2c_add = (uint32_t) handle;
if (i2c_add == LSM303AGR_I2C_ADD_XL) {
/* enable auto incremented in multiple read/write commands */
Reg |= 0x80;
}
HAL_I2C_Mem_Read(&hi2c2, (uint8_t) i2c_add, Reg, I2C_MEMADD_SIZE_8BIT, Bufp, len, 1000);
return 0;
}
I think you do not need LSM303AGR, any I2C memory will do, but >4 bytes should be read with a single call.
Just make sure that the thread which calls HAL_I2C_Mem_Read() gets pre-empted frequently.
I guess if you use such setup and set a breakpoint to line stm32f4xx_hal_i2c.c:2815, then you should eventually get it stopped, and from there you can observe the incorrect behavior: going to lines 2711-2741 and not disabling I2C ACK while running out of bytes to receive.
Let me know if I can help somehow else.
Best regards.
Hi. What is the status of this ticket? Is it planned to be fixed soon? We are facing a similar issue on a stm32f412vg when reading data from an external EEPROM. See also https://github.com/STMicroelectronics/stm32f4xx_hal_driver/issues/17#issuecomment-1380198130.
Hi @ks000,
As this issue is a duplicate of stm32f4xx_hal_driver#17 please allow me thus to close this one and keep the other.
With regards,