简体   繁体   中英

No interrupt in SDcard with DMA on STM32L476 and CubeMX

I have an unsolved issue with SDcard.

The project is based on STM32L476, the IDE is Atollic and configuration is made by STM32CucbeMX (as I do with all my designs).

Without DMA (no DMA template in FATFS and no configured DMA channels) the SDcard works. When I use the DMA (see screenshot), the very first read operation stucks and after the 30s timeout I get an error.

DMA配置

I have investigated inside the code and I have the evidence that there is not any interrupt after the HAL_SD_ReadBlocks_DMA(). This is the cause of the timeout. The function itself returns no error, so it believes the data transfer is running, but it is not.

The interrupts are so configured:

中断请求配置

The CubeMX is version 6.2.1. and the STM32L4 packages are updated.

My opinion is that there is some error in libraries or in the Cube generated code. I have seen other (rare) posts about a similar issue suspecting a libray error.

This is very imbarassing for me because I have an important design that is waiting for the SD card working at full speed.

Without DMA, as I told, the SDcard works but the writing speed is too low and I must run the interface at very low frequency to avoid writing errors. I suppose that read/write operations via DMA will give me the needed speed, but this interrupt problem keeps all the things in stall.

Is there some idea about the non-working interrupt?

Thank you very much

I had the same issue, here is how I fixed it.

This solution is base a solution I found on the stm32 forums but I tried to move stuff to make it simple to implement and so that no everything gets lost each time you regenerate with cube mx. I put override functions in the main but they could be in the user section of the bsp file as well I think.

For reference, I started from this example then applied the fix below https://community.st.com/s/article/how-to-create-a-file-system-on-a-sd-card-using-stm32bubeide?t=1637759710161

Fix steps:

  1. In cubeMX use a single DMA on channel 4 instead of 1 for Rx and 1 for Tx.

  2. In main.c

    • create config functions for Tx and Rx dma
/* USER CODE BEGIN PFP */
    //Create config functions for rx and tx dma
    static HAL_StatusTypeDef SD_DMAConfigRx(SD_HandleTypeDef *hsd);
    static HAL_StatusTypeDef SD_DMAConfigTx(SD_HandleTypeDef *hsd);
    /* USER CODE END PFP */
  • These function will be called each time you tx or rx to reconfigure DMA for rx or tx
        /* USER CODE BEGIN 4 */
    
    /**
      * @brief Configure the DMA to receive data from the SD card
      * @retval
      *  HAL_ERROR or HAL_OK
      */
    static HAL_StatusTypeDef SD_DMAConfigRx(SD_HandleTypeDef *hsd)
    {
      static DMA_HandleTypeDef hdma_rx;
      HAL_StatusTypeDef status = HAL_ERROR;
    
      /* Configure DMA Rx parameters */
      hdma_rx.Init.Request             = DMA_REQUEST_7;
      hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
      hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
      hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;
      hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
      hdma_rx.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    
      hdma_rx.Instance = DMA2_Channel4;
    
      /* Associate the DMA handle */
      __HAL_LINKDMA(hsd, hdmarx, hdma_rx);
    
      /* Stop any ongoing transfer and reset the state*/
      HAL_DMA_Abort(&hdma_rx);
    
      /* Deinitialize the Channel for new transfer */
      HAL_DMA_DeInit(&hdma_rx);
    
      /* Configure the DMA Channel */
      status = HAL_DMA_Init(&hdma_rx);
    
      /* NVIC configuration for DMA transfer complete interrupt */
      HAL_NVIC_SetPriority(DMA2_Channel4_IRQn, 6, 0);
      HAL_NVIC_EnableIRQ(DMA2_Channel4_IRQn);
    
      return (status);
    }
    
    /**
      * @brief Configure the DMA to transmit data to the SD card
      * @retval
      *  HAL_ERROR or HAL_OK
      */
    static HAL_StatusTypeDef SD_DMAConfigTx(SD_HandleTypeDef *hsd)
    {
      static DMA_HandleTypeDef hdma_tx;
      HAL_StatusTypeDef status;
    
      /* Configure DMA Tx parameters */
      hdma_tx.Init.Request             = DMA_REQUEST_7;
      hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
      hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
      hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
      hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
      hdma_tx.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    
      hdma_tx.Instance = DMA2_Channel4;
    
      /* Associate the DMA handle */
      __HAL_LINKDMA(hsd, hdmatx, hdma_tx);
    
      /* Stop any ongoing transfer and reset the state*/
      HAL_DMA_Abort(&hdma_tx);
    
      /* Deinitialize the Channel for new transfer */
      HAL_DMA_DeInit(&hdma_tx);
    
      /* Configure the DMA Channel */
      status = HAL_DMA_Init(&hdma_tx);
    
      /* NVIC configuration for DMA transfer complete interrupt */
      HAL_NVIC_SetPriority(DMA2_Channel4_IRQn, 6, 0);
      HAL_NVIC_EnableIRQ(DMA2_Channel4_IRQn);
    
      return (status);
    }

  • Also in the user section 4 of the main, override the bsp functions for tx and rx
