SD卡应用总结(Fatfs)

转载自:SD卡应用总结(FatFs)

  对于SD卡的应用,想必大家都尝试多。不过,很多网友恐怕只停留在实验的基础上吧。对于SD卡在文件系统下或者不带文件系统下,对SD卡的操作都是很简单的。是的,只是简单的文件读写确实不难。但是,如果每秒钟不停的写数据,而且是不停的工作,恐怕SD卡的应用就没有这么简单了吧,有时总会出现一些莫名其妙的问题。

  不知道大家是否遇到过这些问题?本人开发了几个关于SD卡的项目,例如,定时拍照、定时录音等。对于这样的项目,基本上要求每一秒都在不停的写数据,而且一般一天工作好几个小时,甚至会不停的工作。在这些项目中,本人遇到太多的问题,下面把遇到的问题及解决方法与大家分享,希望有同样经验的网友一起分享一下您的经验。

--------------------------------------------------------------------------------------------------------

问题1:根目录下文件毁坏。
    
     现象:在FatFs下可以读写文件,可在PC上无法打开目录,提示文件毁坏。

分析:通过WinHex软件打开磁盘,发现目录完全正常,但是FAT表已经毁坏,引起的原因可能是带电插拔。

解决:既然是FAT表与目录对不上,而且FAT毁坏,就是用PC修复也只会删除这些文件,对于我们的单片机来说,也没有好的解决方法,那就格式吧。

下面的代码用于判断FAT表是否和文件目录对应的上,使用的方法是:扫描FAT表,看看应用了多少簇,在通过读取FSInfo扇区的信息,看这两者是否一致。一致时为正确,不一致一般有问题。

 1 /*-----------------------------------------------------------------------*/
 2 /* File system check                                                     */
 3 /*-----------------------------------------------------------------------*/
 4
 5 FRESULT f_fsCheck(
 6 const TCHAR *path,    /* Pointer to the logical drive number (root dir) */
 7 DWORD *nclst,        /* Pointer to the variable to return number of free clusters */
 8 FATFS **fatfs        /* Pointer to pointer to corresponding file system object to return */
 9 )
10 {
11     FRESULT res;
12     FATFS *fs;
13     DWORD n, clst, sect, stat;
14     UINT i;
15     BYTE fat, *p;
16
17     /* Get drive number */
18     res = chk_mounted(&path, fatfs, 0);
19     fs = *fatfs;
20     if (res == FR_OK)
21     {
22         /* Get number of free clusters */
23         fat = fs->fs_type;
24         n = 0;
25         if (fat == FS_FAT12)
26         {
27             clst = 2;
28             do {
29                 stat = get_fat(fs, clst);
30                 if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
31                 if (stat == 1) { res = FR_INT_ERR; break; }
32                 if (stat == 0) n++;
33             } while (++clst < fs->n_fatent);
34         }
35         else
36         {
37             clst = fs->n_fatent;
38             sect = fs->fatbase;
39             i = 0; p = 0;
40             BYTE cnt = 0;
41             do{
42                 if(!i)
43                 {
44                     res = move_window(fs, sect++);
45                     if (res != FR_OK) break;
46                     p = fs->win;
47                     i = SS(fs);
48                 }
49                 if (fat == FS_FAT16)
50                 {
51                     if (LD_WORD(p) == 0) n++;
52                     p += 2; i -= 2;
53                 }
54                 else
55                 {
56                     if ((LD_DWORD(p) & 0x0FFFFFFF) == 0)
57                     {
58                         if (++cnt > 10)    // 连续10个空簇,退出
59                         {
60                             break;
61                         }
62                     }
63                     else
64                     {
65                         n++;
66                         cnt = 0;
67                     }
68                     p += 4; i -= 4;
69                 }
70             } while (--clst);
71         }
72
73         if (fs->last_clust > (n+10))
74         {
75             res = FR_INT_ERR;
76         }
77     }
78     LEAVE_FF(fs, res);
79 }

FatFs中并没有这个函数,是本添加的。后面我们可以调用这个函数实现FAT检查功能。

 1 /**************************************************************************************
 2 * FunctionName   : FatFileSystemCheck()
 3 * Description    : FsInfo校验
 4 * EntryParameter : None
 5 * ReturnValue    : 返回操作结果
 6 **************************************************************************************/
 7 u8 FatFileSystemCheck(void)
 8 {
 9     FATFS *pFs;
10     FATFS fs;
11     FRESULT res;
12     DWORD fre_clust;
13
14     f_mount(0, &fs);
15     res = f_fsCheck("", &fre_clust, &pFs);
16     f_mount(0, 0);
17
18     return res;
19 }

