来源:http://www.devlabs.cn/?p=226

起因

去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工, 但好歹能工作. 后来没时间也没心情了, 就搁在一边, 没再管. 前几天又找出来, 想着弄稳定了, 过年回家的时候玩一下, 结果发现居然不工作了. 想当初调试的时候折腾的死去活来, 走了无数弯路, 现在说不工作就不工作, 心里各种不服, 加了一堆调试信息输出后, 发现SD卡初始化是正常的, 但在读写的时候返回FR_NO_FILESYSTEM. 于是找出仿真器, 跟踪了一下, 另外祭出神器WinHex, 终于找出问题所在.

环境:
MPU: MSP430F5418.
SD Card: SanDisk 1G T-Flash.
Interface: 硬件SPI.
FatFs: 0.09

问题现像: 在电脑上读写正常的SD卡, 使用FatFs读写不正常, 返回的错误类型为FR_NO_FILESYSTEM.
问题原因: 如果你移植时SD卡低层读写函数正确的话, 就不是你的问题. 具体下面会分析.
解决方法: 1. 使用FatFs自带的f_mkfs()函数格式化SD卡, 但注意底层函数的正确(disk_ioctl()), 要不会出现莫名其妙的问题, 比如将你1G的卡格式化为30M.
                  2. 使用FatFs V0.10.

*本人注释*

f_mkfs() 应在 f_mount()之后,否则出现,无工作区的错位。不经意容易犯错。呵呵。

FR_NOT_ENABLED

逻辑驱动器没有工作区。

基本知识.

1.从MBR说起.
MBR(Master Boot Record, 主引导记录)位于SD卡的第0扇区(物理), 共512个字节. 其中前446个字节为引导代码, 接下来64个字节为分区表, 再接下来两个字节为签名, 固定为 0×55, 0xAA.

下图是我的SD卡的MBR:

64个字节的分区表分为4组, 每16字节为一组, 每一组描述一个分区. 这16个字节数据的意义在此不做详述, 只须了解偏移为0H, 4H, 8H处的数据即可.

偏移 0x00H 起的一个字节表示分区状态, 0×00表示非活动状态, 0×80表示活动状态. 其它数值无意义.
偏移 0x04H 起的一个字节表示文件系统类型, 常见的有: 01, FAT32; 06, FAT16; 07, NTFS. 0为法值.
偏移 0x08H 起的4个字节数据表示相对扇区数, 即从磁盘开始到该分区开始的偏移量, 以扇区计算. 此数值指向的物理扇区也就是逻辑扇区0. 比如此数值为63, 则第63个物理扇区为逻辑扇区0.

2.DBR(Dos Boot Record, Dos引导记录).
DBR位于逻辑扇区0, 属于FAT文件系统的一部分, 记录着文件系统的相关信息.
类似MBR, DBR也由几部分组成, 如下:
    偏移        字段长度        名称
    0×00        3字节           跳转指令
    0×03        8字节           厂商标志和OS版本号
    0x0B        53字节         BPB(BIOS Parameter Bblock)
    0×40        26字节          扩展BPB
    0x5A        420字节       引导程序代码
    0x1FE       2字节           签名, 0×55, 0xAA
我不打算研究文件系统, 所以这些数据不用去管它, 我们唯一需要了解的是, 当SD卡被格式化为FAT文件系统后, 会在DBR的BPB或者扩展BPB中出现FAT字样.

下图是我的SD卡的DBR:

基本知识到此结束. 如果想进一步研究FAT文件系统, 可参考本文末尾给的的链接.

FatFs检测文件系统的手段.

既然FatFs提示无文件系统, 那FatFs是通过何种手段来检测是否存在文件系统的呢?
在FatFs中有一个check_fs()函数(第1981行 – 第1998行)用来检测FAT文件系统的有效性, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static BYTE check_fs (    /* 0:FAT-VBR, 1:Valid BR but not FAT, 2:Not a BR, 3:Disk error */FATFS *fs,    /* File system object */DWORD sect    /* Sector# (lba) to check if it is an FAT boot record or not */
)
{if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK)    /* Load boot record */return 3;/* Check record signature (always placed at offset 510 even if the sector size is >512) */if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)return 2;if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)    /* Check "FAT" string */return 0;if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)return 0;return 1;
}

此函数先读取了指定的扇区, 然后执行相应的检查.
BS_FilSysType和BS_FilSysType2均为常数宏定义, 值分别为54和82, 代表了”FAT”字样出现在DBR中的偏移量.
将0×544146拆分为三个8位数据, 转为ASCII码, 即为FAT.
可见FatFs就是通过检测DBR的BPB和扩展BPB中是否存在FAT字样来判断目标文件系统是否为FAT文件系统的.

