cancel
Showing results for 
Search instead for 
Did you mean: 

How to know the when the DMA transfer done?

diverger
Senior

Hi.

I use a trigger source to trigger  SPI tx/rx linked-list DMA (not circular). But have no clues how to jude if the tx/rx DMA is done. I traced the IRQ handler, it calls, the code below: 

 

 

 

/**
  * @brief  DMA SPI transmit receive process complete callback.
  *   hdma: pointer to a DMA_HandleTypeDef structure that contains
  *               the configuration information for the specified DMA module.
  * @retval None
  */
static void SPI_DMATransmitReceiveCplt(DMA_HandleTypeDef *hdma)
{
  SPI_HandleTypeDef *hspi = (SPI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;

  if (hspi->State != HAL_SPI_STATE_ABORT)
  {
    if ((hspi->hdmarx->Mode == DMA_LINKEDLIST_CIRCULAR) &&
        (hspi->hdmatx->Mode == DMA_LINKEDLIST_CIRCULAR))
    {
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)
      hspi->TxRxCpltCallback(hspi);
#else
      HAL_SPI_TxRxCpltCallback(hspi);
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
    }
    else
    {
      /* Enable EOT interrupt */
      __HAL_SPI_ENABLE_IT(hspi, SPI_IT_EOT);
    }
  }
}

 

 

It seems only when the DMA is configured as 'DMA_LINKEDLIST_CIRCULAR', the 'HAL_SPI_TxRxCpltCallback()' get called.

But in other situation, it only enable the EOT interrupt and simply return.

 

1 ACCEPTED SOLUTION

Accepted Solutions

Now that the caffeine has flushed out of my system (an event I promptly celebrated with recaffeination), I've looked at this again and I think you were looking at the wrong place.

 

You need to look at the IRQ handler for the DMA channel, not the SPI transfer. i.e. HAL_DMA_IRQHandler

which includes this conditional:

 

 if ((__HAL_DMA_GET_FLAG(hdma, DMA_FLAG_TC) != 0U))
  {
    /* Check if interrupt source is enabled */
    if (__HAL_DMA_GET_IT_SOURCE(hdma, DMA_IT_TC) != 0U)
    {
      // [...]
      /* Check transfer complete callback */
      if (hdma->XferCpltCallback != NULL)
      {
        /* Channel Transfer Complete callback */
        hdma->XferCpltCallback(hdma);
      }
    }
  }

 

So, you need to put your TC handler in hdma->XferCpltCallback. Which you can do cleanly using HAL_DMA_RegisterCallback

- If someone's post helped resolve your issue, please thank them by clicking "Accept as Solution".
- Please post an update with details once you've solved your issue. Your experience may help others.

View solution in original post

5 REPLIES 5
BarryWhit
Lead II

Yeah, this looks strange to me as well. Especially since

 

#define DMA_LINKEDLIST_NORMAL   DMA_LINKEDLIST             /*!< Linear linked-list DMA channel transfer   */
#define DMA_LINKEDLIST_CIRCULAR (DMA_LINKEDLIST | (0x01U)) /*!< Circular linked-list DMA channel transfer */

HAL_SPI_TransmitReceive_DMA() {
    if ((hspi->hdmarx->Mode & DMA_LINKEDLIST) == DMA_LINKEDLIST) {
         HAL_DMAEx_List_Start_IT() -> {
           /* Enable common interrupts: Transfer Complete and Transfer Errors ITs */
           __HAL_DMA_ENABLE_IT(hdma, (DMA_IT_TC | DMA_IT_DTE | DMA_IT_ULE | DMA_IT_USE | DMA_IT_TO));     
         }       
    }
}

 

So, the TC event is enabled whether mode is  Circular or Linear.

 

For now, you can use CubeMX/ProjectManager/Advanced Settings to enable "Register Callback" For SPI,

[Wrong]

- If someone's post helped resolve your issue, please thank them by clicking "Accept as Solution".
- Please post an update with details once you've solved your issue. Your experience may help others.

Hi,

Though I can register my own callback, the callback will be called only in linked-circular mode. :)

It seems the code intends to pass the event handling to the EOT handler,  then which handler callback should I use?

BarryWhit
Lead II

You're right of course. Too much coffee. Or maybe not enough.

- If someone's post helped resolve your issue, please thank them by clicking "Accept as Solution".
- Please post an update with details once you've solved your issue. Your experience may help others.

Hi,

I think my guess is right. After the EOT interrupt enabled, it will fire the SPI EOT IRQ, and the SPI IRQ handler will be called, then the same TxRx complete interrupt callback will be called. So, the strategy is mask the SPI's EOT interrupt, and let DMA engine to handle burst events, and finally enable the EOT of the last burst which hands the flow back to SPI, 

Thanks.

Now that the caffeine has flushed out of my system (an event I promptly celebrated with recaffeination), I've looked at this again and I think you were looking at the wrong place.

 

You need to look at the IRQ handler for the DMA channel, not the SPI transfer. i.e. HAL_DMA_IRQHandler

which includes this conditional:

 

 if ((__HAL_DMA_GET_FLAG(hdma, DMA_FLAG_TC) != 0U))
  {
    /* Check if interrupt source is enabled */
    if (__HAL_DMA_GET_IT_SOURCE(hdma, DMA_IT_TC) != 0U)
    {
      // [...]
      /* Check transfer complete callback */
      if (hdma->XferCpltCallback != NULL)
      {
        /* Channel Transfer Complete callback */
        hdma->XferCpltCallback(hdma);
      }
    }
  }

 

So, you need to put your TC handler in hdma->XferCpltCallback. Which you can do cleanly using HAL_DMA_RegisterCallback

- If someone's post helped resolve your issue, please thank them by clicking "Accept as Solution".
- Please post an update with details once you've solved your issue. Your experience may help others.