后面我们可以通过此函数的返回值,看是否要格式。

1 if (FatFileSystemCheck() != FR_OK)     // 文件系统毁坏,格式
2  {
3      App_Format();
4  }

问题2:根目录正常,里边的文件夹毁坏。
    
     现象:在FatFs下可以读写文件,可在PC上可以打开根目录,却无法里面的文件夹,提示文件毁坏。

分析:通过WinHex软件打开磁盘,发现目录完全正常,但是FAT表与目录数据对应不上,引起的原因可能是带电插拔。

解决:既然是FAT表与目录对不上,就是用PC修复也只会删除这些文件,对于我们的单片机来说,也没有好的解决方法,那就删除这个文件吧。

下面的代码用于判断用于判断是否可以在这个文件夹下新建文件,能新建就是正常的,否则异常,删除这个文件夹。

 1 /****************************************************************************
 2 * FunctionName   : FatCreateDir()
 3 * Description    : 创建一个新目录
 4 * EntryParameter : folder - 文件夹的名称
 5 * ReturnValue    : 成功返回真,否则返回假
 6 ****************************************************************************/
 7 u8 FatCreateDir(u8 *dir)
 8 {
 9     FATFS fs;
10     FRESULT res;
11     DIR dirs;
12     f_mount(0, &fs);
13
14     res = f_opendir(&dirs, (const TCHAR *)dir);        // 打开目录
15     if (res == FR_NO_PATH)                            // 没有则创建
16     {
17         res = f_mkdir((const TCHAR *)dir);            // 创建目录
18         if (res != FR_OK)
19         {
20             FatDeleteFile(dir);
21             res = (FRESULT)FatCreateDir(dir);
22         }
23     }
24
25     f_mount(0, 0);
26     return res;
27 }

问题3:文件大小为0字节,并且无法删除。
    
     现象:文件已经存在,但在PC下无法删除,删除后会自动生成。

分析:既然文件已经创建,但没有内容,说明,文件打开后,写数据失败。

解决:既然文件已经新建,但没有写内容,我们可以在写内容失败后删除此文件,否则后面就删不掉了,只能格式了。

下面的代码用于判断文件是否读写正确,文件内容为0字节,而且写失败就删除。

 1 /**************************************************************************************
 2 * FunctionName   : FatWriteFile()
 3 * Description    : 写一个文件
 4 * EntryParameter : fname - 文件名,包含路径,pBuf - 缓冲,len - 长度
 5 * ReturnValue    : 成功返回真,否则返回假
 6 **************************************************************************************/
 7 u8 FatWriteFile(u8 *fname, u8 *pBuf, u16 len)
 8 {
 9     FATFS fs;
10     FIL fno;
11     UINT  bw;
12     FRESULT res;
13
14     f_mount(0, &fs);
15     res = f_open(&fno, (const TCHAR *)fname, FA_OPEN_ALWAYS|FA_WRITE);
16
17     if (res == FR_OK)
18     {
19         res = f_lseek(&fno, fno.fsize);            // 获取偏移指针
20         if (res == FR_OK)
21         {
22             res = f_write(&fno, pBuf, len, &bw);    // 数据写入
23             if ((res != FR_OK) && (fno.fsize == 0))
24             {
25                 res = f_unlink((const TCHAR *)fname);
26             }
27         }
28     }
29
30     f_close(&fno);
31     f_mount(0, 0);
32     return res;
33 }

问题4:SD卡电源无法关断。
    
     现象:通过I/O端口控制SD卡电源,关断后SD卡电源端还有2.9V左右的电压。

分析:不管用mos管还是电源芯片,通过I/O端口控制都应该截断电源,但事实上SD卡电源叫还是有电,原因是这些电压是通过SPI的4个端口串进去了,特别是片选管脚。

解决:既然是通过这几个管脚窜进去的,那么在关掉电源之前让这几个管脚都没有电压输入就可以了。

问题5:临界代码。
    
     现象:在操作文件系统时有时还没有读写完成,就断电或插拔SD卡。

分析:如果没有写完数据就直接断电或插拔会导致文件或文件系统毁坏。

解决:在对文件进行写操作时进来减小临界代码的尺寸。