另外注意函数的返回值:
0为有效的FAT分区;
1为BR(Boot Record)有效但并不是FAT分区.
2为BR无效.
3为读取数据出现错误.
 
既然SD卡经过正确的格式化, 那么DBR一定不会出错, 所以问题不会在这里.

接下来找到调用check_fs()的地方, 从chk_mounted()函数(第2007行)的2066行开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */
fmt = check_fs(fs, bsect = 0);      /* Load sector 0 and check if it is an FAT-VBR (in SFD) */
if (LD2PT(vol) && !fmt) fmt = 1;    /* Force non-SFD if the volume is forced partition */
if (fmt == 1) {                     /* Not an FAT-VBR, the physical drive can be partitioned *//* Check the partition listed in the partition table */pi = LD2PT(vol);if (pi) pi--;tbl = &fs->win[MBR_Table + pi * SZ_PTE];    /* Partition table */if (tbl[4]) {                               /* Is the partition existing? */bsect = LD_DWORD(&tbl[8]);              /* Partition offset in LBA */fmt = check_fs(fs, bsect);              /* Check the partition */}
}
if (fmt == 3) return FR_DISK_ERR;
if (fmt) return FR_NO_FILESYSTEM;               /* No FAT volume is found */

解释一下这段代码.
如果没有定义_MULLTI_PARTITION的话, 则LD2PT(vol)值始终为0(参照LDPT的宏定义).
这里我们只有一个分区, 所以此值为0.
fmt = check_fs(fs, bsect = 0)对SD卡的0扇区(物理)执行文件系统有效性检查. 如果SD卡无MBR, 即第0扇区就为DBR,且文件系统正确的话, 那么第二个if将不会再执行.
如果SD卡有MBR, 那么函数必定返回1, 那么下面的函数将会从MBR的分区表中查找到逻辑扇区0(即第一个分区DBR所在的物理扇区), 再次进行文件系统有效性检测.
逻辑很完美, 但问题恰恰出在这里.

注意这几行代码:

1
2
3
4
5
6
7
pi = LDPT(vol);
if (pi) pi--;
tbl = &fs->win[MBR_Table + pi * SZ_PTE];        // 得到一个分区表项 SE_PTE = 16
if (tbl[4]) {                                   // 如果存在文件系统bsect = LD_DWORD(&tbl[8]);                  // 得到分区的DBR所在位置(物理扇区)fmt = check_fs(fs, bsect);                  // 执行检测
}

前面说过, LDPT(vol) 的值始终为0, tbl[4](即分区表第4个字节)指的是文件系统类型, 不可为0.
如果SD卡存在MBR, 则从MBR的第一个分区表项中得到第一个分区的相关信息, 如果此分区存在文件系统的话(检查第4个字节得到), 通过分区表项找到分区的DBR位置, 再次进行文件系统有效性检查.

以上就是这段代码的所有的逻辑.
看起来还是没有问题.

是的, 代码是没有问题, 但是SD卡会耍流氓, 不, 应该是格式化SD卡的软件会耍流氓.
一般我们不会给SD卡分区, 所以SD卡只有一个分区. 如果SD卡有MBR(一般都会有), 那意味着将会有4个分区表项. 重点就在这里. 按道理说SD卡分区相关数据应该在MBR的第一个分区表项里面, 但是, 这些数据存在于第二个分区表项或者第三个或者第四个都没有问题, PC都是可以正确认出来的, FatFs不行. 如果分区数据正好在第4个分区表项里, PC识别是没有问题的, FatFs傻乎乎的只会去检查第一个分区表项, 然后就杯具了, 只好报告FR_NO_FILESYSTEM.

如果你仔细看图一的话你就会发现, 我的这张SD卡的分区相关数据正好就在MBR的第4个分区表项里!

更糟糕的时, 就算你使用Windows(我使用的是Win7 32bit sp1)将SD卡再格式化一次, 分区表是不会改变的. 所以只好使用FatFs自带的f_mkfs()再格式化一次了, 或者, 你可以使用WinHex手工更改一下(我就是这么干的), 再或者, 自己动手更改FatFs源代码.

关于我的SD卡为什么会出现这种情况:
记得前几个月给电脑更新了一下BIOS, 正好使用这张SD卡做引导, 所以应该是更改BIOS的程序制作引导磁盘的时候”闯的祸”.

后记:
打算向ELM ChaN反映这个问题, 于是到FatFs的官网转了一圈, 发现FatFs已经更新到V0.10了, 下载下来一看, 此问题已经得到解决, 所以解决此问题又多了一个选择: 使用FatFs V0.10.

声明: 原创文章, 转载请注明出处. 若有问题, 请留言指出, 不胜感激.

