FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.(写的好)
来源: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解决方法.(写的好)相关推荐
- FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.
原文地址:http://www.devlabs.cn/?p=226 18 一月 2014 3 起因 去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工 ...
- stm32 Fatfs 读写SD卡
源:stm32 Fatfs 读写SD卡 转载于:https://www.cnblogs.com/LittleTiger/p/4864052.html
- android 清空数组缓存,Android数据持久化之读写SD卡中内容的方法详解
本文实例讲述了Android数据持久化之读写SD卡中内容的方法.分享给大家供大家参考,具体如下: 前面文章里讲的那三个方法:openFileOutput.openFileInput虽然都能通过流对象O ...
- STM32CubeMX+SPI+FATFS读写SD卡
一.软件硬件说明 软件:STM32CubeMX V6.6.1 /KEIL5 V5.29 硬件:正点原子mini开发板,SD卡,通过SPI方式驱动SD卡,用的是SPI1接口 以上内容来源于正点原子min ...
- 海思IPC平台快速拔插SD卡会出现SD卡不识别解决方法
内核需要定时检测SD卡是否插入或拔出,默认给的定时检测时间为200ms. 此定时检测时间也可通过配置内核menuconfig更改.配置路径及配置选项如下: Device Drivers ---> ...
- VS code+STM32CubeMX 使用 FreeRTOS+FatFS+USB_DEVICE 搭建 SD卡 读卡器 和 读写 SD卡 示例项目
文章目录 1. 新建项目 2. 配置 CubeMX 项目 3. 配置 EIDE 项目 4. 编写代码 5. 编译下载 6. 效果展示 本例介绍如何使用 vscode 插件 EIDE 和 STM32Cu ...
- Android读写SD卡
Android读写SD卡 0. 参考 解决各版本安卓读写SD卡的问题-java.io.IOException: Operation not permitted问题(兼容到android13) 1. 安 ...
- 掌握与SD卡“交流”的方法,轻松完成单片机读写SD卡的底层驱动程序
从大二开始写代码至今已经五六年了,之前做过很多嵌入式项目,参加过很多竞赛:慢慢才发现之前很多是知其然不知其所以然,很多东西都是从CSDN,论坛,各个网站学习过来的:工作以后才发现,做出一个东西并不难, ...
- STM32F1 HAL库读写SD卡的操作要点
本文采用的HAL库版本为STM32Cube_FW_F1_V1.8.0(带Patch-CubeF1 1.8.4). 知识点一:SD卡数据线位宽的配置 SD卡可以采用1位数据线模式,也可以采用4位数据线模 ...
最新文章
- Slave: received end packet from server, apparent master shutdown
- 它又来了!Fastjson 被发现其用于安全控制的开关autotype限制可被绕过...你方了没?...
- 分库分表介绍和Sharding-JDBC快速入门
- python实现文件上传和下载_[Python] socket实现TFTP上传和下载
- Spotlight on oracle 使用
- python安装报错类型_Python处理验证码第一篇(pytesser初探及报错处理)
- 为什么 Linux 默认页大小是 4KB
- 全奖博士招生,美国中佛罗里达大学计算机视觉研究中心
- 云端远程Ubuntu系统进行无桌面Web浏览器自动化测试
- 初学者python笔记(元组、字典、集合详解)
- Kotlin入门(18)利用单例对象获取时间
- 超三十万台设备感染银行木马、远程代码漏洞可攻击云主机|12月7日全球网络安全热点
- H3C Wx5004无线控制器修改SSID名称
- SQL基础系列(三)——分组查询
- C# 按块选择 autoCAD二次开发
- 三星i919u android 6,SCH-I919U
- web前端学习之——页面美妆师css3基础篇
- 多组数据求最大公约数
- eval函数 c语言,matlab中eval函数的用法
- Django 使用QSL server数据库