纬图虚拟仪器论坛

 找回密码
 立即注册
搜索
查看: 71256|回复: 76

STM32例程之FATFS文件系统(SPI方式)移植笔记(源码下载)

[复制链接]
发表于 2012-8-1 14:38:22 | 显示全部楼层 |阅读模式
本帖最后由 wdluo 于 2012-8-6 15:52 编辑

STM32的FATFS文件系统移植笔记


一、序言
    经常在网上、群里看到很多人问关于STM32的FATFS文件系统移植的问题,刚好自己最近也在调试这个程序,为了让大家少走弯路,我把我的调试过程和方法也贡献给大家。

二、FATFS简介
    FatFs Module是一种完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C语言编写,所以具有良好的硬件平台独立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列单片机上而只需做简单的修改。它支持FATl2、FATl6和FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。

三、移植准备
    1、FATFS源代码的获取,可以到官网下载:http://elm-chan.org/fsw/ff/00index_e.html 最新版本是R0.09版本,我们就移植这个版本的。
    2、解压文件会得到两个文件夹,一个是doc文件夹,这里是FATFS的一些使用文档和说明,以后在文件编程的时候可以查看该文档。另一个是src文件夹,里面就是我们所要的源文件。
    3、建立一个STM32的工程,为方便调试,我们应重载printf()底层函数实现串口打印输出。可以参考已经建立好的printf()打印输出工程:http://www.viewtool.com/bbs/foru ... d=77&extra=page%3D1
四、开始移植
    1、在已经建立好的工程目录User文件夹下新建两个文件夹,FATFS_V0.09和SPI_SD_Card,FATFS_V0.09用于存放FATFS源文件,SPI_SD_Card用于存放SPI的驱动文件。
    2、如图1将ff.c添加到工程文件夹中,并新建diskio.c文件,在diskio.c文件中实现五个函数:
  1. DSTATUS disk_initialize (BYTE);//SD卡的初始化
  2.         DSTATUS disk_status (BYTE);//获取SD卡的状态,这里可以不用管
  3.         DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);//从SD卡读取数据
  4.         DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);//将数据写入SD卡,若该文件系统为只读文件系统则不用实现该函数
  5.         DRESULT disk_ioctl (BYTE, BYTE, void*);//获取SD卡文件系统相关信息
复制代码

图1

图1


图1
    3、初步实现以上五个函数
        FATFS初始化函数:
  1. DSTATUS disk_initialize (
  2.                 BYTE drv                                /* Physical drive nmuber (0..) */
  3.         )
  4.         {
  5.                 switch (drv)
  6.                 {
  7.                         case 0 :
  8.                                 return RES_OK;
  9.                         case 1 :
  10.                                 return RES_OK;         
  11.                         case 2 :
  12.                                 return RES_OK;         
  13.                         case 3 :
  14.                                 return RES_OK;
  15.                         default:
  16.                                 return STA_NOINIT;
  17.                 }
  18.         }
复制代码
FATFS状态获取函数:
  1. DSTATUS disk_status (
  2.                 BYTE drv                /* Physical drive nmuber (0..) */
  3.         )
  4.         {
  5.                 switch (drv)
  6.                 {
  7.                         case 0 :
  8.                                 return RES_OK;
  9.                         case 1 :
  10.                                 return RES_OK;
  11.                         case 2 :
  12.                                 return RES_OK;
  13.                         default:
  14.                                 return STA_NOINIT;
  15.                 }
  16.         }
复制代码
FATFS底层读数据函数:
  1. DRESULT disk_read (
  2.                 BYTE drv,                /* Physical drive nmuber (0..) */
  3.                 BYTE *buff,                /* Data buffer to store read data */
  4.                 DWORD sector,        /* Sector address (LBA) */
  5.                 BYTE count                /* Number of sectors to read (1..255) */
  6.         )
  7.         {
  8.                 if( !count )
  9.                 {   
  10.                         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
  11.                 }
  12.                 switch (drv)
  13.                 {
  14.                         case 0:
  15.                             if(count==1)            /* 1个sector的读操作 */      
  16.                             {   
  17.                                         return RES_OK;   
  18.                             }                                                
  19.                             else                    /* 多个sector的读操作 */     
  20.                             {  
  21.                                         return RES_OK;
  22.                             }                                                
  23.                         case 1:
  24.                             if(count==1)            /* 1个sector的读操作 */      
  25.                             {   
  26.                                         return RES_OK;   
  27.                             }                                                
  28.                             else                    /* 多个sector的读操作 */     
  29.                             {  
  30.                                         return RES_OK;
  31.                             }

  32.                         default:
  33.                                 return RES_ERROR;
  34.                 }
  35.         }
