STM32F4 I2C read out with least overhead
Clash Royale CLAN TAG#URR8PPP
up vote
1
down vote
favorite
I have a sensor connected over I2C, where I read out data at a constant address. The uC is an STM32F410CB with 100MHz. A control loop runs at around 16kHz without reading out the sensor and drops to 5 kHz when I always read out the sensor. I2C is configured in the fast mode (400kHz).
So I decided to use the DMA, where a function is available in the HAL libraries that look pretty promising:
HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,addr_mem_size,data,data_size);
So I implemented this function, which is also called every cycle the control loop runs. The frequency of the control loop increased to 7kHz now, but that's still not enough for my application. Only 3 Interrupts are generated for the read out, I don't know where this huge overhead comes from. When I only call the function every third time, the control loop runs twice at 16kHz, then once at 7kHz. I need a more stable result. Is there a way to fully autonomous read out a sensor over I2C at a constant register address, such that nearly no overhead is generated?
/**
* @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address
* @param MemAddress Internal memory address
* @param MemAddSize Size of internal memory address
* @param pData Pointer to data buffer
* @param Size Amount of data to be read
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
uint32_t tickstart = 0x00U;
__IO uint32_t count = 0U;
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
/* Check the parameters */
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize));
if(hi2c->State == HAL_I2C_STATE_READY)
/* Wait until BUSY flag is reset */
count = I2C_TIMEOUT_BUSY_FLAG * (SystemCoreClock /25U /1000U);
do
if(count-- == 0U)
hi2c->PreviousState = I2C_STATE_NONE;
hi2c->State= HAL_I2C_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
while(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) != RESET);
/* Process Locked */
__HAL_LOCK(hi2c);
/* Check if the I2C is already enabled */
if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
/* Enable I2C peripheral */
__HAL_I2C_ENABLE(hi2c);
/* Disable Pos */
hi2c->Instance->CR1 &= ~I2C_CR1_POS;
hi2c->State = HAL_I2C_STATE_BUSY_RX;
hi2c->Mode = HAL_I2C_MODE_MEM;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferOptions = I2C_NO_OPTION_FRAME;
hi2c->XferSize = hi2c->XferCount;
if(hi2c->XferSize > 0U)
/* Set the I2C DMA transfer complete callback */
hi2c->hdmarx->XferCpltCallback = I2C_DMAXferCplt;
/* Set the DMA error callback */
hi2c->hdmarx->XferErrorCallback = I2C_DMAError;
/* Set the unused DMA callbacks to NULL */
hi2c->hdmarx->XferHalfCpltCallback = NULL;
hi2c->hdmarx->XferM1CpltCallback = NULL;
hi2c->hdmarx->XferM1HalfCpltCallback = NULL;
hi2c->hdmarx->XferAbortCallback = NULL;
/* Enable the DMA Stream */
HAL_DMA_Start_IT(hi2c->hdmarx, (uint32_t)&hi2c->Instance->DR, (uint32_t)hi2c->pBuffPtr, hi2c->XferSize);
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
if(Size == 1U)
/* Disable Acknowledge */
hi2c->Instance->CR1 &= ~I2C_CR1_ACK;
else
/* Enable Last DMA bit */
hi2c->Instance->CR2
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
/* Note : The I2C interrupts must be enabled after unlocking current process
to avoid the risk of I2C interrupt handle execution before current
process unlock */
/* Enable ERR interrupt */
__HAL_I2C_ENABLE_IT(hi2c, I2C_IT_ERR);
/* Enable DMA Request */
hi2c->Instance->CR2
else
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Generate Stop */
hi2c->Instance->CR1
return HAL_OK;
else
return HAL_BUSY;
Edit: Here is the code for the I2C readout:
Low Level:
//DMA I2C
err_t I2C_Driver_TXRX_DMA(uint16_t addr, uint16_t addr_mem, uint8_t* pData, uint16_t size)
addr = addr << 1;
err_t err=.value=HAL_OK;
err.value=HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,1,pData,size); //Error callback called in error case
return err;
Higher Layer:
uint8_t buf_rx[2]=0;
uint16_t pos=0;
void triggerDMAMeasurementPosition()
if(getI2CErrorState())
I2C_Error_Solver(ENC_ADDR);
i2c_err=0;
else
I2C_Driver_TXRX_DMA(ENC_ADDR,RAW_ANGLE_ADDR,buf_rx,2);
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) buf_rx[1];
I2C Init:
/* I2C1 init function */
static void MX_I2C1_Init(void)
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
_Error_Handler(__FILE__, __LINE__);
Edit 2: BTW, the slow down happens only when HAL_DMA_Start_IT(..) is called inside HAL_I2C_Mem_Read_DMA(..). So it is probably somehow related to the interrupts and what happens inside there.
stm32
 |Â