我们可以尽量减少操作文件的时间,如果时间不能减少,我们可以减少临界代码的尺寸,可以在代码中添加f_sync()函数。例如下面的写WAV文件中,由于需要分别写入头和文件内容,我们可以再写入一段数据后添加一个同步还是。

 1 /****************************************************************************
 2 * FunctionName   : FatWriteWave()
 3 * Description    : 写WAV文件
 4 * EntryParameter : fname - 路径,pHd - 文件头,pDat - 数据,datLen - 数据长度
 5 * ReturnValue    : 成功返回0,否则返回1
 6 ****************************************************************************/
 7 u8 FatWriteWave(u8 *fname, u8 *pHd, u8 hdLen, u8 *pDat, u16 datLen)
 8 {
 9     FATFS fs;
10     FIL fno;
11     UINT  bw;
12     FRESULT res;
13
14     f_mount(0, &fs);
15     res = f_open(&fno, (const TCHAR *)fname, FA_OPEN_ALWAYS|FA_WRITE);
16     if (res == FR_OK)
17     {
18         res = (fno.fsize > 0) ? f_lseek(&fno, fno.fsize) : f_lseek(&fno, hdLen);
19         if (res == FR_OK)
20         {
21             res = f_write(&fno, pDat, datLen, &bw);    // 数据写入
22             if ((res != FR_OK) && (fno.fsize == 0))
23             {
24                 res = f_unlink((const TCHAR *)fname);
25             }
26             else
27             {
28                 f_sync(&fno);
29                 if (res == FR_OK)
30                 {
31                     res = f_lseek(&fno, 0);
32                     if (res == FR_OK)
33                     {
34                         res = f_write(&fno, pHd, hdLen, &bw);    // 数据写入成功后再写文件头
35                         if ((res != FR_OK) && (fno.fsize == 0))
36                         {
37                             res = f_unlink((const TCHAR *)fname);
38                         }
39                     }
40                 }
41             }
42         }
43     }
44
45     f_close(&fno);
46     f_mount(0, 0);
47     return res;
48 }

问题6:FAT表与FSInfo信息不匹配。

现象:为了尽快操作文件,而不用通过FAT遍历就可以知道SD卡的存储状态,在FSInfo中存储了未使用簇数和空闲簇号,但某种原因导致FAT表中是实际使用情况与FSInfo中信息不匹配。

分析:FSInfo中的信息可以快速定位到SD卡中的空闲区域,如果这里的信息不正确,我们只能通过FAT表获取这些信息。如果SD卡很大,特别是应用了很大空间,从FAT表中获取这些信息非常缓慢。

解决:如果某处读写操作非常缓慢时,可能是FAT表与FSInfo中的信息不匹配,我们需要进行一次匹配以矫正FSInfo中的信息。

下面的代码可以通过扫描FAT区获取真正的空闲号和空余空间,同时矫正这些信息。

 1 /*-----------------------------------------------------------------------*/
 2 /* Get Number of Free Clusters and proof */
 3 /*-----------------------------------------------------------------------*/
 4
 5 FRESULT f_getfreeproof(
 6 const TCHAR *path, /* Pointer to the logical drive number (root dir) */
 7 DWORD *nclst, /* Pointer to the variable to return number of free clusters */
 8 FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */
 9 )
10 {
11     FRESULT res;
12     FATFS *fs;
13     DWORD n, clst, sect, stat;
14     UINT i;
15     BYTE fat, *p;
16
17     /* Get drive number */
18     res = chk_mounted(&path, fatfs, 0);
19     fs = *fatfs;
20     if (res == FR_OK)
21     {
22         /* Get number of free clusters */
23         fat = fs->fs_type;
24         n = 0;
25         if (fat == FS_FAT12)
26         {
27             clst = 2;
28             do{
29                 stat = get_fat(fs, clst);
30                 if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
31                 if (stat == 1) { res = FR_INT_ERR; break; }
32                 if (stat == 0) n++;
33             } while (++clst < fs->n_fatent);
34         }
35         else
36         {
37             clst = fs->n_fatent;
38             sect = fs->fatbase;
39             i = 0; p = 0;
40             do {
41                 if (!i)
42                 {
43                     res = move_window(fs, sect++);
44                     if (res != FR_OK) break;
45                     p = fs->win;
46                     i = SS(fs);
47                 }
48                 if (fat == FS_FAT16)
49                 {
50                     if (LD_WORD(p) == 0) n++;
51                     p += 2; i -= 2;
52                 }
53                 else
54                 {
55                     if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
56                     p += 4; i -= 4;
57                 }
58             } while (--clst);
59         }
60
61         if (fs->free_clust != n)
62         {
63             fs->free_clust = n;
64             fs->last_clust = 0x02;
65             if (fat == FS_FAT32) fs->fsi_flag = 1;
66             *nclst = n;
67             sync(fs);
68         }
69     }
70     LEAVE_FF(fs, res);
71 }