复制代码
FATFS底层写数据函数:
  1. DRESULT disk_write (
  2.                 BYTE drv,                        /* Physical drive nmuber (0..) */
  3.                 const BYTE *buff,                /* Data to be written */
  4.                 DWORD sector,                /* Sector address (LBA) */
  5.                 BYTE count                        /* Number of sectors to write (1..255) */
  6.         )
  7.         {
  8.                 if( !count )
  9.                 {   
  10.                         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
  11.                 }
  12.                 switch (drv)
  13.                 {
  14.                         case 0:
  15.                             if(count==1)            /* 1个sector的写操作 */      
  16.                             {   
  17.                                         return RES_OK;
  18.                             }                                                
  19.                             else                    /* 多个sector的写操作 */   
  20.                             {  
  21.                                         return RES_OK;  
  22.                             }                                                
  23.                         case 1:
  24.                             if(count==1)            /* 1个sector的写操作 */      
  25.                             {  
  26.                                         return RES_OK;
  27.                             }                                                
  28.                             else                    /* 多个sector的写操作 */   
  29.                             {  
  30.                                         return RES_OK;
  31.                             }                                                
  32.          
  33.                         default:return RES_ERROR;
  34.                 }
  35.         }
复制代码
FATFS磁盘控制函数:
  1. DRESULT disk_ioctl (
  2.                 BYTE drv,                /* Physical drive nmuber (0..) */
  3.                 BYTE ctrl,                /* Control code */
  4.                 void *buff                /* Buffer to send/receive control data */
  5.         )
  6.         {
  7.                 if (drv==0)
  8.                 {   
  9.                         switch (ctrl)
  10.                         {
  11.                                 case CTRL_SYNC :
  12.                                         return RES_OK;
  13.                                 case GET_SECTOR_COUNT :
  14.                                 return RES_OK;
  15.                                 case GET_BLOCK_SIZE :
  16.                                 return RES_OK;        
  17.                                 case CTRL_POWER :
  18.                                         break;
  19.                                 case CTRL_LOCK :
  20.                                         break;
  21.                                 case CTRL_EJECT :
  22.                                         break;
  23.                         /* MMC/SDC command */
  24.                                 case MMC_GET_TYPE :
  25.                                         break;
  26.                                 case MMC_GET_CSD :
  27.                                         break;
  28.                                 case MMC_GET_CID :
  29.                                         break;
  30.                                 case MMC_GET_OCR :
  31.                                         break;
  32.                                 case MMC_GET_SDSTAT :
  33.                                         break;        
  34.                         }
  35.             }else if(drv==1){
  36.                         switch (ctrl)
  37.                         {
  38.                                 case CTRL_SYNC :
  39.                                         return RES_OK;
  40.                                 case GET_SECTOR_COUNT :
  41.                                 return RES_OK;
  42.                                 case GET_SECTOR_SIZE :
  43.                                         return RES_OK;
  44.                                 case GET_BLOCK_SIZE :
  45.                                 return RES_OK;        
  46.                                 case CTRL_POWER :
  47.                                         break;
  48.                                 case CTRL_LOCK :
  49.                                         break;
  50.                                 case CTRL_EJECT :
  51.                                         break;
  52.                         /* MMC/SDC command */
  53.                                 case MMC_GET_TYPE :
  54.                                         break;
  55.                                 case MMC_GET_CSD :
  56.                                         break;
  57.                                 case MMC_GET_CID :
  58.                                         break;
  59.                                 case MMC_GET_OCR :
  60.                                         break;
  61.                                 case MMC_GET_SDSTAT :
  62.                                         break;        
  63.                         }         
  64.                 }
  65.                 else{                                 
  66.                         return RES_PARERR;  
  67.                 }
  68.                 return RES_PARERR;
  69.         }
