2020-09-13 05:32 PM
Hello, I had problems with setting up HAL_I2C_Mem_Read_DMA() function. Firstly I only defined the Rx DMA channel, but the program was still ending in HardFault_Handler(). After a long debugging I found bug in the "static void I2C_DMAXferCplt(DMA_HandleTypeDef *hdma) " (stm32f1xx_hal_i2c.c) line 6661 (14,15 in the snippet ).
The pointer to the undefined (Tx) DMA channel has value NULL -> write operation to this address cause HardFault. Is there any better way how to avoid the hard fault, then the creation of the Tx DMA?
static void I2C_DMAXferCplt(DMA_HandleTypeDef *hdma)
{
I2C_HandleTypeDef *hi2c = (I2C_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */
/* Declaration of temporary variable to prevent undefined behavior of volatile usage */
HAL_I2C_StateTypeDef CurrentState = hi2c->State;
HAL_I2C_ModeTypeDef CurrentMode = hi2c->Mode;
uint32_t CurrentXferOptions = hi2c->XferOptions;
/* Disable EVT and ERR interrupt */
__HAL_I2C_DISABLE_IT(hi2c, I2C_IT_EVT | I2C_IT_ERR);
/* Clear Complete callback */
hi2c->hdmatx->XferCpltCallback = NULL;
hi2c->hdmarx->XferCpltCallback = NULL;
if ((((uint32_t)CurrentState & (uint32_t)HAL_I2C_STATE_BUSY_TX) == (uint32_t)HAL_I2C_STATE_BUSY_TX) || ((((uint32_t)CurrentState & (uint32_t)HAL_I2C_STATE_BUSY_RX) == (uint32_t)HAL_I2C_STATE_BUSY_RX) && (CurrentMode == HAL_I2C_MODE_SLAVE)))
{
/* Disable DMA Request */
CLEAR_BIT(hi2c->Instance->CR2, I2C_CR2_DMAEN);
hi2c->XferCount = 0U;
if (CurrentState == HAL_I2C_STATE_BUSY_TX_LISTEN)
{
/* Set state at HAL_I2C_STATE_LISTEN */
hi2c->PreviousState = I2C_STATE_SLAVE_BUSY_TX;
hi2c->State = HAL_I2C_STATE_LISTEN;
/* Call the corresponding callback to inform upper layer of End of Transfer */
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
hi2c->SlaveTxCpltCallback(hi2c);
#else
HAL_I2C_SlaveTxCpltCallback(hi2c);
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
}
else if (CurrentState == HAL_I2C_STATE_BUSY_RX_LISTEN)
{
/* Set state at HAL_I2C_STATE_LISTEN */
hi2c->PreviousState = I2C_STATE_SLAVE_BUSY_RX;
hi2c->State = HAL_I2C_STATE_LISTEN;
/* Call the corresponding callback to inform upper layer of End of Transfer */
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
hi2c->SlaveRxCpltCallback(hi2c);
#else
HAL_I2C_SlaveRxCpltCallback(hi2c);
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
}
else
{
/* Do nothing */
}
/* Enable EVT and ERR interrupt to treat end of transfer in IRQ handler */
__HAL_I2C_ENABLE_IT(hi2c, I2C_IT_EVT | I2C_IT_ERR);
}
/* Check current Mode, in case of treatment DMA handler have been preempted by a prior interrupt */
else if (hi2c->Mode != HAL_I2C_MODE_NONE)
{
if (hi2c->XferCount == (uint16_t)1)
{
/* Disable Acknowledge */
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK);
}
/* Disable EVT and ERR interrupt */
__HAL_I2C_DISABLE_IT(hi2c, I2C_IT_EVT | I2C_IT_ERR);
/* Prepare next transfer or stop current transfer */
if ((CurrentXferOptions == I2C_NO_OPTION_FRAME) || (CurrentXferOptions == I2C_FIRST_AND_LAST_FRAME) || (CurrentXferOptions == I2C_OTHER_AND_LAST_FRAME) || (CurrentXferOptions == I2C_LAST_FRAME))
{
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
}
/* Disable Last DMA */
CLEAR_BIT(hi2c->Instance->CR2, I2C_CR2_LAST);
/* Disable DMA Request */
CLEAR_BIT(hi2c->Instance->CR2, I2C_CR2_DMAEN);
hi2c->XferCount = 0U;
/* Check if Errors has been detected during transfer */
if (hi2c->ErrorCode != HAL_I2C_ERROR_NONE)
{
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
hi2c->ErrorCallback(hi2c);
#else
HAL_I2C_ErrorCallback(hi2c);
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
}
else
{
hi2c->State = HAL_I2C_STATE_READY;
if (hi2c->Mode == HAL_I2C_MODE_MEM)
{
hi2c->Mode = HAL_I2C_MODE_NONE;
hi2c->PreviousState = I2C_STATE_NONE;
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
hi2c->MemRxCpltCallback(hi2c);
#else
HAL_I2C_MemRxCpltCallback(hi2c);
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
}
else
{
hi2c->Mode = HAL_I2C_MODE_NONE;
hi2c->PreviousState = I2C_STATE_MASTER_BUSY_RX;
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
hi2c->MasterRxCpltCallback(hi2c);
#else
HAL_I2C_MasterRxCpltCallback(hi2c);
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
}
}
}
else
{
/* Do nothing */
}
}