show 5 more comments
up vote
1
down vote
favorite
I have a sensor connected over I2C, where I read out data at a constant address. The uC is an STM32F410CB with 100MHz. A control loop runs at around 16kHz without reading out the sensor and drops to 5 kHz when I always read out the sensor. I2C is configured in the fast mode (400kHz).
So I decided to use the DMA, where a function is available in the HAL libraries that look pretty promising:
HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,addr_mem_size,data,data_size);
So I implemented this function, which is also called every cycle the control loop runs. The frequency of the control loop increased to 7kHz now, but that's still not enough for my application. Only 3 Interrupts are generated for the read out, I don't know where this huge overhead comes from. When I only call the function every third time, the control loop runs twice at 16kHz, then once at 7kHz. I need a more stable result. Is there a way to fully autonomous read out a sensor over I2C at a constant register address, such that nearly no overhead is generated?
/**
* @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address
* @param MemAddress Internal memory address
* @param MemAddSize Size of internal memory address
* @param pData Pointer to data buffer
* @param Size Amount of data to be read
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
uint32_t tickstart = 0x00U;
__IO uint32_t count = 0U;
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
/* Check the parameters */
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize));
if(hi2c->State == HAL_I2C_STATE_READY)
/* Wait until BUSY flag is reset */
count = I2C_TIMEOUT_BUSY_FLAG * (SystemCoreClock /25U /1000U);
do
if(count-- == 0U)
hi2c->PreviousState = I2C_STATE_NONE;
hi2c->State= HAL_I2C_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
while(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) != RESET);
/* Process Locked */
__HAL_LOCK(hi2c);
/* Check if the I2C is already enabled */
if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
/* Enable I2C peripheral */
__HAL_I2C_ENABLE(hi2c);
/* Disable Pos */
hi2c->Instance->CR1 &= ~I2C_CR1_POS;
hi2c->State = HAL_I2C_STATE_BUSY_RX;
hi2c->Mode = HAL_I2C_MODE_MEM;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferOptions = I2C_NO_OPTION_FRAME;
hi2c->XferSize = hi2c->XferCount;
if(hi2c->XferSize > 0U)
/* Set the I2C DMA transfer complete callback */
hi2c->hdmarx->XferCpltCallback = I2C_DMAXferCplt;
/* Set the DMA error callback */
hi2c->hdmarx->XferErrorCallback = I2C_DMAError;
/* Set the unused DMA callbacks to NULL */
hi2c->hdmarx->XferHalfCpltCallback = NULL;
hi2c->hdmarx->XferM1CpltCallback = NULL;
hi2c->hdmarx->XferM1HalfCpltCallback = NULL;
hi2c->hdmarx->XferAbortCallback = NULL;
/* Enable the DMA Stream */
HAL_DMA_Start_IT(hi2c->hdmarx, (uint32_t)&hi2c->Instance->DR, (uint32_t)hi2c->pBuffPtr, hi2c->XferSize);
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
if(Size == 1U)
/* Disable Acknowledge */
hi2c->Instance->CR1 &= ~I2C_CR1_ACK;
else
/* Enable Last DMA bit */
hi2c->Instance->CR2
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
/* Note : The I2C interrupts must be enabled after unlocking current process
to avoid the risk of I2C interrupt handle execution before current
process unlock */
/* Enable ERR interrupt */
__HAL_I2C_ENABLE_IT(hi2c, I2C_IT_ERR);
/* Enable DMA Request */
hi2c->Instance->CR2
else
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Generate Stop */
hi2c->Instance->CR1
return HAL_OK;
else
return HAL_BUSY;
Edit: Here is the code for the I2C readout:
Low Level:
//DMA I2C
err_t I2C_Driver_TXRX_DMA(uint16_t addr, uint16_t addr_mem, uint8_t* pData, uint16_t size)
addr = addr << 1;
err_t err=.value=HAL_OK;
err.value=HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,1,pData,size); //Error callback called in error case
return err;
Higher Layer:
uint8_t buf_rx[2]=0;
uint16_t pos=0;
void triggerDMAMeasurementPosition()
if(getI2CErrorState())
I2C_Error_Solver(ENC_ADDR);
i2c_err=0;
else
I2C_Driver_TXRX_DMA(ENC_ADDR,RAW_ANGLE_ADDR,buf_rx,2);
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) buf_rx[1];
I2C Init:
/* I2C1 init function */
static void MX_I2C1_Init(void)
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
_Error_Handler(__FILE__, __LINE__);
Edit 2: BTW, the slow down happens only when HAL_DMA_Start_IT(..) is called inside HAL_I2C_Mem_Read_DMA(..). So it is probably somehow related to the interrupts and what happens inside there.
stm32
It would help to have your code too - it's unlikely to be a HAL issue. Also, what's the sensor?
– awjlogan
3 hours ago
Can you change the sensor to a SPI version ? Are the I2C lines only used for this particular IC ?
– Damien
3 hours ago
SPI is no option, I know that would be pretty handy. The sensor is an AS5601 magnetic encoder, there are no cheap SPI versions available. The I2C is only used for this sensor.
– HansPeterLoft
3 hours ago
"So I implemented this function, which is also called every cycle the control loop runs...." This sound like your problem, depending on how much data you are sending and receiving it could easily prevent you to operate at 15 kHz.
– Rokta
3 hours ago
Well, when I call this function and the DMA is ongoing, it will just return a busy and continue, or at least it should? But somehow it always slows down to 7kHz when this function is called.
– HansPeterLoft
3 hours ago
 |Â