//Override DMA write functions
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
{
  uint8_t sd_state = MSD_OK;

  // Invalidate the dma rx handle
  hsd1.hdmarx = NULL;

  // Prepare the dma channel for a read operation
  sd_state = SD_DMAConfigTx(&hsd1);

  if(sd_state == HAL_OK)
  {
    /* Write block(s) in DMA transfer mode */
    sd_state = HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks);
  }

  if( sd_state == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }

  return sd_state;
}

//Override DMA read functions
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks)
{
  uint8_t sd_state = MSD_OK;
  /* Invalidate the dma tx handle*/
  hsd1.hdmatx = NULL;

  /* Prepare the dma channel for a read operation */
  sd_state = SD_DMAConfigRx(&hsd1);

  if(sd_state == HAL_OK)
  {
       /* Read block(s) in DMA transfer mode */
        sd_state = HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks);
  }

  if( sd_state == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }
}

  1. Finally, change the DMA IRQ in stm32l4xx_it.c to use rx parameters or tx parameters depending on the status
/**
  * @brief This function handles DMA2 channel4 global interrupt.
  */
void DMA2_Channel4_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Channel4_IRQn 0 */

  /* USER CODE END DMA2_Channel4_IRQn 0 */
    //DMAFIX comment or delete HAL_DMA_IRQHandler(&hdma_sdmmc1);
  /* USER CODE BEGIN DMA2_Channel4_IRQn 1 */
  //DMAFIX separate irq handler by tx/rx, new code in irq handler
  if((hsd1.Context == (SD_CONTEXT_DMA | SD_CONTEXT_READ_SINGLE_BLOCK)) ||
     (hsd1.Context == (SD_CONTEXT_DMA | SD_CONTEXT_READ_MULTIPLE_BLOCK)))
      {
        //BSP_SD_DMA_Rx_IRQHandler();
       HAL_DMA_IRQHandler(hsd1.hdmarx);

      }
   else if((hsd1.Context == (SD_CONTEXT_DMA | SD_CONTEXT_WRITE_SINGLE_BLOCK)) ||
           (hsd1.Context == (SD_CONTEXT_DMA | SD_CONTEXT_WRITE_MULTIPLE_BLOCK)))
     {
        //BSP_SD_DMA_Tx_IRQHandler();
       HAL_DMA_IRQHandler(hsd1.hdmatx);
     }
  /* USER CODE END DMA2_Channel4_IRQn 1 */
}

note: each time yo regenerate with Cube Mx, you will need to go back and comment or delete the call to HAL_DMA_IRQHandler(&hdma_sdmmc1);

Hope it helps, I am not finish with that project, there might be a better way but so far this is what unblocked me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM