embedded protocol_error ai_generated true

I2C: clock stretch timeout on SCL line, slave holding clock low

ID: embedded/i2c-clock-stretch-timeout

Also available as: JSON · Markdown · 中文
79%Fix Rate
86%Confidence
1Evidence
2024-02-10First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
STM32Cube_FW_L4_V1.18.0 active
IAR EWARM 9.40.1 active
ARM GCC 10.3.1 active
FreeRTOSv202212.01 active

Root Cause

I2C slave device stretches the clock (holds SCL low) beyond the master's timeout period, often due to slow internal processing or bus lock.

generic

中文

I2C从设备拉伸时钟(保持SCL低电平)超过主设备超时周期,通常由内部处理缓慢或总线锁定引起。

Official Documentation

https://www.st.com/resource/en/application_note/dm00108059-i2c-timing-configuration-on-stm32-mcus-stmicroelectronics.pdf

Workarounds

  1. 82% success Increase I2C timeout in HAL_I2C_Master_Transmit(): set hi2c1.Init.Timing = 0x20C0E0FF; (for 100 kHz) and use HAL_I2C_Master_Transmit() with a timeout of 10000 ms instead of default 1000 ms.
    Increase I2C timeout in HAL_I2C_Master_Transmit(): set hi2c1.Init.Timing = 0x20C0E0FF; (for 100 kHz) and use HAL_I2C_Master_Transmit() with a timeout of 10000 ms instead of default 1000 ms.
  2. 78% success Implement software reset of I2C peripheral after timeout: call HAL_I2C_DeInit(&hi2c1); HAL_I2C_Init(&hi2c1); then retry the transaction up to 3 times.
    Implement software reset of I2C peripheral after timeout: call HAL_I2C_DeInit(&hi2c1); HAL_I2C_Init(&hi2c1); then retry the transaction up to 3 times.
  3. 85% success Use I2C recovery sequence: toggle SCL 9 times while SDA is high to release the slave: for(int i=0;i<9;i++) { HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET); delay_us(5); }
    Use I2C recovery sequence: toggle SCL 9 times while SDA is high to release the slave: for(int i=0;i<9;i++) { HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET); delay_us(5); }

中文步骤

  1. Increase I2C timeout in HAL_I2C_Master_Transmit(): set hi2c1.Init.Timing = 0x20C0E0FF; (for 100 kHz) and use HAL_I2C_Master_Transmit() with a timeout of 10000 ms instead of default 1000 ms.
  2. Implement software reset of I2C peripheral after timeout: call HAL_I2C_DeInit(&hi2c1); HAL_I2C_Init(&hi2c1); then retry the transaction up to 3 times.
  3. Use I2C recovery sequence: toggle SCL 9 times while SDA is high to release the slave: for(int i=0;i<9;i++) { HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET); delay_us(5); }

Dead Ends

Common approaches that don't work:

  1. Reducing I2C bus speed from 400 kHz to 100 kHz 80% fail

    Slower speed may increase stretch duration, making timeout more likely.

  2. Disabling clock stretching in the slave device configuration 90% fail

    Many I2C slaves require clock stretching for proper operation; disabling it may cause data corruption.

  3. Adding a hardware pull-up resistor on SCL line 85% fail

    Pull-up resistors are already present; this does not address the slave holding the line low.