STM32L476 和 CubeMX 上带有 DMA 的 SD 卡没有中断

Posted

技术标签:

【中文标题】STM32L476 和 CubeMX 上带有 DMA 的 SD 卡没有中断【英文标题】:No interrupt in SDcard with DMA on STM32L476 and CubeMX 【发布时间】:2021-10-29 00:45:29 【问题描述】:

我有一个未解决的 SD 卡问题。

该项目基于 STM32L476,IDE 是 Atollic,配置由 STM32CucbeMX 进行(就像我对所有设计一样)。

没有 DMA(FATFS 中没有 DMA 模板,也没有配置 DMA 通道),SD 卡可以工作。当我使用 DMA 时(见屏幕截图),第一次读取操作卡住了,在 30 秒超时后出现错误。

我在代码内部进行了调查,我有证据表明在 HAL_SD_ReadBlocks_DMA() 之后没有任何中断。这就是超时的原因。该函数本身没有返回错误,因此它认为数据传输正在运行,但事实并非如此。

中断是这样配置的:

CubeMX 版本为 6.2.1。并且更新了 STM32L4 包。

我的看法是库或 Cube 生成的代码中存在一些错误。我看过其他(罕见的)关于类似问题的帖子,怀疑是 libray 错误。

这对我来说非常尴尬,因为我有一个重要的设计正在等待 SD 卡全速工作。

没有 DMA,正如我所说,SD 卡可以工作,但写入速度太低,我必须以非常低的频率运行接口以避免写入错误。我想通过 DMA 的读/写操作会给我所需的速度,但是这个中断问题让所有事情都停滞不前。

关于非工作中断有什么想法吗?

非常感谢

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,这是我的解决方法。

这个解决方案是基于我在 stm32 论坛上找到的一个解决方案,但我尝试移动一些东西以使其易于实施,这样每次使用 cube mx 重新生成时都不会丢失所有内容。我将覆盖函数放在 main 中,但我认为它们也可以放在 bsp 文件的用户部分中。

作为参考,我从这个例子开始,然后应用了下面的修复 https://community.st.com/s/article/how-to-create-a-file-system-on-a-sd-card-using-stm32bubeide?t=1637759710161

修复步骤:

    在 cubeMX 中,在通道 4 上使用单个 DMA,而不是 1 用于 Rx,1 用于 Tx。

    在 main.c 中

    为 Tx 和 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 */
每次 tx 或 rx 为 rx 或 tx 重新配置 DMA 时都会调用这些函数
        /* 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);
    

同样在 main 的用户部分 4 中,覆盖 tx 和 rx 的 bsp 函数
//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;
  


    最后,将 stm32l4xx_it.c 中的 DMA IRQ 更改为根据状态使用 rx 参数或 tx 参数
/**
  * @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 */


注意:每次使用 Cube Mx 重新生成时,您都需要返回并注释或删除对 HAL_DMA_IRQHandler(&hdma_sdmmc1) 的调用;

希望它有所帮助,我还没有完成那个项目,可能有更好的方法,但到目前为止,这就是我解除障碍的原因。

【讨论】:

以上是关于STM32L476 和 CubeMX 上带有 DMA 的 SD 卡没有中断的主要内容,如果未能解决你的问题,请参考以下文章

STM32L476应用开发之一:初次使用

STM32L476 flash 页擦除没有效果

STM32L476应用开发之二:模拟量数据采集

最终启动序列中的错误 - STM32L476 的 Eclipse 系统工作台调试

STM32L476的RTC使用问题记录

stm32l476 HAl GPIO Init将程序计数器发送到位置0000 0000