Skip to main content
Visitor II
September 14, 2020
Question

STM32F103 I2C DMA HAL bug

  • September 14, 2020
  • 0 replies
  • 994 views

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 */
 }
}

    This topic has been closed for replies.