复制代码
以上函数都只是实现一个框架,并没有做实际的事情,下一步就需要把操作SD卡的程序填充在这个框架里面。
    4、实现disk_initialize()函数
        该函数在挂载文件系统的时候会被调用,主要是实现读写SD卡前对SD卡进行初始化,根据SD卡的传输协议,我们按照如下步骤初始化SD卡:
        a、判断SD卡是否插入,可以通过检查SD卡卡座的CD脚电平进行判断,一般插入卡后该引脚会变成低电平。
        b、稍微延时一段时间后发送至少74个时钟给SD卡。
        c、发送CMD0命令给SD卡,直到SD卡返回0x01为止,这里可以循环多次发送。
                程序如下:
  1.   /* Start send CMD0 till return 0x01 means in IDLE state */
  2.                 for(retry=0; retry<0xFFF; retry++)
  3.                 {
  4.                         r1 = MSD0_send_command(CMD0, 0, 0x95);
  5.                         if(r1 == 0x01)
  6.                         {
  7.                                 retry = 0;
  8.                                 break;
  9.                         }
  10.                 }
复制代码
d、发送CMD8获取卡的类型,不同类型的卡其初始化方式有所不同。
        e、根据卡的类型对卡进行初始化。具体初始化方式可以参考附件程序。
        注:在初始化SD卡之前应该初始化SPI接口和相关的管脚。
        实现后的程序如下:
  1. DSTATUS disk_initialize (
  2.                 BYTE drv                                /* Physical drive nmuber (0..) */
  3.         )
  4.         {
  5.                 int Status;
  6.                 switch (drv)
  7.                 {
  8.                         case 0 :
  9.                                 Status = MSD0_Init();
  10.                                 if(Status==0){
  11.                                         return RES_OK;
  12.                                 }else{
  13.                                         return STA_NOINIT;
  14.                                 }
  15.                         case 1 :
  16.                                 return RES_OK;         
  17.                         case 2 :
  18.                                 return RES_OK;         
  19.                         case 3 :
  20.                                 return RES_OK;
  21.                         default:
  22.                                 return STA_NOINIT;
  23.                 }
  24.         }
复制代码
MSD0_Init()函数在SPI_MSD0_Driver.c文件中实现。
    5、实现disk_read()函数
        该函数是读取SD卡扇区数据的函数,根据SD卡数据传输协议可知有读取单扇区和读取多扇区两种操作模式,为提高读文件的速度应该实现读取多扇区函数。
        实现后的程序如下:
  1. DRESULT disk_read (
  2.                 BYTE drv,                /* Physical drive nmuber (0..) */
  3.                 BYTE *buff,                /* Data buffer to store read data */
  4.                 DWORD sector,        /* Sector address (LBA) */
  5.                 BYTE count                /* Number of sectors to read (1..255) */
  6.         )
  7.         {
  8.                 int Status;
  9.                 if( !count )
  10.                 {   
  11.                         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
  12.                 }
  13.                 switch (drv)
  14.                 {
  15.                         case 0:
  16.                             if(count==1)            /* 1个sector的读操作 */      
  17.                             {   
  18.                                         Status =  MSD0_ReadSingleBlock( sector ,buff );
  19.                                         if(Status == 0){
  20.                                                 return RES_OK;
  21.                                         }else{
  22.                                                 return RES_ERROR;
  23.                                         }   
  24.                             }                                                
  25.                             else                    /* 多个sector的读操作 */     
  26.                             {  
  27.                                         Status = MSD0_ReadMultiBlock( sector , buff ,count);
  28.                                         if(Status == 0){
  29.                                                 return RES_OK;
  30.                                         }else{
  31.                                                 return RES_ERROR;
  32.                                         }
  33.                             }                                                
  34.                         case 1:
  35.                             if(count==1)            /* 1个sector的读操作 */      
  36.                             {   
  37.                                         return RES_OK;   
  38.                             }                                                
  39.                             else                    /* 多个sector的读操作 */     
  40.                             {  
  41.                                         return RES_OK;
  42.                             }

  43.                         default:
  44.                                 return RES_ERROR;
  45.                 }
  46.         }
复制代码
MSD0_ReadSingleBlock()和MSD0_ReadMultiBlock()函数都是SD卡操作的底层函数,我们在SPI_MSD0_Driver.c文件中实现。
    6、实现disk_write()函数
        该函数主要实现对SD卡进行写数据操作,和读数据操作一样也分单块写和多块写,建议实现多块写的方式,这样可以提高写数据速度。
        实现后的程序如下:
  1. DRESULT disk_write (
  2.                 BYTE drv,                        /* Physical drive nmuber (0..) */
  3.                 const BYTE *buff,                /* Data to be written */
  4.                 DWORD sector,                /* Sector address (LBA) */
  5.                 BYTE count                        /* Number of sectors to write (1..255) */
  6.         )
  7.         {
  8.                 int Status;
  9.                 if( !count )
  10.                 {   
  11.                         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
  12.                 }
  13.                 switch (drv)
  14.                 {
  15.                         case 0:
  16.                             if(count==1)            /* 1个sector的写操作 */      
  17.                             {   
  18.                                         Status = MSD0_WriteSingleBlock( sector , (uint8_t *)(&buff[0]) );
  19.                                         if(Status == 0){
  20.                                                 return RES_OK;
  21.                                         }else{
  22.                                                 return RES_ERROR;
  23.                                         }
  24.                             }                                                
  25.                             else                    /* 多个sector的写操作 */   
  26.                             {  
  27.                                         Status = MSD0_WriteMultiBlock( sector , (uint8_t *)(&buff[0]) , count );
  28.                                         if(Status == 0){
  29.                                                 return RES_OK;
  30.                                         }else{
  31.                                                 return RES_ERROR;
  32.                                         }   
  33.                             }                                                
  34.                         case 1:
  35.                             if(count==1)            /* 1个sector的写操作 */      
  36.                             {  
  37.                                         return RES_OK;
  38.                             }                                                
  39.                             else                    /* 多个sector的写操作 */   
  40.                             {  
  41.                                         return RES_OK;
  42.                             }                                                
  43.          
  44.                         default:return RES_ERROR;
  45.                 }
  46.         }
复制代码
MSD0_WriteSingleBlock()和MSD0_WriteMultiBlock()函数都是SD卡操作的底层函数,我们在SPI_MSD0_Driver.c文件中实现。
    7、实现disk_ioctl()函数
        该函数在磁盘格式化、获取文件系统信息等操作时会被调用。
        实现后的程序如下:
  1. DRESULT disk_ioctl (
  2.                 BYTE drv,                /* Physical drive nmuber (0..) */
  3.                 BYTE ctrl,                /* Control code */
  4.                 void *buff                /* Buffer to send/receive control data */
  5.         )
  6.         {
  7.                 if (drv==0)
  8.                 {   
  9.                         MSD0_GetCardInfo(&SD0_CardInfo);
  10.                         switch (ctrl)
  11.                         {
  12.                                 case CTRL_SYNC :
  13.                                         return RES_OK;
  14.                                 case GET_SECTOR_COUNT :
  15.                                         *(DWORD*)buff = SD0_CardInfo.Capacity/SD0_CardInfo.BlockSize;
  16.                                 return RES_OK;
  17.                                 case GET_BLOCK_SIZE :
  18.                                         *(WORD*)buff = SD0_CardInfo.BlockSize;
  19.                                 return RES_OK;        
  20.                                 case CTRL_POWER :
  21.                                         break;
  22.                                 case CTRL_LOCK :
  23.                                         break;
  24.                                 case CTRL_EJECT :
  25.                                         break;
  26.                         /* MMC/SDC command */
  27.                                 case MMC_GET_TYPE :
  28.                                         break;
  29.                                 case MMC_GET_CSD :
  30.                                         break;
  31.                                 case MMC_GET_CID :
  32.                                         break;
  33.                                 case MMC_GET_OCR :
  34.                                         break;
  35.                                 case MMC_GET_SDSTAT :
  36.                                         break;        
  37.                         }
  38.             }else if(drv==1){
  39.                         switch (ctrl)
  40.                         {
  41.                                 case CTRL_SYNC :
  42.                                         return RES_OK;
  43.                                 case GET_SECTOR_COUNT :
  44.                                 return RES_OK;
  45.                                 case GET_SECTOR_SIZE :
  46.                                         return RES_OK;
  47.                                 case GET_BLOCK_SIZE :
  48.                                 return RES_OK;        
  49.                                 case CTRL_POWER :
  50.                                         break;
  51.                                 case CTRL_LOCK :
  52.                                         break;
  53.                                 case CTRL_EJECT :
  54.                                         break;
  55.                         /* MMC/SDC command */
  56.                                 case MMC_GET_TYPE :
  57.                                         break;
  58.                                 case MMC_GET_CSD :
  59.                                         break;
  60.                                 case MMC_GET_CID :
  61.                                         break;
  62.                                 case MMC_GET_OCR :
  63.                                         break;
  64.                                 case MMC_GET_SDSTAT :
  65.                                         break;        
  66.                         }         
  67.                 }
  68.                 else{                                 
  69.                         return RES_PARERR;  
  70.                 }
  71.                 return RES_PARERR;
  72.         }
复制代码
MSD0_GetCardInfo()函数也在SPI_MSD0_Driver.c文件中实现,其中SD0_CardInfo为PMSD_CARDINFO类型的全局变量,它在SPI_MSD0_Driver.h文件中被定义。
    8、到此diskio.c这个文件中的所有函数就已经实现,下一步就是实现SPI_MSD0_Driver.c文件中的相关函数,SPI_MSD0_Driver.c文件可以在网上下载,参考的程序比较多,本工程使用的这个文件也是在网上下载并进行一定的修改过的。本文件中函数的实现方式可以参考源代码。
五、文件系统测试
    1、测试写文件
    测试代码如下:
  1. //写文件测试
  2.         printf("write file test......\n\r");
  3.         res = f_open(&fdst, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
  4.         if(res != FR_OK){
  5.                 printf("open file error : %d\n\r",res);
  6.         }else{
  7.                 res = f_write(&fdst, textFileBuffer, sizeof(textFileBuffer), &bw);               /* Write it to the dst file */
  8.                 if(res == FR_OK){
  9.                         printf("write data ok! %d\n\r",bw);
  10.                 }else{
  11.                         printf("write data error : %d\n\r",res);
  12.                 }
  13.                 /*close file */
  14.                 f_close(&fdst);
  15.         }
复制代码
注意:成功打开文件后一定要调用f_close()函数,否则数据无法写入SD卡中。
    2、测试读文件
  1. //读文件测试
  2.         printf("read file test......\n\r");
  3.         res = f_open(&fsrc, "0:/test.txt", FA_OPEN_EXISTING | FA_READ);
  4.         if(res != FR_OK){
  5.                 printf("open file error : %d\n\r",res);
  6.         }else{
  7.                 res = f_read(&fsrc, buffer, sizeof(textFileBuffer), &br);     /* Read a chunk of src file */
  8.                 if(res==FR_OK){
  9.                         printf("read data num : %d\n\r",br);
  10.                         printf("%s\n\r",buffer);
  11.                 }else{
  12.                         printf("read file error : %d\n\r",res);
  13.                 }
  14.                 /*close file */
  15.                 f_close(&fsrc);
  16.         }
复制代码
3、测试结果
    测试结果如图2所示。

图2

图2

图2
六、中文长文件名支持
    1、要支持长文件名需要在ffconf.h文件中修改两个宏定义。如下为我们修改后的宏定义。
    #define        _CODE_PAGE        936
    #define        _USE_LFN        1                /* 0 to 3 */
    2、添加支持中文编码的文件
    重新编译会发现有如图3的错误。原因是要支持中文文件名需要包含另外一个文件cc936.c,该文件在FATFS文件系统源码的.\src\option目录下,将它添加到工程文件目录FATFS中。如图4是我们添加文件后的工程文件结构,再次编译就通过了。如图5所示。我们发现增加这个文件后代码量增加了很多,主要原因是这个文件是我们支持中文所需要的中文编码文件。

图4

图4

图3

图5

图5

图4

图3

图3

图5
    3、再次下载到板子中运行,发现中文的长文件名显示正常了。如图6所示。
图6.jpg
    4、若不需要支持中文长文件名而只支持英文长文件名则可以将宏定义做如下修改:
    #define        _CODE_PAGE        437
    #define        _USE_LFN        1                /* 0 to 3 */
    同时将ccsbcs.c添加到工程目录中,这样就可以减小很多大代码量。将程序下载板子后再次运行结果如图7所示,可以看到可以支持英文的长文件名。
图7.jpg
七、源文件下载
STM32_FATFS_SPI.rar (1.3 MB, 下载次数: 7244)
回复

使用道具 举报

发表于 2012-8-3 16:41:55 | 显示全部楼层
真的真的帮助很大啊~LZ给跪了~
回复 支持 反对

使用道具 举报

发表于 2012-8-3 22:23:11 | 显示全部楼层
我想问一下~4.c里面的那段代码挂在哪啊?求指点~
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-3 23:08:00 | 显示全部楼层
xr0992 发表于 2012-8-3 22:23
我想问一下~4.c里面的那段代码挂在哪啊?求指点~

什么代码?
回复 支持 反对

使用道具 举报

发表于 2012-8-10 09:23:20 | 显示全部楼层

回帖奖励 +2

不明觉厉啊....有代码的都是好贴
回复 支持 反对

使用道具 举报

发表于 2012-8-11 14:51:01 | 显示全部楼层
不管你信不信,反正我是信了。
回复 支持 反对

使用道具 举报

发表于 2012-8-16 10:15:12 | 显示全部楼层
我现在也在研究这个,很好,正需要,感激中!
回复 支持 反对

使用道具 举报

发表于 2012-8-18 13:23:30 | 显示全部楼层
我刚接触FATFS,用了楼主的例程,想请一个问题:

*******************************************************************************
************************ Copyright 2009-2012, ViewTool ************************
*************************** http://www.viewtool.com ***************************
***************************** All Rights Reserved *****************************
*******************************************************************************
write file test......
open file error : 3
read file test......
open file error : 3

begin scan files path0......
scan files error : 3

Get total drive space faild!

不知道是为什么??????
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-18 13:47:12 | 显示全部楼层
20091613310050 发表于 2012-8-18 13:23
我刚接触FATFS,用了楼主的例程,想请一个问题:

*************************************************** ...

是SPI模式吗?你把SPI时钟频率调低试试
回复 支持 反对

使用道具 举报

发表于 2012-8-18 15:37:17 | 显示全部楼层
恩恩,我用的就是SPI模式。我现在试着把频率调低试试看,多谢你的回答。
回复 支持 反对

使用道具 举报

发表于 2012-8-18 15:53:03 | 显示全部楼层

inking...
Program Size: Code=11060 RO-data=2064 RW-data=612 ZI-data=7636  
FromELF: creating hex file...
".\Output\Project.axf" - 0 Error(s), 1 Warning(s).
我直接用工程文件编译,出现一个警告,而楼主你图5显示的编译结果没有警告呀?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-18 16:03:09 | 显示全部楼层
20091613310050 发表于 2012-8-18 15:53
inking...
Program Size: Code=11060 RO-data=2064 RW-data=612 ZI-data=7636  
FromELF: creating hex ...

是什么警告??

回复 支持 反对

使用道具 举报

发表于 2012-8-18 16:13:31 | 显示全部楼层
刚刚试着把SPI频率改成10M的了,但是问题还是没有解决
回复 支持 反对

使用道具 举报

发表于 2012-8-19 10:19:13 | 显示全部楼层
楼主,我想问一下,你的程序只支持TF卡(1G/2G)。4G SDHC支持吗,还有就是你的程序里面有关于卡版本的代码,但是我不知道自己的卡版本是多少,怎么样能查看TF卡的版本信息呀????????????
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-19 10:21:28 | 显示全部楼层
20091613310050 发表于 2012-8-19 10:19
楼主,我想问一下,你的程序只支持TF卡(1G/2G)。4G SDHC支持吗,还有就是你的程序里面有关于卡版本的代码, ...

我程序是支持4G卡的,我就是用的4G卡调试的,卡的版本信息查询方法可以参考SD的数据手册,有相关命令的。
回复 支持 反对

使用道具 举报

发表于 2012-8-19 10:35:50 | 显示全部楼层
嗯嗯,多谢了。
回复 支持 反对

使用道具 举报

发表于 2012-8-19 17:05:32 | 显示全部楼层
喔噢-----------太感谢楼主了,原来我的开发板没有SD卡插入检测的SD_CD引脚,把程序简单修改了就运行通过了。。。。
回复 支持 反对

使用道具 举报

发表于 2012-8-19 17:26:44 | 显示全部楼层
不知道LZ用的什么SD卡,反正我移植过去是SD卡驱动有问题,具体没研究。
初始化当然是成功的,可以检测卡的类型。

找了两天时间,找到个兼容性很强的代码,支持TF卡(1G/2G),金士顿2G,4G 16G SD卡,等FATFS移植好一并奉上,以后各种项目用到SD功能直接COPY,30秒修改就OK。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-19 17:33:04 | 显示全部楼层
vzhaodan 发表于 2012-8-19 17:26
不知道LZ用的什么SD卡,反正我移植过去是SD卡驱动有问题,具体没研究。
初始化当然是成功的,可以检测卡的 ...

好!非常期待!
回复 支持 反对

使用道具 举报

发表于 2012-8-19 17:42:50 | 显示全部楼层
喔噢--------------我也很期待!!!!
回复 支持 反对

使用道具 举报

发表于 2012-8-20 15:10:26 | 显示全部楼层
版主你好
按照你这个文件系统的方式移植过去式这样的
xxxx.jpg
打开文件错误,是什么原因,我对文件系统不了解,只是想先跑通再研究下
至于文件系统中的4个接口函数我的测试过没问题的:
u8 SD_ReadSingleBlock(u32 sector, u8 *buffer);  //读一个sector
u8 SD_WriteSingleBlock(u32 sector, const u8 *buffer);                 //写一个sector
u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count);         //读多个sector
u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count);//写多个sector