show 5 more comments
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have a sensor connected over I2C, where I read out data at a constant address. The uC is an STM32F410CB with 100MHz. A control loop runs at around 16kHz without reading out the sensor and drops to 5 kHz when I always read out the sensor. I2C is configured in the fast mode (400kHz).
So I decided to use the DMA, where a function is available in the HAL libraries that look pretty promising:
HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,addr_mem_size,data,data_size);
So I implemented this function, which is also called every cycle the control loop runs. The frequency of the control loop increased to 7kHz now, but that's still not enough for my application. Only 3 Interrupts are generated for the read out, I don't know where this huge overhead comes from. When I only call the function every third time, the control loop runs twice at 16kHz, then once at 7kHz. I need a more stable result. Is there a way to fully autonomous read out a sensor over I2C at a constant register address, such that nearly no overhead is generated?
/**
* @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address
* @param MemAddress Internal memory address
* @param MemAddSize Size of internal memory address
* @param pData Pointer to data buffer
* @param Size Amount of data to be read
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
uint32_t tickstart = 0x00U;
__IO uint32_t count = 0U;
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
/* Check the parameters */
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize));
if(hi2c->State == HAL_I2C_STATE_READY)
/* Wait until BUSY flag is reset */
count = I2C_TIMEOUT_BUSY_FLAG * (SystemCoreClock /25U /1000U);
do
if(count-- == 0U)
hi2c->PreviousState = I2C_STATE_NONE;
hi2c->State= HAL_I2C_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
while(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) != RESET);
/* Process Locked */
__HAL_LOCK(hi2c);
/* Check if the I2C is already enabled */
if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
/* Enable I2C peripheral */
__HAL_I2C_ENABLE(hi2c);
/* Disable Pos */
hi2c->Instance->CR1 &= ~I2C_CR1_POS;
hi2c->State = HAL_I2C_STATE_BUSY_RX;
hi2c->Mode = HAL_I2C_MODE_MEM;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferOptions = I2C_NO_OPTION_FRAME;
hi2c->XferSize = hi2c->XferCount;
if(hi2c->XferSize > 0U)
/* Set the I2C DMA transfer complete callback */
hi2c->hdmarx->XferCpltCallback = I2C_DMAXferCplt;
/* Set the DMA error callback */
hi2c->hdmarx->XferErrorCallback = I2C_DMAError;
/* Set the unused DMA callbacks to NULL */
hi2c->hdmarx->XferHalfCpltCallback = NULL;
hi2c->hdmarx->XferM1CpltCallback = NULL;
hi2c->hdmarx->XferM1HalfCpltCallback = NULL;
hi2c->hdmarx->XferAbortCallback = NULL;
/* Enable the DMA Stream */
HAL_DMA_Start_IT(hi2c->hdmarx, (uint32_t)&hi2c->Instance->DR, (uint32_t)hi2c->pBuffPtr, hi2c->XferSize);
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
if(Size == 1U)
/* Disable Acknowledge */
hi2c->Instance->CR1 &= ~I2C_CR1_ACK;
else
/* Enable Last DMA bit */
hi2c->Instance->CR2
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
/* Note : The I2C interrupts must be enabled after unlocking current process
to avoid the risk of I2C interrupt handle execution before current
process unlock */
/* Enable ERR interrupt */
__HAL_I2C_ENABLE_IT(hi2c, I2C_IT_ERR);
/* Enable DMA Request */
hi2c->Instance->CR2
else
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Generate Stop */
hi2c->Instance->CR1
return HAL_OK;
else
return HAL_BUSY;
Edit: Here is the code for the I2C readout:
Low Level:
//DMA I2C
err_t I2C_Driver_TXRX_DMA(uint16_t addr, uint16_t addr_mem, uint8_t* pData, uint16_t size)
addr = addr << 1;
err_t err=.value=HAL_OK;
err.value=HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,1,pData,size); //Error callback called in error case
return err;
Higher Layer:
uint8_t buf_rx[2]=0;
uint16_t pos=0;
void triggerDMAMeasurementPosition()
if(getI2CErrorState())
I2C_Error_Solver(ENC_ADDR);
i2c_err=0;
else
I2C_Driver_TXRX_DMA(ENC_ADDR,RAW_ANGLE_ADDR,buf_rx,2);
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) buf_rx[1];
I2C Init:
/* I2C1 init function */
static void MX_I2C1_Init(void)
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
_Error_Handler(__FILE__, __LINE__);
Edit 2: BTW, the slow down happens only when HAL_DMA_Start_IT(..) is called inside HAL_I2C_Mem_Read_DMA(..). So it is probably somehow related to the interrupts and what happens inside there.
stm32
I have a sensor connected over I2C, where I read out data at a constant address. The uC is an STM32F410CB with 100MHz. A control loop runs at around 16kHz without reading out the sensor and drops to 5 kHz when I always read out the sensor. I2C is configured in the fast mode (400kHz).
So I decided to use the DMA, where a function is available in the HAL libraries that look pretty promising:
HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,addr_mem_size,data,data_size);
So I implemented this function, which is also called every cycle the control loop runs. The frequency of the control loop increased to 7kHz now, but that's still not enough for my application. Only 3 Interrupts are generated for the read out, I don't know where this huge overhead comes from. When I only call the function every third time, the control loop runs twice at 16kHz, then once at 7kHz. I need a more stable result. Is there a way to fully autonomous read out a sensor over I2C at a constant register address, such that nearly no overhead is generated?
/**
* @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address
* @param MemAddress Internal memory address
* @param MemAddSize Size of internal memory address
* @param pData Pointer to data buffer
* @param Size Amount of data to be read
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
uint32_t tickstart = 0x00U;
__IO uint32_t count = 0U;
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
/* Check the parameters */
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize));
if(hi2c->State == HAL_I2C_STATE_READY)
/* Wait until BUSY flag is reset */
count = I2C_TIMEOUT_BUSY_FLAG * (SystemCoreClock /25U /1000U);
do
if(count-- == 0U)
hi2c->PreviousState = I2C_STATE_NONE;
hi2c->State= HAL_I2C_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
while(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) != RESET);
/* Process Locked */
__HAL_LOCK(hi2c);
/* Check if the I2C is already enabled */
if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
/* Enable I2C peripheral */
__HAL_I2C_ENABLE(hi2c);
/* Disable Pos */
hi2c->Instance->CR1 &= ~I2C_CR1_POS;
hi2c->State = HAL_I2C_STATE_BUSY_RX;
hi2c->Mode = HAL_I2C_MODE_MEM;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferOptions = I2C_NO_OPTION_FRAME;
hi2c->XferSize = hi2c->XferCount;
if(hi2c->XferSize > 0U)
/* Set the I2C DMA transfer complete callback */
hi2c->hdmarx->XferCpltCallback = I2C_DMAXferCplt;
/* Set the DMA error callback */
hi2c->hdmarx->XferErrorCallback = I2C_DMAError;
/* Set the unused DMA callbacks to NULL */
hi2c->hdmarx->XferHalfCpltCallback = NULL;
hi2c->hdmarx->XferM1CpltCallback = NULL;
hi2c->hdmarx->XferM1HalfCpltCallback = NULL;
hi2c->hdmarx->XferAbortCallback = NULL;
/* Enable the DMA Stream */
HAL_DMA_Start_IT(hi2c->hdmarx, (uint32_t)&hi2c->Instance->DR, (uint32_t)hi2c->pBuffPtr, hi2c->XferSize);
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
if(Size == 1U)
/* Disable Acknowledge */
hi2c->Instance->CR1 &= ~I2C_CR1_ACK;
else
/* Enable Last DMA bit */
hi2c->Instance->CR2
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
/* Note : The I2C interrupts must be enabled after unlocking current process
to avoid the risk of I2C interrupt handle execution before current
process unlock */
/* Enable ERR interrupt */
__HAL_I2C_ENABLE_IT(hi2c, I2C_IT_ERR);
/* Enable DMA Request */
hi2c->Instance->CR2
else
/* Send Slave Address and Memory Address */
if(I2C_RequestMemoryRead(hi2c, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, tickstart) != HAL_OK)
if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
else
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Generate Stop */
hi2c->Instance->CR1
return HAL_OK;
else
return HAL_BUSY;
Edit: Here is the code for the I2C readout:
Low Level:
//DMA I2C
err_t I2C_Driver_TXRX_DMA(uint16_t addr, uint16_t addr_mem, uint8_t* pData, uint16_t size)
addr = addr << 1;
err_t err=.value=HAL_OK;
err.value=HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,1,pData,size); //Error callback called in error case
return err;
Higher Layer:
uint8_t buf_rx[2]=0;
uint16_t pos=0;
void triggerDMAMeasurementPosition()
if(getI2CErrorState())
I2C_Error_Solver(ENC_ADDR);
i2c_err=0;
else
I2C_Driver_TXRX_DMA(ENC_ADDR,RAW_ANGLE_ADDR,buf_rx,2);
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) buf_rx[1];
I2C Init:
/* I2C1 init function */
static void MX_I2C1_Init(void)
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
_Error_Handler(__FILE__, __LINE__);
Edit 2: BTW, the slow down happens only when HAL_DMA_Start_IT(..) is called inside HAL_I2C_Mem_Read_DMA(..). So it is probably somehow related to the interrupts and what happens inside there.
stm32
stm32
edited 2 hours ago
asked 3 hours ago
HansPeterLoft
1191215
1191215
It would help to have your code too - it's unlikely to be a HAL issue. Also, what's the sensor?
– awjlogan
3 hours ago
Can you change the sensor to a SPI version ? Are the I2C lines only used for this particular IC ?
– Damien
3 hours ago
SPI is no option, I know that would be pretty handy. The sensor is an AS5601 magnetic encoder, there are no cheap SPI versions available. The I2C is only used for this sensor.
– HansPeterLoft
3 hours ago
"So I implemented this function, which is also called every cycle the control loop runs...." This sound like your problem, depending on how much data you are sending and receiving it could easily prevent you to operate at 15 kHz.
– Rokta
3 hours ago
Well, when I call this function and the DMA is ongoing, it will just return a busy and continue, or at least it should? But somehow it always slows down to 7kHz when this function is called.
– HansPeterLoft
3 hours ago
 |Â
