STM32: Busy flag is set after I2C initialization
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.
Here is some code that might help you out. Basically, it's a realization of the Errata sheet (section 2.14.7) mentioned in a previous answer. I'm using the HAL library, and there are some references to the IKS01A1 driver header definitions (my periferal with the problem was the gyro on that board).
/* USER CODE BEGIN 1 */
/**
1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.
2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level
(Write 1 to GPIOx_ODR).
3. Check SCL and SDA High level in GPIOx_IDR.
4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
5. Check SDA Low level in GPIOx_IDR.
6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
7. Check SCL Low level in GPIOx_IDR.
8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to
GPIOx_ODR).
9. Check SCL High level in GPIOx_IDR.
10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to
GPIOx_ODR).
11. Check SDA High level in GPIOx_IDR.
12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
13. Set SWRST bit in I2Cx_CR1 register.
14. Clear SWRST bit in I2Cx_CR1 register.
15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.
**/
void HAL_I2C_ClearBusyFlagErrata_2_14_7(I2C_HandleTypeDef *hi2c) {
static uint8_t resetTried = 0;
if (resetTried == 1) {
return ;
}
uint32_t SDA_PIN = NUCLEO_I2C_EXPBD_SDA_PIN;
uint32_t SCL_PIN = NUCLEO_I2C_EXPBD_SCL_PIN;
GPIO_InitTypeDef GPIO_InitStruct;
// 1
__HAL_I2C_DISABLE(hi2c);
// 2
GPIO_InitStruct.Pin = SDA_PIN|SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WRITE_ODR(GPIOB, SDA_PIN);
HAL_GPIO_WRITE_ODR(GPIOB, SCL_PIN);
// 3
GPIO_PinState pinState;
if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_RESET) {
for(;;){}
}
if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_RESET) {
for(;;){}
}
// 4
GPIO_InitStruct.Pin = SDA_PIN;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_TogglePin(GPIOB, SDA_PIN);
// 5
if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_SET) {
for(;;){}
}
// 6
GPIO_InitStruct.Pin = SCL_PIN;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_TogglePin(GPIOB, SCL_PIN);
// 7
if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_SET) {
for(;;){}
}
// 8
GPIO_InitStruct.Pin = SDA_PIN;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WRITE_ODR(GPIOB, SDA_PIN);
// 9
if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_RESET) {
for(;;){}
}
// 10
GPIO_InitStruct.Pin = SCL_PIN;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WRITE_ODR(GPIOB, SCL_PIN);
// 11
if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_RESET) {
for(;;){}
}
// 12
GPIO_InitStruct.Pin = SDA_PIN|SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Alternate = NUCLEO_I2C_EXPBD_SCL_SDA_AF;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 13
hi2c->Instance->CR1 |= I2C_CR1_SWRST;
// 14
hi2c->Instance->CR1 ^= I2C_CR1_SWRST;
// 15
__HAL_I2C_ENABLE(hi2c);
resetTried = 1;
}
void HAL_GPIO_WRITE_ODR(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->ODR |= GPIO_Pin;
}
Just another thing to consider: In this ERRATA document (page 24) you can find there is a glitch in I2C analog filter which can cause BUSY
flag hanging. There is also a workaround you can try - it works for me.