回复 支持 反对

使用道具 举报

发表于 2012-8-20 15:13:17 | 显示全部楼层
在PC机上可以看到文件写入的.txt成功,之后格式化SD卡,在放在MCU上测试这样
xxxx.jpg
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 15:18:40 | 显示全部楼层
本帖最后由 wdluo 于 2012-8-20 15:21 编辑
vzhaodan 发表于 2012-8-20 15:10
版主你好
按照你这个文件系统的方式移植过去式这样的
  1. /* File function return code (FRESULT) */

  2. typedef enum {
  3.         FR_OK = 0,                     /* (0) Succeeded */
  4.         FR_DISK_ERR,                /* (1) A hard error occured in the low level disk I/O layer */
  5.         FR_INT_ERR,                  /* (2) Assertion failed */
  6.         FR_NOT_READY,            /* (3) The physical drive cannot work */
  7.         FR_NO_FILE,                  /* (4) Could not find the file */
  8.         FR_NO_PATH,                 /* (5) Could not find the path */
  9.         FR_INVALID_NAME,         /* (6) The path name format is invalid */
  10.         FR_DENIED,             /* (7) Acces denied due to prohibited access or directory full */
  11.         FR_EXIST,                        /* (8) Acces denied due to prohibited access */
  12.         FR_INVALID_OBJECT,      /* (9) The file/directory object is invalid */
  13.         FR_WRITE_PROTECTED,  /* (10) The physical drive is write protected */
  14.         FR_INVALID_DRIVE,         /* (11) The logical drive number is invalid */
  15.         FR_NOT_ENABLED,          /* (12) The volume has no work area */
  16.         FR_NO_FILESYSTEM,       /* (13) There is no valid FAT volume */
  17.         FR_MKFS_ABORTED,       /* (14) The f_mkfs() aborted due to any parameter error */
  18.         FR_TIMEOUT,                  /* (15) Could not get a grant to access the volume within defined period */
  19.         FR_LOCKED,                     /* (16) The operation is rejected according to the file shareing policy */
  20.         FR_NOT_ENOUGH_CORE,    /* (17) LFN working buffer could not be allocated */
  21.         FR_TOO_MANY_OPEN_FILES,  /* (18) Number of open files > _FS_SHARE */
  22.         FR_INVALID_PARAMETER        /* (19) Given parameter is invalid */
  23. } FRESULT;