show 5 more comments
It would help to have your code too - it's unlikely to be a HAL issue. Also, what's the sensor?
– awjlogan
3 hours ago
Can you change the sensor to a SPI version ? Are the I2C lines only used for this particular IC ?
– Damien
3 hours ago
SPI is no option, I know that would be pretty handy. The sensor is an AS5601 magnetic encoder, there are no cheap SPI versions available. The I2C is only used for this sensor.
– HansPeterLoft
3 hours ago
"So I implemented this function, which is also called every cycle the control loop runs...." This sound like your problem, depending on how much data you are sending and receiving it could easily prevent you to operate at 15 kHz.
– Rokta
3 hours ago
Well, when I call this function and the DMA is ongoing, it will just return a busy and continue, or at least it should? But somehow it always slows down to 7kHz when this function is called.
– HansPeterLoft
3 hours ago
It would help to have your code too - it's unlikely to be a HAL issue. Also, what's the sensor?
– awjlogan
3 hours ago
It would help to have your code too - it's unlikely to be a HAL issue. Also, what's the sensor?
– awjlogan
3 hours ago
Can you change the sensor to a SPI version ? Are the I2C lines only used for this particular IC ?
– Damien
3 hours ago
Can you change the sensor to a SPI version ? Are the I2C lines only used for this particular IC ?
– Damien
3 hours ago
SPI is no option, I know that would be pretty handy. The sensor is an AS5601 magnetic encoder, there are no cheap SPI versions available. The I2C is only used for this sensor.
– HansPeterLoft
3 hours ago
SPI is no option, I know that would be pretty handy. The sensor is an AS5601 magnetic encoder, there are no cheap SPI versions available. The I2C is only used for this sensor.
– HansPeterLoft
3 hours ago
"So I implemented this function, which is also called every cycle the control loop runs...." This sound like your problem, depending on how much data you are sending and receiving it could easily prevent you to operate at 15 kHz.
– Rokta
3 hours ago
"So I implemented this function, which is also called every cycle the control loop runs...." This sound like your problem, depending on how much data you are sending and receiving it could easily prevent you to operate at 15 kHz.
– Rokta
3 hours ago
Well, when I call this function and the DMA is ongoing, it will just return a busy and continue, or at least it should? But somehow it always slows down to 7kHz when this function is called.
– HansPeterLoft
3 hours ago
Well, when I call this function and the DMA is ongoing, it will just return a busy and continue, or at least it should? But somehow it always slows down to 7kHz when this function is called.
– HansPeterLoft
3 hours ago
 |Â