问题7:文件毁坏。
    
     现象:在FatFs下写入文件时,有时由于头没有写对,有时由于尾没有写读,导致文件文件打开。

分析:通过WinHex软件打开磁盘,发现文件内容不正确,有点缺头,有的缺尾。

解决:既然是文件头或未不正确,我们可以对其头或尾进行判断,不正确的可以删除掉。

下面的代码是以JPGE文件为例,如果JPGE文件的头和尾不正确时,图片显示不对,对于头不对时,显示无法打开,如果是尾不正确可以打开,但部分内容无法显示,只能显示部分图像。我们可以通过判断,把不正确的图片删除,保留也没有意义。此发可以应用到其他文件上,至于该判断头还是尾,根据文件更改。

 1 /**************************************************************************************
 2 * FunctionName   : FatJpgFileJud()
 3 * Description    : 文件判断
 4 * EntryParameter : None
 5 * ReturnValue    : 格式错误返回1,否则返回0
 6 **************************************************************************************/
 7 u8 FatJpgFileJud(u8 *fname)
 8 {
 9     FATFS fs;
10     FRESULT res;
11     FIL file;
12     UINT br;
13     u8 reVal = 1;
14     u8 buf[2] = {0};
15
16     f_mount(0, &fs);
17     res = f_open(&file, (const TCHAR *)fname, FA_READ);    // 打开文件
18
19     if (res == FR_OK)
20     {
21         f_lseek(&file, 0);        // 打开指定位置
22         res = f_read(&file, buf, 2, &br);        // 读取数据
23         if ((res == FR_OK) && (buf[0] == 0xFF) && (buf[1] == 0xD8))    // 头判断
24         {
25             f_lseek(&file, file.fsize-2);            // 获取偏移指针
26             res = f_read(&file, buf, 2, &br);        // 读取数据
27             if ((res == FR_OK) && (buf[0] == 0xFF) && (buf[1] == 0xD9))    // 尾判断
28             {
29                 reVal = FR_OK;
30             }
31         }
32     }
33
34     f_close(&file);
35     return (reVal);
36 }

问题8:SD卡数据写入失败。
    
     现象:在FatFs下写入文件时,有时会一次写入不了数据,有时会连续几次写入不了数据。

分析:写入不了数据,是一些存储异常或者SD卡异常导致,例如接触不良、内存或堆栈问题等。

解决:写不了数据并不意味做SD卡有问题,我们可以让设备重启,再写入数据。

如果连续几次写不了数据就格式化SD卡,势必导致SD卡中文件内容的丢失,为了把损失将到最低,我们可以让设备重启,如果仍然无法写入数据,再格式化SD卡。

 1 /****************************************************************************
 2 * FunctionName   : AppSDCardAbnormal()
 3 * Description    : SD卡异常处理
 4 * EntryParameter : None
 5 * ReturnValue    : None
 6 ****************************************************************************/
 7 void AppSDCardAbnormal(void)
 8 {
 9     if ((AppPar.WrdErr+CMRPar.SECnt > APP_WRD_ER) && (AppPar.SdcSta == APP_SDC_NRM))
10     {
11         if (SDGetCardStatus() != SD_CARD_NO)    // 读取SD卡状态
12         {
13             FLSRestart();        // 重启
14         }
15         else
16         {
17             AppPar.WrdErr = 0;
18             CMRPar.SECnt  = 0;
19         }
20     }
21 }

问题9:SD卡热插拔。
    
     现象:在很都时候,我们都需要对SD卡进行热插拔操作,而我们知道,很多文件毁坏都是这样操作导致的。

分析:在读写SD卡时,突出断电由于文件并没有操作完成,会导致文件毁坏。

解决:在对SD卡进行插拔操作时,断掉SD卡的供电。

要读SD卡进行断电操作,可以有很多方法,例如,可以把SD卡锁在设备中,扒卡之前必须开锁,通过锁我们知道要对SD卡进行插拔了,所以,不能再对SD卡操作了,切断SD卡供电。在没有插入卡之前不能对SD卡供电。

当然我们还可以通过按键之类的东西实现,以保证不对SD卡带电操作即可。

SD卡应用总结(Fatfs)

时间: 2024-10-07 22:44:39

SD卡应用总结(Fatfs)的相关文章

在CC2541上移植SD卡驱动