复制代码
上面这个是文件系统错误代码的一些解释,估计还是读函数有点问题
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 15:24:00 | 显示全部楼层
vzhaodan 发表于 2012-8-20 15:13
在PC机上可以看到文件写入的.txt成功,之后格式化SD卡,在放在MCU上测试这样

这个程序可能不是很稳定,我找时间再调试下吧
回复 支持 反对

使用道具 举报

发表于 2012-8-20 15:29:53 | 显示全部楼层
谢谢,我去找找其他的移植资料看看,做这个马虎不得,换一个平台就出各种问题
回复 支持 反对

使用道具 举报

发表于 2012-8-20 15:50:02 | 显示全部楼层
楼主,我想通过超级终端和创建的.txt文件里面的内容进行交互,就是在键盘输入字符串,将字符串写入文件中,该怎么修改呀,给点指导好吗????
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 16:03:05 | 显示全部楼层
20091613310050 发表于 2012-8-20 15:50
楼主,我想通过超级终端和创建的.txt文件里面的内容进行交互,就是在键盘输入字符串,将字符串写入文件中, ...

你在程序里面实现各种命令就可以了,不难的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 16:25:43 | 显示全部楼层
前面坛友反应会出现程序无法正确读写文件的问题,容易返回错误代码为1的错误,我实际测试了下,确实有时候也会有这样的问题,分析主要原因可能是时钟太快引起的,于是将SPI_MSDO_Driver.c文件中的MSD0_SPIHighSpeed()函数中的SPI时钟设置那里将时钟调低即可,比如原来是:SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;,调整成SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;后可以解决问题!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 16:26:51 | 显示全部楼层
vzhaodan 发表于 2012-8-20 15:13
在PC机上可以看到文件写入的.txt成功,之后格式化SD卡,在放在MCU上测试这样