show 5 more comments
4 Answers
4
active
oldest
votes
up vote
2
down vote
No, the STM32F4 I2C implementation does not allow a fully autonomous operation.
There is a state machine which has to be implemented in software for the peripheral to do its work. This can either be done with interrupts or with polling. From my experience handling it with polling results in a more robust operation. Polling of course has the problem that your controller isn't doing useful work, but if your system is just waiting on new input values, it wouldn't matter.
You can implement that yourself to try and fix your problem. If you go down that road, be prepared for some hardship as the I2C peripheral is not a masterpiece in terms of usability.
Going down that route also allows you to get the I2C clock higher than 400 kHz - it is outside of the normal specification but there is a note that you can get details from ST on how to run it at 1 MHz. I asked them and never got feedback, so I fiddled around until I ended up with 800 kHz running from 8 MHz. I guess with 100 MHz you should get 1 MHz quite easily.
add a comment |Â
up vote
2
down vote
First of all - 16 bits + 2x address (16 bits) + memory read address 8 bits + ACK / NACK * 4 I think + 2xSTART + 1xSTOP (if I did not missed something) which makes about 40 clocks. 400kHz / 40 ~= 10k sequences / second.
Reading two bytes in using DMA is useless. It will probably take more time setting it using HAL and processing the callbacks than the transfer itself.
Conclusion - you cant reach this rate using I2C.
Do not use HAL. It is much easier and efficient if you program it the bare register way.
add a comment |Â
up vote
1
down vote
The I2C data rate is probably not high enough to support your desired sampling frequency. Looking at your code, it seems you are reading two bytes of data (ENC_ADDR
, and RAW_ANGLE_ADDR
). Combine that with the address and R/W byte gives 24 bits of data. Now, 400 kHz / 24 is 16.7 kHz so it's tight even with no overhead. I would suggest trying to sample reliably at, say, 8 kHz. Alternatively, looking at the AS5601's datasheet, it supports 1 MHz I2C; if the microcontroller supports that, you could push the data rate up.
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
add a comment |Â
up vote
0
down vote
I'm not familiar ST chip and library, but translating from what I know from other system.
Based on the code, particularly from the lock-unlock sequences, it seems it is being used in a RTOS, which is probably the framework of ST for their chip.
RTOS are great, but it needs some understanding for dealing with code that needs to run fast. Sometimes, if the code is fairly simple and you need speed, it is better to avoid using the RTOS.
RTOS basically is a task scheduler (a mini kernel), and every time it needs to switch from a task to another, it will use some time.
What happens in this code, is that it request the handle on the I2C port, as it wait, it probably will execute other tasks. If that port is used by other tasks, it needs to wait until that other task is done.
Depending on how your hardware is wired, if the I2C line is only used for this sensor, you can bypass the I2C handle request, open the I2C one time and then just keep reading it, this will probably optimize quite a bit. You can also put this process on a specific task with high priority.
If it's used by other chip you need to access, you can also keep everything in a single task, and handle yourself when which chip is called, also avoiding having to unlock / lock the I2C bus.
EDIT
Since it's not an RTOS and added code to your question:
- Try rewriting the I2C driver to avoid all the mutexes.
- I'm not sure the DMA is a gain in there, since you seem to have to handle the packet byte by byte anyway, it loose sense.
- Try avoiding interrupts, processor time switching into interrupt can be quite long and it is always faster to poll it from the main loop to avoid context switch.
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
No, the STM32F4 I2C implementation does not allow a fully autonomous operation.
There is a state machine which has to be implemented in software for the peripheral to do its work. This can either be done with interrupts or with polling. From my experience handling it with polling results in a more robust operation. Polling of course has the problem that your controller isn't doing useful work, but if your system is just waiting on new input values, it wouldn't matter.
You can implement that yourself to try and fix your problem. If you go down that road, be prepared for some hardship as the I2C peripheral is not a masterpiece in terms of usability.
Going down that route also allows you to get the I2C clock higher than 400 kHz - it is outside of the normal specification but there is a note that you can get details from ST on how to run it at 1 MHz. I asked them and never got feedback, so I fiddled around until I ended up with 800 kHz running from 8 MHz. I guess with 100 MHz you should get 1 MHz quite easily.
add a comment |Â
up vote
2
down vote
No, the STM32F4 I2C implementation does not allow a fully autonomous operation.
There is a state machine which has to be implemented in software for the peripheral to do its work. This can either be done with interrupts or with polling. From my experience handling it with polling results in a more robust operation. Polling of course has the problem that your controller isn't doing useful work, but if your system is just waiting on new input values, it wouldn't matter.
You can implement that yourself to try and fix your problem. If you go down that road, be prepared for some hardship as the I2C peripheral is not a masterpiece in terms of usability.
Going down that route also allows you to get the I2C clock higher than 400 kHz - it is outside of the normal specification but there is a note that you can get details from ST on how to run it at 1 MHz. I asked them and never got feedback, so I fiddled around until I ended up with 800 kHz running from 8 MHz. I guess with 100 MHz you should get 1 MHz quite easily.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
No, the STM32F4 I2C implementation does not allow a fully autonomous operation.
There is a state machine which has to be implemented in software for the peripheral to do its work. This can either be done with interrupts or with polling. From my experience handling it with polling results in a more robust operation. Polling of course has the problem that your controller isn't doing useful work, but if your system is just waiting on new input values, it wouldn't matter.
You can implement that yourself to try and fix your problem. If you go down that road, be prepared for some hardship as the I2C peripheral is not a masterpiece in terms of usability.
Going down that route also allows you to get the I2C clock higher than 400 kHz - it is outside of the normal specification but there is a note that you can get details from ST on how to run it at 1 MHz. I asked them and never got feedback, so I fiddled around until I ended up with 800 kHz running from 8 MHz. I guess with 100 MHz you should get 1 MHz quite easily.
No, the STM32F4 I2C implementation does not allow a fully autonomous operation.
There is a state machine which has to be implemented in software for the peripheral to do its work. This can either be done with interrupts or with polling. From my experience handling it with polling results in a more robust operation. Polling of course has the problem that your controller isn't doing useful work, but if your system is just waiting on new input values, it wouldn't matter.
You can implement that yourself to try and fix your problem. If you go down that road, be prepared for some hardship as the I2C peripheral is not a masterpiece in terms of usability.
Going down that route also allows you to get the I2C clock higher than 400 kHz - it is outside of the normal specification but there is a note that you can get details from ST on how to run it at 1 MHz. I asked them and never got feedback, so I fiddled around until I ended up with 800 kHz running from 8 MHz. I guess with 100 MHz you should get 1 MHz quite easily.
answered 1 hour ago


