• 9.2CubeMx配置SD卡FATFS系统_stm32H7系列 SD卡 FR_NO_FILESYSTEM 找不到FatFs系统的问题


    重要说明:首先SD可以开启DMA读取或者单纯的SD的中断,但是其中优先级一定要为SD > SD DMA Rx/Tx > USB,不然当SD卡在读写的时候被其他中断打断,会直接导致U盘掉盘,中途卡顿

    问题现象:

    通过CubeMx配置SDMMC、Fatfs、使能内置DMA;此时配置生成工程可以正常挂载SD卡、访问文件;
    接着添加FMC,此时需要开启MPU内存保护单元、开启Cache;生成工程,此时【retSD = f_mount(&SDFatFS,SDPath,1);//挂载盘符A】会出现f_mount挂载SD卡返回没有文件系统FR_NO_FILESYSTEM,但是SD卡是有文件系统的,即使接着格式化SD卡你会出现问题(retSD = f_mkfs(SDPath,0,0,work,sizeof(work));),会返回无效参数FR_INVALID_PARAMETER。

    FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume /
    FR_INVALID_PARAMETER /
    (19) Given parameter is invalid */

    问题原因及解决方案:

    stm32h7系列的sd卡内置了dma部分,所以需要考虑字节对齐的问题。
    当我们在使用stm32cubemx生成代码时,没有字节对齐的选项;这时,就需要手动打开两个宏定义ENABLE_SD_DMA_CACHE_MAINTENANCE 、ENABLE_SCRATCH_BUFFER
    文件:sd_diskio.c

    /*
     * when using cacheable memory region, it may be needed to maintain the cache
     * validity. Enable the define below to activate a cache maintenance at each
     * read and write operation.
     * Notice: This is applicable only for cortex M7 based platform.
     */
    /* USER CODE BEGIN enableSDDmaCacheMaintenance */
    #define ENABLE_SD_DMA_CACHE_MAINTENANCE  1
    /* USER CODE END enableSDDmaCacheMaintenance */
    
    /*
    * Some DMA requires 4-Byte aligned address buffer to correctly read/write data,
    * in FatFs some accesses aren't thus we need a 4-byte aligned scratch buffer to correctly
    * transfer data
    */
    /* USER CODE BEGIN enableScratchBuffer */
    #define ENABLE_SCRATCH_BUFFER
    /* USER CODE END enableScratchBuffer */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    机器翻译:
    ENABLE_SD_DMA_CACHE_MAINTENANCE :当使用可缓存内存区域时,可能需要维护缓存有效性。启用下面的定义来激活每个缓存的维护读写操作。注意:这只适用于基于cortex M7的平台。
    ENABLE_SCRATCH_BUFFER:一些DMA需要4字节对齐的地址缓冲区来正确读写数据。在fatf中,一些访问不是这样,因此我们需要一个4字节对齐的刮擦缓冲区来正确传输数据
    此时就可以解决了
    参考:https://blog.csdn.net/kavieen/article/details/124025353

    CubeMx SD卡工程配置:

    #原理图

    在这里插入图片描述
    /**SDMMC1 GPIO Configuration
    PC8 ------> SDMMC1_D0
    PC9 ------> SDMMC1_D1
    PC10 ------> SDMMC1_D2
    PC11 ------> SDMMC1_D3
    PC12 ------> SDMMC1_CK
    PD2 ------> SDMMC1_CMD
    +Vmcu = +3.3V
    注:CD引脚用于检测SD卡插拔,按需添加

    CubeMx配置

    在这里插入图片描述
    在这里插入图片描述
    选择外部晶振25MHz
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    SDMMC时钟110MHz
    在这里插入图片描述
    在这里插入图片描述
    可以看到SDMMC需要小于25MHz
    SDMMC_CK frequency = sdmmc_ker_ck / [2 * CLKDIV].
    sdmmc_ker_ck = 110MHz
    所以CLKDIV 最大等于3
    SDMMC_CK frequency = 110MHz / (2*3) = 18.333MHz
    在这里插入图片描述
    配置SDMMC引脚所有都上拉
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    注:如果带RTOS,则必须使能FS_REENTRANT (Re-Entrancy)
    CODE_PANGE:也可以选Chinese(需要占用较大RAM)的。直接用英文的防止了cubeide报出RAM用完的错误
    FS_EXFAT:如果sd卡的格式不是fat32,而是exfat的话,这里一定要enable,不然会挂载失败,爆出FR_NOFILESYSTEM的错误
    FS_REENTRANT (Re-Entrancy) FS_REENTRANT (Re-Entrancy) Parameter Description:FS_REENTRANT选项切换FatFs模块的可重入性(线程安全)。—0:禁止重入。SYNC_t和FS_TIMEOUT不起作用。依赖:当freeertos被禁用时强制禁用。

    使能DMA
    在这里插入图片描述
    注意H7内置DMA,如果是其他型号,还需配置相对应的DMA
    比如F4的还需配置四这里的DMA在这里插入图片描述
    在这里插入图片描述
    添加个串口
    在这里插入图片描述
    生成工程

    工程源码编写:

    主要提示:cortex M7系列开启MPU和Cache后务必需要此操作:

    sd_diskio.c
    开启宏ENABLE_SD_DMA_CACHE_MAINTENANCE、ENABLE_SCRATCH_BUFFER

    /*
     * when using cacheable memory region, it may be needed to maintain the cache
     * validity. Enable the define below to activate a cache maintenance at each
     * read and write operation.
     * Notice: This is applicable only for cortex M7 based platform.
     */
    /* USER CODE BEGIN enableSDDmaCacheMaintenance */
    #define ENABLE_SD_DMA_CACHE_MAINTENANCE  1
    /* USER CODE END enableSDDmaCacheMaintenance */
    
    /*
    * Some DMA requires 4-Byte aligned address buffer to correctly read/write data,
    * in FatFs some accesses aren't thus we need a 4-byte aligned scratch buffer to correctly
    * transfer data
    */
    /* USER CODE BEGIN enableScratchBuffer */
    #define ENABLE_SCRATCH_BUFFER
    /* USER CODE END enableScratchBuffer */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    代码编写:

    添加已经写好的文件disk_driver.c、disk_driver.h
    disk_driver文件用于挂载SD卡文件系统、格式化、读写文件示例函数
    disk_driver.c

    /**********************************************************************
    *file:磁盘文件
    *author:残梦
    *versions:V1.0
    *date:2023.10.10
    *note:
    **********************************************************************/
    #include "disk_driver.h"
    #include "sdmmc.h"
    
    static int32_t Disk_Mount_SD(void);
    static int Disk_File_Read_SystemParameter(void);
    
    static status_EnumDef disk_state[eDisk_Num] = {eStatus_Invalid};//磁盘状态
    
    /****************************************
    @function:磁盘挂载
    @param:void
    @return:-1--失败,0--正常
    @note:
    ****************************************/
    int32_t Disk_Mount(void)
    {
      if(Disk_Mount_SD() < 0)
      {
        disk_state[eDisk_SD] = eStatus_Invalid;
        return -1;
      }else{disk_state[eDisk_SD] = eStatus_Valid;}
    
      //Disk_File_Read_SystemParameter();
      return 0;
    }
    
    /****************************************
    @function:挂载磁盘-SD卡
    @param:void
    @return:-1--失败,0--正常
    @note:
    ****************************************/
    static int32_t Disk_Mount_SD(void)
    {
      BYTE work[_MAX_SS];
      HAL_SD_CardInfoTypeDef SdCard;
    
      retSD = f_mount(&SDFatFS,SDPath,1);//挂载盘符A
      if(retSD == FR_NO_FILESYSTEM)//没有文件系统就格式化创建创建文件系统
      {
          retSD = f_mkfs(SDPath,0,0,work,sizeof(work));
          if(retSD == FR_OK)
          {
              retSD = f_mount(NULL,SDPath,1);//格式化后,先取消挂载
              retSD = f_mount(&SDFatFS,SDPath,1);//挂载
          }
          else//格式化失败
          {
            printf("Description Failed to format the SD card...%d\n",retSD);
            goto SD_FAIL;
          }
      }
      else if(retSD != FR_OK)//挂载失败
      {
        printf("Mount failure=%d\n",retSD);
        goto SD_FAIL;
      }
    
      retSD = f_mount(&SDFatFS,SDPath,1);
      if(retSD != FR_OK){printf("f_mount():retSD=%d\n",retSD);goto SD_FAIL;}
    
      if(HAL_SD_GetCardInfo(&hsd1,&SdCard) != HAL_OK){printf("HAL_SD_GetCardInfo()\n");goto SD_FAIL;}
      printf("SD卡容量:%.2fGB\n",(float)((uint64_t)SdCard.BlockNbr * (uint64_t)SdCard.BlockSize / 1024.0f / 1024.0f / 1024.0f));
    
      return 0;
      SD_FAIL:
      {
        printf("Error[Disk_Mount_SD()]:The disk fails to be mounted...\n");
        return -1;
      }
    }
    
    /****************************************
    @function:获取磁盘状态
    @param:void
    @return:见status_EnumDef
    @note:
    ****************************************/
    status_EnumDef Disk_Status_Get(Disk_List_EnumDef disk)
    {
        return disk_state[disk];
    }
    
    /****************************************
    @function:读取板参数文件
    @param:
    @return:-1--读取失败,0--成功
    @note:
    ****************************************/
    static int Disk_File_Read_SystemParameter(void)
    {
        FRESULT res_sd;
        UINT fnum;
        char string[200];
        int32_t ByteNum = 0,value = 0;
        uint32_t line = 0;
    
        if(!Disk_Status_Get(eDisk_SD))return -1;
        memset(string,0,sizeof(string));
        sprintf(string,"%sSystemParameter.txt",SDPath);
        res_sd = f_open(&SDFile, string, FA_OPEN_EXISTING | FA_READ);
        if(res_sd == FR_NO_FILE)//文件不存在
        {
            printf("file does not exist:%s\n",string);
    
            //创建默认文件
            res_sd = f_open(&SDFile, string,FA_CREATE_ALWAYS | FA_WRITE );
            if(res_sd != FR_OK)
            {
                printf("[%d]:Failed to create the file!%s\n",res_sd,string);
                return -1;
            }
    
            memset(string,0,sizeof(string));
            sprintf(string,"%s\n",bsp_BoardVersion);
            ByteNum = strlen(string);
            res_sd=f_write(&SDFile,string,ByteNum,&fnum);
            if(&SDFile != NULL){res_sd = f_close(&SDFile);}
        }
        else if(res_sd != FR_OK)
        {
            printf("[%d]:File opening failure!%s\n",res_sd,string);
            return -1;
        }
    
        line = 0;
        while(!(f_eof(&SDFile)))
        {
            memset(string,0,sizeof(string));f_gets(string,sizeof(string),&SDFile);if(strlen(string) == 0){break;}
            switch(line++)
            {
                case 0:
                {
                    //sscanf(string,"RemoteControlID[%d]:{set range:0xFA-0xFD}\n",&value);
                    printf("%s\n",string);
                }break;
                default:break;
            }
        }
        if(&SDFile != NULL){res_sd = f_close(&SDFile);}
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150

    disk_driver.h

    #ifndef _disk_driver_H_
    #define _disk_driver_H_
    #ifdef __cplusplus
    extern "C" {
    #endif
    #include "main.h"
    #include "fatfs.h"
    
    typedef enum
    {
      eDisk_SD = 0,
      eDisk_Num
    }Disk_List_EnumDef;//磁盘资源列表
    
    int32_t Disk_Mount(void);
    status_EnumDef Disk_Status_Get(Disk_List_EnumDef disk);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    补充:

    typedef enum
    {
        eStatus_Invalid = 0,
        eStatus_Valid = 1
    }status_EnumDef;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    主函数main.c添加头文件并调用挂载SD卡系统函数

    if(Disk_Mount() < 0){Error_Handler();}
    
    • 1

    在这里插入图片描述

  • 相关阅读:
    Linux下编写一个C语言程序
    Linux中通配符、shell元字符、转义符
    阿里云3M固定带宽服务器速度快吗?是否够用?
    【无标题】
    LVGL库入门教程01-移植到STM32(触摸屏)
    AIGC Midjourney 指令生成高清图像及参数提示词
    Nginx HA高可用实现记录
    【Apollo】Apollo的入门介绍
    内网渗透之Linux反弹shell(一)
    迎战秋招计划
  • 原文地址:https://blog.csdn.net/qq_36561846/article/details/133808890