你把时钟频率改低试试
回复 支持 反对

使用道具 举报

发表于 2012-8-20 16:35:59 | 显示全部楼层
恩恩,我现在开始尝试添加功能。
回复 支持 反对

使用道具 举报

发表于 2012-8-20 17:55:46 | 显示全部楼层
wdluo 发表于 2012-8-20 16:26
你把时钟频率改低试试

所有的SPI速率都试过了,还是跟前面一样 写文件成功,读文件错误。 可能会是啥原因引起的?我再仿真看看
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 18:09:39 | 显示全部楼层
vzhaodan 发表于 2012-8-20 17:55
所有的SPI速率都试过了,还是跟前面一样 写文件成功,读文件错误。 可能会是啥原因引起的?我再仿真看看

那很奇怪啊,我这里完全没问题
回复 支持 反对

使用道具 举报

发表于 2012-8-20 18:15:55 | 显示全部楼层
注释掉写文件的部分,然后直接读文件是正确的,我觉得还是SD驱动部分有问题
2.jpg
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-20 18:16:55 | 显示全部楼层
vzhaodan 发表于 2012-8-20 18:15
注释掉写文件的部分,然后直接读文件是正确的,我觉得还是SD驱动部分有问题

仔细检查下写函数
回复 支持 反对

使用道具 举报

发表于 2012-8-20 18:36:31 | 显示全部楼层
如果把 读文件代码放在前后,写文件代码放在后面,那都没问题了,到底是SD驱动还是文件系统问题呢?我来研究下如果操作文件系统,或许是不规范操作引起的?
回复 支持 反对

使用道具 举报

发表于 2012-8-20 18:43:30 | 显示全部楼层
本帖最后由 vzhaodan 于 2012-8-20 19:33 编辑

111.jpg

板主的程序是在写文件之前挂载文件系统:res = f_mount(0,&fs);  之后再写,再读。
原本我按照版主这个顺是只能写文件,而读文件就有错,现在读文件之前也加上这句:res = f_mount(0,&fs);  就没有问题了,也就是说挂载一次文件系统就让SD重新初始化了一次驱动,这样就行,究竟啥原因呢?
我的程序: SPI_I2S (5) 读写SD_FATFS.rar (844.05 KB, 下载次数: 229)
回复 支持 反对

使用道具 举报

发表于 2012-8-22 00:19:23 | 显示全部楼层
无意中看到了一个很小的问题,就是读文件后马上关闭,再写入文件时导致错误,所以写完毕之后不要关闭文件,再等读之后再关闭文件,这样整个程序测试完全通过呢 。不然像我之前说的当关闭之后重新初始化SD卡就OK了,但这种做法很S B
这小问题主要是代码风格不标准造成的,像JAVA编程风格,层次结构不太清晰,之前没注意到...
其实版主不妨看看自己的程序,刚用4G SD卡测试问题不大,发现学SD各种问题都有
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-22 09:10:19 | 显示全部楼层
vzhaodan 发表于 2012-8-22 00:19
无意中看到了一个很小的问题,就是读文件后马上关闭,再写入文件时导致错误,所以写完毕之后不要关闭文件, ...

写文件后必须要调用关闭文件函数,否则最后一块的数据不会写入SD里面。
回复 支持 反对

使用道具 举报

发表于 2012-8-28 11:49:18 | 显示全部楼层
多谢楼主分享,正在弄这个fatfs
回复 支持 反对

使用道具 举报

发表于 2012-9-10 16:36:55 | 显示全部楼层
用LZ的程序down一遍,更改了下串口1配置变成串口3,芯片选择了103C8的,其他都一样。效果还不错。就是刚开始的时候,随便拿了2张2G的SD卡没格式化过,就会出错,好像错误3,也有错误1.格式化之后就没出现问题了。接下来就去看看SD卡的内部构造和原理。,感谢LZ哈
123.png

点评

呵呵,如果SD卡没有正确的格式化的话会提示找不到文件系统的,当然会出错!  发表于 2012-9-10 16:46
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|纬图虚拟仪器

GMT+8, 2024-12-12 08:43 , Processed in 0.159778 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表