Arsenal
11.7k11239
11.7k11239
add a comment |Â
add a comment |Â
up vote
2
down vote
First of all - 16 bits + 2x address (16 bits) + memory read address 8 bits + ACK / NACK * 4 I think + 2xSTART + 1xSTOP (if I did not missed something) which makes about 40 clocks. 400kHz / 40 ~= 10k sequences / second.
Reading two bytes in using DMA is useless. It will probably take more time setting it using HAL and processing the callbacks than the transfer itself.
Conclusion - you cant reach this rate using I2C.
Do not use HAL. It is much easier and efficient if you program it the bare register way.
add a comment |Â
up vote
2
down vote
First of all - 16 bits + 2x address (16 bits) + memory read address 8 bits + ACK / NACK * 4 I think + 2xSTART + 1xSTOP (if I did not missed something) which makes about 40 clocks. 400kHz / 40 ~= 10k sequences / second.
Reading two bytes in using DMA is useless. It will probably take more time setting it using HAL and processing the callbacks than the transfer itself.
Conclusion - you cant reach this rate using I2C.
Do not use HAL. It is much easier and efficient if you program it the bare register way.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
First of all - 16 bits + 2x address (16 bits) + memory read address 8 bits + ACK / NACK * 4 I think + 2xSTART + 1xSTOP (if I did not missed something) which makes about 40 clocks. 400kHz / 40 ~= 10k sequences / second.
Reading two bytes in using DMA is useless. It will probably take more time setting it using HAL and processing the callbacks than the transfer itself.
Conclusion - you cant reach this rate using I2C.
Do not use HAL. It is much easier and efficient if you program it the bare register way.
First of all - 16 bits + 2x address (16 bits) + memory read address 8 bits + ACK / NACK * 4 I think + 2xSTART + 1xSTOP (if I did not missed something) which makes about 40 clocks. 400kHz / 40 ~= 10k sequences / second.
Reading two bytes in using DMA is useless. It will probably take more time setting it using HAL and processing the callbacks than the transfer itself.
Conclusion - you cant reach this rate using I2C.
Do not use HAL. It is much easier and efficient if you program it the bare register way.
answered 57 mins ago
P__J__
1,172313
1,172313
add a comment |Â
add a comment |Â
up vote
1
down vote
The I2C data rate is probably not high enough to support your desired sampling frequency. Looking at your code, it seems you are reading two bytes of data (ENC_ADDR
, and RAW_ANGLE_ADDR
). Combine that with the address and R/W byte gives 24 bits of data. Now, 400 kHz / 24 is 16.7 kHz so it's tight even with no overhead. I would suggest trying to sample reliably at, say, 8 kHz. Alternatively, looking at the AS5601's datasheet, it supports 1 MHz I2C; if the microcontroller supports that, you could push the data rate up.
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
add a comment |Â
up vote
1
down vote
The I2C data rate is probably not high enough to support your desired sampling frequency. Looking at your code, it seems you are reading two bytes of data (ENC_ADDR
, and RAW_ANGLE_ADDR
). Combine that with the address and R/W byte gives 24 bits of data. Now, 400 kHz / 24 is 16.7 kHz so it's tight even with no overhead. I would suggest trying to sample reliably at, say, 8 kHz. Alternatively, looking at the AS5601's datasheet, it supports 1 MHz I2C; if the microcontroller supports that, you could push the data rate up.
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
add a comment |Â
up vote
1
down vote
up vote
1
down vote
The I2C data rate is probably not high enough to support your desired sampling frequency. Looking at your code, it seems you are reading two bytes of data (ENC_ADDR
, and RAW_ANGLE_ADDR
). Combine that with the address and R/W byte gives 24 bits of data. Now, 400 kHz / 24 is 16.7 kHz so it's tight even with no overhead. I would suggest trying to sample reliably at, say, 8 kHz. Alternatively, looking at the AS5601's datasheet, it supports 1 MHz I2C; if the microcontroller supports that, you could push the data rate up.
The I2C data rate is probably not high enough to support your desired sampling frequency. Looking at your code, it seems you are reading two bytes of data (ENC_ADDR
, and RAW_ANGLE_ADDR
). Combine that with the address and R/W byte gives 24 bits of data. Now, 400 kHz / 24 is 16.7 kHz so it's tight even with no overhead. I would suggest trying to sample reliably at, say, 8 kHz. Alternatively, looking at the AS5601's datasheet, it supports 1 MHz I2C; if the microcontroller supports that, you could push the data rate up.
edited 1 hour ago
answered 1 hour ago
awjlogan
3,1631826
3,1631826
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
add a comment |Â
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Thx, ye I think I really have to change to an SPI version, this will minimize the overhead. I2C is really not made for this I think ,but I wondered anyway what the best method is to read out a sensor over I2C with least overhead.
– HansPeterLoft
1 hour ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
Probably the way you are going is pretty close to minimal overhead - a lot of the HAL functions will be optimised out when not used. You could write a stripped down HAL as Damien suggests, an interesting exercise if nothing else. I2C inherently has quite a high overhead with the START:ADDR:[DATA]:ACK protocol, especially if DATA is short.
– awjlogan
58 mins ago
add a comment |Â
up vote
0
down vote
I'm not familiar ST chip and library, but translating from what I know from other system.
Based on the code, particularly from the lock-unlock sequences, it seems it is being used in a RTOS, which is probably the framework of ST for their chip.
RTOS are great, but it needs some understanding for dealing with code that needs to run fast. Sometimes, if the code is fairly simple and you need speed, it is better to avoid using the RTOS.
RTOS basically is a task scheduler (a mini kernel), and every time it needs to switch from a task to another, it will use some time.
What happens in this code, is that it request the handle on the I2C port, as it wait, it probably will execute other tasks. If that port is used by other tasks, it needs to wait until that other task is done.
Depending on how your hardware is wired, if the I2C line is only used for this sensor, you can bypass the I2C handle request, open the I2C one time and then just keep reading it, this will probably optimize quite a bit. You can also put this process on a specific task with high priority.
If it's used by other chip you need to access, you can also keep everything in a single task, and handle yourself when which chip is called, also avoiding having to unlock / lock the I2C bus.
EDIT
Since it's not an RTOS and added code to your question:
- Try rewriting the I2C driver to avoid all the mutexes.
- I'm not sure the DMA is a gain in there, since you seem to have to handle the packet byte by byte anyway, it loose sense.
- Try avoiding interrupts, processor time switching into interrupt can be quite long and it is always faster to poll it from the main loop to avoid context switch.
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
add a comment |Â
up vote
0
down vote
I'm not familiar ST chip and library, but translating from what I know from other system.
Based on the code, particularly from the lock-unlock sequences, it seems it is being used in a RTOS, which is probably the framework of ST for their chip.
RTOS are great, but it needs some understanding for dealing with code that needs to run fast. Sometimes, if the code is fairly simple and you need speed, it is better to avoid using the RTOS.
RTOS basically is a task scheduler (a mini kernel), and every time it needs to switch from a task to another, it will use some time.
What happens in this code, is that it request the handle on the I2C port, as it wait, it probably will execute other tasks. If that port is used by other tasks, it needs to wait until that other task is done.
Depending on how your hardware is wired, if the I2C line is only used for this sensor, you can bypass the I2C handle request, open the I2C one time and then just keep reading it, this will probably optimize quite a bit. You can also put this process on a specific task with high priority.
If it's used by other chip you need to access, you can also keep everything in a single task, and handle yourself when which chip is called, also avoiding having to unlock / lock the I2C bus.
EDIT
Since it's not an RTOS and added code to your question:
- Try rewriting the I2C driver to avoid all the mutexes.
- I'm not sure the DMA is a gain in there, since you seem to have to handle the packet byte by byte anyway, it loose sense.
- Try avoiding interrupts, processor time switching into interrupt can be quite long and it is always faster to poll it from the main loop to avoid context switch.
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
add a comment |Â
up vote
0
down vote
up vote
0
down vote
I'm not familiar ST chip and library, but translating from what I know from other system.
Based on the code, particularly from the lock-unlock sequences, it seems it is being used in a RTOS, which is probably the framework of ST for their chip.
RTOS are great, but it needs some understanding for dealing with code that needs to run fast. Sometimes, if the code is fairly simple and you need speed, it is better to avoid using the RTOS.
RTOS basically is a task scheduler (a mini kernel), and every time it needs to switch from a task to another, it will use some time.
What happens in this code, is that it request the handle on the I2C port, as it wait, it probably will execute other tasks. If that port is used by other tasks, it needs to wait until that other task is done.
Depending on how your hardware is wired, if the I2C line is only used for this sensor, you can bypass the I2C handle request, open the I2C one time and then just keep reading it, this will probably optimize quite a bit. You can also put this process on a specific task with high priority.
If it's used by other chip you need to access, you can also keep everything in a single task, and handle yourself when which chip is called, also avoiding having to unlock / lock the I2C bus.
EDIT
Since it's not an RTOS and added code to your question:
- Try rewriting the I2C driver to avoid all the mutexes.
- I'm not sure the DMA is a gain in there, since you seem to have to handle the packet byte by byte anyway, it loose sense.
- Try avoiding interrupts, processor time switching into interrupt can be quite long and it is always faster to poll it from the main loop to avoid context switch.
I'm not familiar ST chip and library, but translating from what I know from other system.
Based on the code, particularly from the lock-unlock sequences, it seems it is being used in a RTOS, which is probably the framework of ST for their chip.
RTOS are great, but it needs some understanding for dealing with code that needs to run fast. Sometimes, if the code is fairly simple and you need speed, it is better to avoid using the RTOS.
RTOS basically is a task scheduler (a mini kernel), and every time it needs to switch from a task to another, it will use some time.
What happens in this code, is that it request the handle on the I2C port, as it wait, it probably will execute other tasks. If that port is used by other tasks, it needs to wait until that other task is done.
Depending on how your hardware is wired, if the I2C line is only used for this sensor, you can bypass the I2C handle request, open the I2C one time and then just keep reading it, this will probably optimize quite a bit. You can also put this process on a specific task with high priority.
If it's used by other chip you need to access, you can also keep everything in a single task, and handle yourself when which chip is called, also avoiding having to unlock / lock the I2C bus.
EDIT
Since it's not an RTOS and added code to your question:
- Try rewriting the I2C driver to avoid all the mutexes.
- I'm not sure the DMA is a gain in there, since you seem to have to handle the packet byte by byte anyway, it loose sense.
- Try avoiding interrupts, processor time switching into interrupt can be quite long and it is always faster to poll it from the main loop to avoid context switch.
edited 2 hours ago
answered 3 hours ago
Damien
941112
941112
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
add a comment |Â
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
No RTOS is used in my project, but the code from HAL is probably optimized for a FreeRTOS etc.
– HansPeterLoft
3 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
Either way, try bypassing the mutexes requests
– Damien
2 hours ago
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2felectronics.stackexchange.com%2fquestions%2f401632%2fstm32f4-i2c-read-out-with-least-overhead%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
It would help to have your code too - it's unlikely to be a HAL issue. Also, what's the sensor?
– awjlogan
3 hours ago
Can you change the sensor to a SPI version ? Are the I2C lines only used for this particular IC ?
– Damien
3 hours ago
SPI is no option, I know that would be pretty handy. The sensor is an AS5601 magnetic encoder, there are no cheap SPI versions available. The I2C is only used for this sensor.
– HansPeterLoft
3 hours ago
"So I implemented this function, which is also called every cycle the control loop runs...." This sound like your problem, depending on how much data you are sending and receiving it could easily prevent you to operate at 15 kHz.
– Rokta
3 hours ago
Well, when I call this function and the DMA is ongoing, it will just return a busy and continue, or at least it should? But somehow it always slows down to 7kHz when this function is called.
– HansPeterLoft
3 hours ago