FATFS文件系统返回FR_DISK_ERROR错误的解决方案

问题描述:
我的团队一直在处理一个包含基于标准库的 SD 卡的项目。最近我们决定迁移到 HAL 并开始了。
幸运的是,我们项目的所有部分都尽可能地更改为 HAL,它们运行良好,但我们不知道为什么 SD 卡不能正常运行。
我们没有更改外设的配置时钟,但我们必须在 HAL 中将“SDMMC 控制器的时钟频率”更改为 1.5MHz,而在 STDLibrary 中为 24MHz。因为,它根本不起作用。
此外,我们的客户正在使用多种 SD 卡类型,所有这些都可以,但不是很好。我的意思是,FR_DISK_ERR 在工作期间返回了很多,但我们的设备试图获取FR_OK。
不幸的是,我们总是在某些 SD 卡中收到FR_DISK_ERR,而它在我们的 STDLibrary 版本中一直有效。
此外,我们发现如果“f_mount”函数被调用一次,然后您将 SD 卡取出并重新放入,它将永远无法工作,直到您重置您的微控制器。

我的微控制器
STM32F427VI
和 SDIO 配置如下:

 hsd.Instance = SDIO;
 hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
 hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
 hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
 hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
 hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
 hsd.Init.ClockDiv = 14;

它适用于 4 位宽的总线。
另外,我设备的时钟是 96MHz,“APB2 外设时钟”是 48MHz。
已编辑:
至于重新插入卡 - 当 f_open 返回 FR_DISK_ERR 时,我通过调用 f_mount 再次进行了初始化。我一直这样做直到给FR_OK,但在这种情况下它从未返回FR_OK。
我意识到 f_mount 没有像亲爱的 Jacek Ślimok 所说的那样第二次初始化 SDIO。
因为有一个标志不允许再次调用“SD_initialize”(SD_initialize 函数包括 BSP_SD_Init)。
这是 diskio.c 的代码:

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
  DSTATUS stat = RES_OK;

  if(disk.is_initialized[pdrv] == 0)
  {
    disk.is_initialized[pdrv] = 1;
    stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]);
  }
  return stat;
}

现在我在调用 f_mount 之前使用了 SD_PowerON 和 SD_InitCard,现在它可以正常工作了。 这是一个错误,不是吗?

但其他问题仍然存在。它根本不适用于 24MHz 时钟,并且某些 SD 卡仍会返回 FR_DISK_ERR。

已编辑:
最后,当我将我的 HAL 库更新到 STM32Cube_FW_F4_V1.24.2 时,它起作用了。但是 HAL 仍然不如标准外设好用。例如,我还不能将“ClockDiv”设置为“0”(24MHz)。它根本不起作用。现在我将“ClockDiv”设置为“1”(16MHz),这对我的项目来说还不够好,但我必须这样做。或者,如果您将 SDCard 拿走并在程序运行时再次插入,您将无法使用 f_mount 初始化 FATFS。它根本行不通。您必须自己重新初始化 SDIO Peripheral。不幸的是,现在我没有时间详细了解我的日程安排。也许在未来。
【问题讨论】:
至于重新插入卡 - 那是因为它需要再次初始化,然后f_mount-ed。通过初始化,我并不是指 GPIO / DMA 初始化(您可能会忽略它并摆脱它),而是在插入卡后但在挂载文件系统之前需要再次发送到卡的 SD 命令。特别是 - 请参阅 HAL_SD_InitCard 和内部发送 SD 命令的函数 - 主要是 SD_PowerON 和 SD_InitCard。
【参考方案1】:
disk.is_initialized[0] =0; // 强制重新初始化
安装前。这是支持热插拔 sd 卡的简单方法。

### 如何在 FatFS 文件系统中实现文件删除 FatFS 提供了一个简单的接口来操作文件和目录,其中 `f_unlink` 函数可以用来删除单个文件或空文件夹。以下是具体方法及其代码示例。 #### 使用 f_unlink 删除文件 当目标对象是一个单独的文件时,可以直接调用 `f_unlink` 来完成删除操作。该函数接受一个参数——要删除的目标路径字符串(包括文件名)。如果删除成功,则返回 FR_OK;否则会返回错误码[^1]。 ```c #include "ff.h" FRESULT res; const char* path = "/my_file.txt"; // 定义待删除文件的路径 res = f_unlink(path); if (res != FR_OK) { printf("Delete file failed with error code: %d\n", res); } else { printf("File deleted successfully.\n"); } ``` #### 使用 f_unlink 删除空文件夹 对于不包含任何子项的空文件夹同样适用上述方式。只需指定对应的文件夹路径即可。 ```c #include "ff.h" FRESULT res; const char* dir_path = "/empty_dir"; // 待删除的空文件夹路径 res = f_unlink(dir_path); if (res != FR_OK) { printf("Delete directory failed with error code: %d\n", res); } else { printf("Directory deleted successfully.\n"); } ``` #### 处理非空文件夹的情况 然而,在面对含有子项目的复杂结构化数据集时,单纯依靠一次性的 unlink 调用无法满足需求。此时需先遍历并逐一清理内部成员后再执行最终移除动作。 下面展示了一种递归算法思路用于解决此类场景: ```c #include "ff.h" #include <string.h> // 辅助功能:打印 FRESULT 错误消息 void print_fresult(FRESULT fr){ switch(fr){ case FR_OK : printf("FR_OK"); break; case FR_DISK_ERR : printf("FR_DISK_ERR"); break; ... default : printf("Unknown Error (%u)",fr);break; } } int delete_recursive(const TCHAR *path, BYTE is_directory); /* 主入口 */ int main(){ FRESULT result; result=delete_recursive("/non_empty_folder",1); if(result==FR_OK){ puts("All contents removed and folder itself also."); }else{ putchar('[');print_fresult(result);putchar(']'); } return 0; } /* 实现部分 */ int delete_recursive(const TCHAR *path,BYTE is_directory){ DIR d_obj,*dp=&d_obj; FILINFO fi; TCHAR sub_path[_MAX_LFN+1]; int ret=-1,i,j,len=strlen(path),slash=(BYTE)(path[len-1]=='/'); if(is_directory && !(fi.fattrib&AM_DIR)){ f_findfirst(dp,&fi,path,"*",NULL,NULL)==FR_NO_FILE?ret=0:(ret=-2);// Not a valid DIR? goto exit_point; } for(f_findfirst(dp,&fi,(is_directory?(TCHAR*)path:""),"*.*",NULL)!=FR_OK&&(ret==-1)?(ret=f_error(dp)):((ret>=0)&&(strlen(fi.fname)>0))){ if(!strcmp(fi.fname,".") || !strcmp(fi.fname,"..")) continue;// Skip self & parent links. i=len+j=(BYTE)((!slash&&is_directory)?1:0)+strlen(fi.fname)+(BYTE)(fi.fattrib&AM_DIR); memcpy(sub_path,is_directory?(TCHAR*)path:"",i=j+=len); sub_path[j++]='/'; strcpy(&sub_path[j],fi.fname); ret=((fi.fattrib&AM_DIR)? delete_recursive(sub_path,1):( f_unlink(sub_path)==FR_OK?0:-3)); if(ret<0){ goto exit_point;} } exit_point: if(is_directory && slash && ((ret>=0)||(ret==-2)) ){ ret=f_unlink(path)==FR_OK ? 0 :-4 ; } return ret; } ``` 此段代码定义了一个名为 `delete_recursive` 的辅助函数,它能够处理任意深度嵌套的目录树形结构下的所有元素清除任务,并且最后还会尝试销毁顶层容器本身^。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值