STM32F4 I2C read out with least overhead

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
1
down vote

favorite
1












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.










share|improve this question























  • 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















up vote
1
down vote

favorite
1












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.










share|improve this question























  • 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













up vote
1
down vote

favorite
1









up vote
1
down vote

favorite
1






1





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.










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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

















  • 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











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.






share|improve this answer



























    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.






    share|improve this answer



























      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.






      share|improve this answer






















      • 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

















      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.





      share|improve this answer






















      • 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










      Your Answer




      StackExchange.ifUsing("editor", function ()
      return StackExchange.using("mathjaxEditing", function ()
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      );
      );
      , "mathjax-editing");

      StackExchange.ifUsing("editor", function ()
      return StackExchange.using("schematics", function ()
      StackExchange.schematics.init();
      );
      , "cicuitlab");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "135"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: false,
      noModals: false,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













       

      draft saved


      draft discarded


















      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






























      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.






      share|improve this answer
























        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.






        share|improve this answer






















          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.






          share|improve this answer












          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.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered 1 hour ago









          Arsenal

          11.7k11239




          11.7k11239






















              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.






              share|improve this answer
























                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.






                share|improve this answer






















                  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.






                  share|improve this answer












                  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.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 57 mins ago









                  P__J__

                  1,172313




                  1,172313




















                      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.






                      share|improve this answer






















                      • 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














                      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.






                      share|improve this answer






















                      • 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












                      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.






                      share|improve this answer














                      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.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      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
















                      • 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










                      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.





                      share|improve this answer






















                      • 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














                      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.





                      share|improve this answer






















                      • 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












                      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.





                      share|improve this answer














                      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.






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      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
















                      • 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

















                       

                      draft saved


                      draft discarded















































                       


                      draft saved


                      draft discarded














                      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













































































                      Comments

                      Popular posts from this blog

                      What does second last employer means? [closed]

                      List of Gilmore Girls characters

                      One-line joke