Electronic – STM32: Busy flag is set after I2C initialization

i2cstm32stm32cubemx

For the reference: the same problem is described there, but the author's solution doesn't work for me – I2C busy flag strange behaviour

I used STM32CubeMX to generate project template with I2C peripherals initialization. Unfortunately it works somehow strange: after HAL_I2C_MspInit(I2C1) is being invoked, bus is considered permanently busy.

If I try to apply

__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(1000);
__HAL_RCC_I2C1_RELEASE_RESET();

That resolves problem with BUSY flag, but causes problem – SB bit not being set after START is generated. According to debugger, I2C registers are cleared completely after the reset – I suspect this is the problem with that method.

I also confimed short voltage drop at SDA line during startup, that is probably the cause of the issue. I took a closer look at SDA/SCL pins initialization code generated by CubeMX:

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(hi2c->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */

    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();

  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }

}

I changed it to put clock enable before HAL_GPIO_Init() invocation and now my I2C communications works (at least I didn't noticed anything weird yet).

Finally, my question is – is there any better solution for this? CubeMX places clock enable code after the GPIO init method invocation. I can stay with two invocations of __HAL_RCC_I2C1_CLK_ENABLE(), but that's quite ugly in my opinion, so I am looking for any better solution, either software or hardware.

Device is STM32F100RB on STM32VLDiscovery board (with STLink v1), in case that matters.

Best Answer

In my opinion STM32CubeMX code should not be considered as a ready to use code, but some as an example you can start with. With most of the microcontrollers it works, but there are some rare cases when it is not.

If you know it is not working and you have found the solution as well, you do not have to stick to the original code. In your case you can omit the __HAL_RCC_I2C1_CLK_ENABLE() call after the GPIO initialization, and leave the one before it. If it works, and you have said it works, then use the working way. Even ST's software can have bugs.

You are using an official board so the hardware should be OK, but you can check if the pull-up resistor values are correct. Or if a slave device does something during the initialization.

The best would be to run your code with everything disconnected from the Discovery (apart from the pull-ups), and check if it is still stuck in busy. If yes, it is fine if you replace that line in the generated code. It is not that big modification.

Unfortunately there is not any I2C exapmle in the STM32CubeF1 example package (this is not the code generator), under the STM32Cube_FW_F1_V1.4.0\Projects\STM32VL-Discovery\Examples. But if you check the MspInit functions of the UART or SPI. The clocks are enabled in both of them before the GPIO init.

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
  GPIO_InitTypeDef  GPIO_InitStruct;

  if (hspi->Instance == SPIx)
  {
    /*##-1- Enable peripherals and GPIO Clocks #################################*/
    /* Enable GPIO TX/RX clock */
    SPIx_SCK_GPIO_CLK_ENABLE();
    SPIx_MISO_GPIO_CLK_ENABLE();
    SPIx_MOSI_GPIO_CLK_ENABLE();
    /* Enable SPI clock */
    SPIx_CLK_ENABLE();

    /*##-2- Configure peripheral GPIO ##########################################*/
    /* SPI SCK GPIO pin configuration  */
    GPIO_InitStruct.Pin       = SPIx_SCK_PIN;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct);

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{  
  GPIO_InitTypeDef  GPIO_InitStruct;

  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* Enable GPIO TX/RX clock */
  USARTx_TX_GPIO_CLK_ENABLE();
  USARTx_RX_GPIO_CLK_ENABLE();


  /* Enable USARTx clock */
  USARTx_CLK_ENABLE(); 

  /*##-2- Configure peripheral GPIO ##########################################*/  
  /* UART TX GPIO pin configuration  */
  GPIO_InitStruct.Pin       = USARTx_TX_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_PULLUP;
  GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;

  HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);

So I think your solution is perfectly fine.