Tengo un sensor conectado a través de I2C, donde leo datos en una dirección constante. El uC es un STM32F410CB con 100MHz. Un circuito de control se ejecuta a aproximadamente 16 kHz sin leer el sensor y cae a 5 kHz cuando siempre leo el sensor. I2C está configurado en el modo rápido (400kHz).
Así que decidí usar el DMA, donde hay una función disponible en las bibliotecas HAL que parecen bastante prometedoras:
HAL_I2C_Mem_Read_DMA(i2c1,addr,addr_mem,addr_mem_size,data,data_size);
Así que implementé esta función, que también se denomina en todos los ciclos que se ejecuta el ciclo de control. La frecuencia del bucle de control aumentó a 7 kHz ahora, pero aún no es suficiente para mi aplicación. Solo se generan 3 interrupciones para la lectura, no sé de dónde proviene esta gran sobrecarga. Cuando solo llamo a la función cada tres veces, el bucle de control se ejecuta dos veces a 16 kHz, luego una vez a 7 kHz. Necesito un resultado más estable. ¿Hay alguna forma de leer un sensor completamente autónomo a través de I2C en una dirección de registro constante, de manera que casi no se genere una sobrecarga?
/**
* @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 |= I2C_CR2_LAST;
}
/* 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 |= I2C_CR2_DMAEN;
}
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 |= I2C_CR1_STOP;
hi2c->State = HAL_I2C_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
}
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
Editar: Aquí está el código para la lectura I2C:
Nivel bajo:
//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;
}
Capa superior:
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) {
pos=buf_rx[0]<<8 | buf_rx[1];
}
Inic. I2C:
/* 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__);
}
}
Edición 2: Por cierto, la desaceleración ocurre solo cuando se llama a HAL_DMA_Start_IT (..) dentro de HAL_I2C_Mem_Read_DMA (..). Por lo tanto, es probable que esté relacionado de alguna manera con las interrupciones y con lo que sucede dentro.