相关链接:

FAT文件系统原理

维基百科–主引导记录

FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.(写的好)相关推荐

  1. FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.

    原文地址:http://www.devlabs.cn/?p=226 18 一月 2014 3 起因 去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工 ...

  2. stm32 Fatfs 读写SD卡

    源:stm32 Fatfs 读写SD卡 转载于:https://www.cnblogs.com/LittleTiger/p/4864052.html

  3. android 清空数组缓存,Android数据持久化之读写SD卡中内容的方法详解

    本文实例讲述了Android数据持久化之读写SD卡中内容的方法.分享给大家供大家参考,具体如下: 前面文章里讲的那三个方法:openFileOutput.openFileInput虽然都能通过流对象O ...

  4. STM32CubeMX+SPI+FATFS读写SD卡

    一.软件硬件说明 软件:STM32CubeMX V6.6.1 /KEIL5 V5.29 硬件:正点原子mini开发板,SD卡,通过SPI方式驱动SD卡,用的是SPI1接口 以上内容来源于正点原子min ...

  5. 海思IPC平台快速拔插SD卡会出现SD卡不识别解决方法

    内核需要定时检测SD卡是否插入或拔出,默认给的定时检测时间为200ms. 此定时检测时间也可通过配置内核menuconfig更改.配置路径及配置选项如下: Device Drivers ---> ...

  6. VS code+STM32CubeMX 使用 FreeRTOS+FatFS+USB_DEVICE 搭建 SD卡 读卡器 和 读写 SD卡 示例项目

    文章目录 1. 新建项目 2. 配置 CubeMX 项目 3. 配置 EIDE 项目 4. 编写代码 5. 编译下载 6. 效果展示 本例介绍如何使用 vscode 插件 EIDE 和 STM32Cu ...

  7. Android读写SD卡

    Android读写SD卡 0. 参考 解决各版本安卓读写SD卡的问题-java.io.IOException: Operation not permitted问题(兼容到android13) 1. 安 ...

  8. 掌握与SD卡“交流”的方法,轻松完成单片机读写SD卡的底层驱动程序

    从大二开始写代码至今已经五六年了,之前做过很多嵌入式项目,参加过很多竞赛:慢慢才发现之前很多是知其然不知其所以然,很多东西都是从CSDN,论坛,各个网站学习过来的:工作以后才发现,做出一个东西并不难, ...

  9. STM32F1 HAL库读写SD卡的操作要点

    本文采用的HAL库版本为STM32Cube_FW_F1_V1.8.0(带Patch-CubeF1 1.8.4). 知识点一:SD卡数据线位宽的配置 SD卡可以采用1位数据线模式,也可以采用4位数据线模 ...

最新文章

  1. Slave: received end packet from server, apparent master shutdown
  2. 它又来了!Fastjson 被发现其用于安全控制的开关autotype限制可被绕过...你方了没?...
  3. 分库分表介绍和Sharding-JDBC快速入门
  4. python实现文件上传和下载_[Python] socket实现TFTP上传和下载
  5. Spotlight on oracle 使用
  6. python安装报错类型_Python处理验证码第一篇(pytesser初探及报错处理)
  7. 为什么 Linux 默认页大小是 4KB
  8. 全奖博士招生,美国中佛罗里达大学计算机视觉研究中心
  9. 云端远程Ubuntu系统进行无桌面Web浏览器自动化测试
  10. 初学者python笔记(元组、字典、集合详解)
  11. Kotlin入门(18)利用单例对象获取时间
  12. 超三十万台设备感染银行木马、远程代码漏洞可攻击云主机|12月7日全球网络安全热点
  13. H3C Wx5004无线控制器修改SSID名称
  14. SQL基础系列(三)——分组查询
  15. C# 按块选择 autoCAD二次开发
  16. 三星i919u android 6,SCH-I919U
  17. web前端学习之——页面美妆师css3基础篇
  18. 多组数据求最大公约数
  19. eval函数 c语言,matlab中eval函数的用法
  20. Django 使用QSL server数据库

热门文章

  1. 也就是一些简单的分享
  2. 100天精通Python(可视化篇)——第94天:Pyecharts绘制多种炫酷散点图(参数说明+代码实战)
  3. AIR, 我已经对你彻底失望了.
  4. 腾讯管家和360安全卫士比较谁更流氓
  5. Date日期格式修改
  6. [教程]KALI LINUX 2.0 2021 更新国内源
  7. 爬虫日记之01编辑系统环境变量
  8. Android设备网络数据流量统计
  9. AIX之SMIT介绍
  10. 详解clientWidth,scrollWidth,offsetWidth,innerWidth,outerWidth