I have a problem with an I2C bus on a STM32F4, that get locked pretty fast after startup. The SDA line get hold low the whole time and the SCL line high. So this looks like a standard I2C bus lock to me and I tried the following routine to unlock it after the I2C transmit throws a BUSY error:
void I2C3_ClearBusyFlagErratum(I2C_HandleTypeDef *instance)
{
GPIO_InitTypeDef GPIO_InitStruct;
int timeout =100;
int timeout_cnt=0;
// 1. Clear PE bit.
instance->Instance->CR1 &= ~(0x0001);
// 2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C3;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = I2C3_SCL_PIN;
HAL_GPIO_Init(I2C3_SCL_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(I2C3_SCL_PORT, I2C3_SCL_PIN, GPIO_PIN_SET);
GPIO_InitStruct.Pin = I2C3_SDA_PIN;
HAL_GPIO_Init(I2C3_SDA_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(I2C3_SDA_PORT, I2C3_SDA_PIN, GPIO_PIN_SET);
// 3. Check SCL and SDA High level in GPIOx_IDR.
while (GPIO_PIN_SET != HAL_GPIO_ReadPin(I2C3_SCL_PORT, I2C3_SCL_PIN))
{
timeout_cnt++;
if(timeout_cnt>timeout)
return;
}
while (GPIO_PIN_SET != HAL_GPIO_ReadPin(I2C3_SDA_PORT, I2C3_SDA_PIN))
{
timeout_cnt++;
if(timeout_cnt>timeout)
return;
}
// 4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
HAL_GPIO_WritePin(I2C3_SDA_PORT, I2C3_SDA_PIN, GPIO_PIN_RESET);
// 5. Check SDA Low level in GPIOx_IDR.
while (GPIO_PIN_RESET != HAL_GPIO_ReadPin(I2C3_SDA_PORT, I2C3_SDA_PIN))
{
timeout_cnt++;
if(timeout_cnt>timeout)
return;
}
// 6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
HAL_GPIO_WritePin(I2C3_SCL_PORT, I2C3_SCL_PIN, GPIO_PIN_RESET);
// 7. Check SCL Low level in GPIOx_IDR.
while (GPIO_PIN_RESET != HAL_GPIO_ReadPin(I2C3_SCL_PORT, I2C3_SCL_PIN))
{
timeout_cnt++;
if(timeout_cnt>timeout)
return;
}
// 8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
HAL_GPIO_WritePin(I2C3_SCL_PORT, I2C3_SCL_PIN, GPIO_PIN_SET);
// 9. Check SCL High level in GPIOx_IDR.
while (GPIO_PIN_SET != HAL_GPIO_ReadPin(I2C3_SCL_PORT, I2C3_SCL_PIN))
{
timeout_cnt++;
if(timeout_cnt>timeout)
return;
}
// 10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to GPIOx_ODR).
HAL_GPIO_WritePin(I2C3_SDA_PORT, I2C3_SDA_PIN, GPIO_PIN_SET);
// 11. Check SDA High level in GPIOx_IDR.
while (GPIO_PIN_SET != HAL_GPIO_ReadPin(I2C3_SDA_PORT, I2C3_SDA_PIN))
{
timeout_cnt++;
if(timeout_cnt>timeout)
return;
}
// 12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C3;
GPIO_InitStruct.Pin = I2C3_SCL_PIN;
HAL_GPIO_Init(I2C3_SCL_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = I2C3_SDA_PIN;
HAL_GPIO_Init(I2C3_SDA_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(I2C3_SCL_PORT, I2C3_SCL_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(I2C3_SDA_PORT, I2C3_SDA_PIN, GPIO_PIN_SET);
// 13. Set SWRST bit in I2Cx_CR1 register.
instance->Instance->CR1 |= 0x8000;
asm("nop");
// 14. Clear SWRST bit in I2Cx_CR1 register.
instance->Instance->CR1 &= ~0x8000;
asm("nop");
// 15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register
instance->Instance->CR1 |= 0x0001;
// Call initialization function.
HAL_I2C_Init(instance);
}
The Problem is, this routine comes never further than step 3, while (GPIO_PIN_SET != HAL_GPIO_ReadPin(I2C3_SDA_PORT, I2C3_SDA_PIN)) and will then return. So , the SDA cannot be set to high anymore. Has anyone an Idea how I could unlock the bus? The Busy flag is never set low after an error occured.