2025-01-17 10:55 AM
Hi.
I am trying to communicate with AS5600 Position Sensor. We are using I2C communication for this purpose. Using STM32F439ZIT6 chip , running chip at 168 MHZ clock.
We are sommunicating with the sensor at Fast mode 400000 speed. Connection seems OK while there is no problem. But somehow when HAL turns error we lose alot of time to recover. At our main loop our cycle goes up to 50 ms ( tickresult) while trying to recover. Can you help me what I am doing wrong and what could be the source of this time loss.
HAL_StatusTypeDef I2C_ResetBus(I2C_HandleTypeDef* hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// Configure SCL and SDA as GPIO outputs
GPIO_InitStruct.Pin = MOTOR_SDA_Pin | MOTOR_SCL_Pin; // SCL and SDA pins
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(MOTOR_SCL_GPIO_Port, &GPIO_InitStruct); // GPIO port B
// Generate 9 clock pulses to release any stuck slave device
for (int i = 0; i < 9; i++) {
HAL_GPIO_WritePin(MOTOR_SCL_GPIO_Port, MOTOR_SCL_Pin, GPIO_PIN_SET); // SCL high
delay_us(10);
HAL_GPIO_WritePin(MOTOR_SCL_GPIO_Port, MOTOR_SCL_Pin, GPIO_PIN_RESET); // SCL low
delay_us(10);
}
// Generate a STOP condition
HAL_GPIO_WritePin(MOTOR_SDA_GPIO_Port, MOTOR_SDA_Pin, GPIO_PIN_RESET); // SDA low
delay_us(10);
HAL_GPIO_WritePin(MOTOR_SCL_GPIO_Port, MOTOR_SCL_Pin, GPIO_PIN_SET); // SCL high
delay_us(10);
HAL_GPIO_WritePin(MOTOR_SDA_GPIO_Port, MOTOR_SDA_Pin, GPIO_PIN_SET); // SDA high
delay_us(10);
// Reconfigure SCL and SDA as I2C pins
GPIO_InitStruct.Pin = MOTOR_SDA_Pin | MOTOR_SCL_Pin; // SCL and SDA pins
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; // I2C alternate function
HAL_GPIO_Init(MOTOR_SCL_GPIO_Port, &GPIO_InitStruct); // GPIO port B
// Reinitialize the I2C peripheral
return HAL_I2C_Init(hi2c);
}
HAL_StatusTypeDef AS5600_get_angle(AS5600_TypeDef* const handle, uint16_t* const angle)
{
uint8_t data[2] = {0};
HAL_StatusTypeDef status = HAL_I2C_Mem_Read(handle->i2c_handle, AS5600_SHIFTED_SLAVE_ADDRESS, AS5600_REGISTER_ANGLE_HIGH, I2C_MEMADD_SIZE_8BIT, data, 2, handle->i2c_timeout);
// If the I2C bus is busy, attempt recovery and retry
if (status == HAL_BUSY) {
// Attempt to reset the I2C bus
if (I2C_ResetBus(handle->i2c_handle) != HAL_OK) {
// If reset fails, return an error
return HAL_ERROR;
}
// Retry reading after resetting the bus
status = HAL_I2C_Mem_Read(handle->i2c_handle, AS5600_SHIFTED_SLAVE_ADDRESS, AS5600_REGISTER_ANGLE_HIGH, I2C_MEMADD_SIZE_8BIT, data, 2, handle->i2c_timeout);
}
// Check for other error statuses
if (status != HAL_OK) {
return status;
}
// If successful, compute the angle
*angle = ((data[0] << 8) | data[1]);
return HAL_OK;
}
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
tick1 = HAL_GetTick();
AS5600_get_angle(&as5600_instance, &angle);
tick2 = HAL_GetTick();
tickresult = tick2 - tick1;
}
I also tried to implement IT for non blocking comm. But somehow it performs worse than blocking comm.
Here is what I did
volatile uint8_t i2c_transfer_complete = 0;
uint8_t data[2] = {0};
HAL_StatusTypeDef AS5600_get_angle(AS5600_TypeDef* const handle, uint16_t* const angle)
{
HAL_StatusTypeDef status;
// Start the non-blocking I2C read operation
status = HAL_I2C_Mem_Read_IT(handle->i2c_handle,
AS5600_SHIFTED_SLAVE_ADDRESS,
AS5600_REGISTER_ANGLE_HIGH,
I2C_MEMADD_SIZE_8BIT,
data,
2);
if (status != HAL_OK)
{
return status; // Return if the read operation fails
}
// Wait for the transfer to complete
uint32_t start_time = HAL_GetTick();
while (!i2c_transfer_complete)
{
// Add a 3ms timeout to prevent infinite looping
if ((HAL_GetTick() - start_time) > 3)
{
return HAL_TIMEOUT;
}
}
// Reset the completion flag for the next operation
i2c_transfer_complete = 0;
// Process the received data
*angle = ((data[0] << 8) | data[1]);
return HAL_OK;
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if (hi2c->Instance == I2C1)
{
i2c_transfer_complete = 1; // Mark transfer as complete
}
}
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
if (hi2c->Instance == I2C1)
{
// Optionally reset the I2C bus if needed
I2C_ResetBus(hi2c);
}
}
Thanks.