I2C busy flag strange behaviour
ST has released an errata sheet called:

STM32F100xC, STM32F10 0xD and STM32F100xE high-density value line device limitations.

The interesting point here is:


  2.9.7 I2C analog filter may provide wrong value, locking BUSY flag and preventing master mode entry


There is a detailed 15 step workaround that worked for me, surprisingly for an STM32F446, so I2C peripherals of every STM32 CORTEX-M series might be affected.

During this operation, the lines must not be actively pulled up or down by a bus member. So if you connect two I2C interfaces of the same MCU to the bus, first set up the pins of both to Alternate Function/Open Drain, then call the routine, as a transition of logical levels is required.

Here is an example with HAL libraries I use after the first initialization and during runtime, if an error occurs. As said above, this is for STM32F4, libraries for SMT32F1 might differ a bit.

struct I2C_Module
{
  I2C_HandleTypeDef   instance;
  uint16_t            sdaPin;
  GPIO_TypeDef*       sdaPort;
  uint16_t            sclPin;
  GPIO_TypeDef*       sclPort;
};

void I2C_ClearBusyFlagErratum(struct I2C_Module* i2c)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  // 1. Clear PE bit.
  i2c-&gt;instance.Instance-&gt;CR1 &amp;= ~(0x0001);

  //  2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
  GPIO_InitStructure.Mode         = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStructure.Alternate    = I2C_PIN_MAP;
  GPIO_InitStructure.Pull         = GPIO_PULLUP;
  GPIO_InitStructure.Speed        = GPIO_SPEED_FREQ_HIGH;

  GPIO_InitStructure.Pin          = i2c-&gt;sclPin;
  HAL_GPIO_Init(i2c-&gt;sclPort, &amp;GPIO_InitStructure);
  HAL_GPIO_WritePin(i2c-&gt;sclPort, i2c-&gt;sclPin, GPIO_PIN_SET);

  GPIO_InitStructure.Pin          = i2c-&gt;sdaPin;
  HAL_GPIO_Init(i2c-&gt;sdaPort, &amp;GPIO_InitStructure);
  HAL_GPIO_WritePin(i2c-&gt;sdaPort, i2c-&gt;sdaPin, GPIO_PIN_SET);

  // 3. Check SCL and SDA High level in GPIOx_IDR.
  while (GPIO_PIN_SET != HAL_GPIO_ReadPin(i2c-&gt;sclPort, i2c-&gt;sclPin))
  {
    asm("nop");
  }

  while (GPIO_PIN_SET != HAL_GPIO_ReadPin(i2c-&gt;sdaPort, i2c-&gt;sdaPin))
  {
    asm("nop");
  }

  // 4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
  HAL_GPIO_WritePin(i2c-&gt;sdaPort, i2c-&gt;sdaPin, GPIO_PIN_RESET);

  //  5. Check SDA Low level in GPIOx_IDR.
  while (GPIO_PIN_RESET != HAL_GPIO_ReadPin(i2c-&gt;sdaPort, i2c-&gt;sdaPin))
  {
    asm("nop");
  }

  // 6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
  HAL_GPIO_WritePin(i2c-&gt;sclPort, i2c-&gt;sclPin, GPIO_PIN_RESET);

  //  7. Check SCL Low level in GPIOx_IDR.
  while (GPIO_PIN_RESET != HAL_GPIO_ReadPin(i2c-&gt;sclPort, i2c-&gt;sclPin))
  {
    asm("nop");
  }

  // 8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
  HAL_GPIO_WritePin(i2c-&gt;sclPort, i2c-&gt;sclPin, GPIO_PIN_SET);

  // 9. Check SCL High level in GPIOx_IDR.
  while (GPIO_PIN_SET != HAL_GPIO_ReadPin(i2c-&gt;sclPort, i2c-&gt;sclPin))
  {
    asm("nop");
  }

  // 10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to GPIOx_ODR).
  HAL_GPIO_WritePin(i2c-&gt;sdaPort, i2c-&gt;sdaPin, GPIO_PIN_SET);

  // 11. Check SDA High level in GPIOx_IDR.
  while (GPIO_PIN_SET != HAL_GPIO_ReadPin(i2c-&gt;sdaPort, i2c-&gt;sdaPin))
  {
    asm("nop");
  }

  // 12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
  GPIO_InitStructure.Mode         = GPIO_MODE_AF_OD;
  GPIO_InitStructure.Alternate    = I2C_PIN_MAP;

  GPIO_InitStructure.Pin          = i2c-&gt;sclPin;
  HAL_GPIO_Init(i2c-&gt;sclPort, &amp;GPIO_InitStructure);

  GPIO_InitStructure.Pin          = i2c-&gt;sdaPin;
  HAL_GPIO_Init(i2c-&gt;sdaPort, &amp;GPIO_InitStructure);

  // 13. Set SWRST bit in I2Cx_CR1 register.
  i2c-&gt;instance.Instance-&gt;CR1 |= 0x8000;

  asm("nop");

  // 14. Clear SWRST bit in I2Cx_CR1 register.
  i2c-&gt;instance.Instance-&gt;CR1 &amp;= ~0x8000;

  asm("nop");

  // 15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register
  i2c-&gt;instance.Instance-&gt;CR1 |= 0x0001;

  // Call initialization function.
  HAL_I2C_Init(&amp;(i2c-&gt;instance));
}