CC2541不知道现在还有没有人用,当初算是BLE芯片里头资料比较丰富的一个,只是硬件资源太菜了,51内核真是捉急.去年因为某些原因,在上面实现了SD卡驱动,估计还没有人做过,现在把过程发出来,让大家瞧瞧. CC2541本来是用于低功耗蓝牙通信的,一般也不会有人用来扩展SD卡.不过肯定还是存在一些特殊需求用户,对吧.高速的SD卡驱动一般都用SDIO总线,CC2541自然只能用SPI总线了,速度不会太快,SPI时钟只能到4M.而且受限于CC2541的BLE协议栈,不能存太多的东西,否则时间过长会影

用FATFS在SD卡里写一串数字

用FATFS写SD卡,需要把数组的各个位取出来,变成字符串,然后才能写进去,,如果直接写就会出现乱码 还有一点要注意,就是 要写进去的东西如:pzu和pv1都是指针,,,,把一个指针指向数组之后,才能写,,,也许把指针分配空间后也能写,,不太清楚了... 原文地址:https://www.cnblogs.com/chulin/p/8997811.html

RT-Thread 学习笔记(九)---开启基于SD卡中的 Elm FatFS 文件系统

软件环境:Win7,Keil MDK 4.72a, IAR EWARM 7.2, GCC 4.2,Python 2.7 ,SCons 2.3.2 硬件环境:Armfly STM32F103ZE-EK v3.0开发板 参考文章:RT-Thread编程指南 由于SD卡可插拔的便捷性,使得其在嵌入式中的应用中广泛使用. [1]修改底层驱动接口 (1)打开Armfly STM32F103ZE-EK v3.0开发板找到SD卡硬件接口部分,如下图: 从硬件接口可以看到,SD是SDIO的接口类型,amobbs

如何使用CubeMx制作一个基于SD卡的文件系统工程(2)

本文是原文http://blog.csdn.net/flydream0/article/details/52777923的补充. 原文并没有考虑SD卡拔插问题,且SDIO没有使用DMA,本文作为补充,将示例如何改善这两方面的问题. 1 SD卡拔插检测 FATFS文件系统初始化得修改下: void MX_FATFS_Init(void) { /*## FatFS: Link the SD driver ###########################*/ retSD = FATFS_Link

单片机SD 卡读写

1.迄今为止看到的最详细的关于SD卡SPI mode的分析和代码 http://elm-chan.org/docs/mmc/mmc_e.html 2.转载http://blog.csdn.net/ming1006/article/details/7281597 现在我们手机的内存卡多为Micro SD卡,又叫TF卡,所以Micro SD卡比SD卡常见.自己曾经也想写写SD卡的读取程序,但又不想特地再去买个SD卡,这时想起手机内存卡不是和SD卡很像吗?在网上查了以后发现SD卡和Micro SD卡其

使用FreeRTOS在SD卡驱动使用非系统延时导致上电重启不工作的情况

一.问题描述在一个使用FreeRTOS的工程中,只做了SD卡的驱动,由于RTOS使用了Systick,故非系统延时函数使用的是 DWT中的时钟周期(CYCCNT)计数功能,但是在SD卡驱动中使用了这个非系统延时导致,烧写程序后板子工作正常,而下电再上电后板子无反应,分析排查去掉了这个非系统延时后工作正常. 二.使用环境1)开发环境使用的是MDK5.20,下载器为JLINK:2)软件工程是V6的FreeRTOS模板工程,SD卡驱动也是V6的,非系统延时函数所在文件为V6的 bsp_dwt.c:3)

读写sd卡代码分析(vivado sdk c++)

void ReadFloatsFromSDFile(float *weightsFromFile, const std::string file_name) { FIL fil; /* File object */ FATFS fatfs; FILINFO file_info; char *SD_File; FRESULT Res; UINT NumBytesRead; Res = f_mount(&fatfs, "0:/", 0); if (Res != FR_OK) pri

第36章 SDIO—SD卡读写测试

第36章     SDIO-SD卡读写测试 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:<STM32F4xx参考手册>.<STM32F4xx规格书>.库帮助文档<stm32f4xx_dsp_stdperiph_lib_um.chm>以及SD简易规格文件<Physical Layer Simplified Specificatio

sd卡读写——sd example阅读

改mss后import example 主要是用fat的函数读写sd 1 /****************************************************************************** 2 *版权这里可以忽略 3 * Copyright (C) 2013 - 2015 Xilinx, Inc. All rights reserved. 4 * 5 * Permission is hereby granted, free of charge, to