打印
[ZLG-ARM]

LPC1114 FAT下读写SD卡

[复制链接]
4646|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
北京户口|  楼主 | 2010-7-19 20:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
前一贴里利用的是SSP总线下进行扇区的读写,这一贴将在FAT文件系统下进**的读写。



       本来想自己写个简单的FAT文件系统,但是,在前不就,本人写了个FAT在AVR ICC下能够正常的运行,移植到LPC2148中来时死活不运行,这让我对自己写FAT失去了信心。本身FAT就是个比较复杂的东东,没有那么都时间去写。

再说了自己写的总是兼容性上有一定的问题。所以,决定还是移植一个比较好的文件系统吧。



    在网络上的文件系统常见的有四个:UC/FS;ZLG/FS;efsl;FatFS。



第一个:UC/FS。文件系统没得什么说的,UCOSII那个公司开发的,稳定性,兼容性应该都不会差。

第二个:ZLG/FS。周立功的很多的开发板上面都送了这个文件系统的源代码的,在网上找到一个现成的读写硬盘的,只是是基于LPC2200系列的处理器的。

第三个:efsl。是一个开源的项目,免费,只需要提供读扇区和写扇区2个函数。第四个是FatFs,跟efsl一样,也是一个开源的项目,移植的时候比efsl多几个简单的函数。

第四个:FatFS。开源,免费,高效!



通过综合考虑,决定移植第四个。

相关帖子

沙发
北京户口|  楼主 | 2010-7-19 20:34 | 只看该作者
网上移植的文字很多,大家可以参考一下:http://www.cnblogs.com/ylshu/archive/2009/09/25/1574106.html







       在FatFs R0.08中,文件结构稍有改变。如果需要可以到官方网进行下载:http://elm-chan.org/fsw/ff/00index_e.html



FatFs的FatFs有两个文件夹,一个是 doc ,FatFs的说明,包括特性,系统函数,以及可能的一些问题,另一个就是源代码文件夹src了,总共8个文件,diskio.c和diskio.h是硬件层,ff.c和ff.h是FatFs的文件系统层和文件系统的API层,integer.h是文件系统所用到的数据类型的定义,ffconf.h为文件系统配置文件,tff.c和tff.h是Tiny的文件系统层和文件系统的API层,还有一个00readme.txt简要的介绍了FatFSHE FatFs/Tiny,包括他们所支持的API,怎么配置等等。







      移植的问题:







     第一个数据类型:在integer.h里面去定义好数据的类型。这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。







     第二个就是配置:打开ffconf.h(我用的FatFs,不是Tiny,可以在此头文件中进行定义),文件系统的配置裁剪等均在此头文件中进行定义配置。







     第三个函数编写:打开diskio.c,进行底层驱动编写。



disk_initialize  -  Initialize disk drive

disk_status     -  Get disk status

disk_read       -  Read sector(s)

disk_write       -  Write sector(s)

disk_ioctl        -  Control device dependent features

get_fattime     -  Get current time





disk_initialize : 如果不需要可以直接返回0;也可以调用SD初始化函数。







       disk_status:    不需要直接返回。



   



       disk_read:      注意参数,如果读取多个扇区时一定要编写一个读多个扇区的函数,而不能用循环来调用读单个扇区的函数否则效率就不搞了。







       disk_write:      和上面的disk_read函数一样。







       disk_ioctl:       仅仅在格式化的时候被使用,在调试读写的时候,这个函数直接让他返回0就OK 了。







       get_fattime:    获取时间,此函数是通过RTC获取,如果没有此功能的可以用别的方式获取,不用的话,返回0就行。







移植就主要是更改这些,之后就可以运行试试了。

使用特权

评论回复
板凳
北京户口|  楼主 | 2010-7-19 20:34 | 只看该作者
了移植FatFs提高SD卡的读写速度,特定编写一个多扇区读写函数。

使用特权

评论回复
地板
北京户口|  楼主 | 2010-7-19 20:34 | 只看该作者
移植代码待续……

使用特权

评论回复
5
北京户口|  楼主 | 2010-7-19 20:34 | 只看该作者
/**************************************************************************************
* FunctionName   : MMCReadMultipleBolck()
* Description    : 读取一扇区数据到buffer缓冲中
* EntryParameter : NO
* ReturnValue    : 返回操作状态:失败-1;成功-0
**************************************************************************************/
uint8_t MMCReadMultipleBolck(uint32_t addr,uint8_t *buf,uint8_t count)
{
    uint16_t i;



  if (MMCWriteCmd(CMD18,0xFF,addr) != 0x00)           // 发送CMD18
  {
      return 1;                                    // 读取失败
  }



  do
  {
   while (SSPTransceiver(0xFF) != 0xFE)
   {
    ;                                 // 等待数据接受开始,受到0xFE表示开始
   }



   for (i=0; i<512; i++)                        // 读取数据
   {
    *buf++ = SSPTransceiver(0xFF);
   }



   SSPTransceiver(0xFF);                 // 取走CRC字节
   SSPTransceiver(0xFF);



  }while (--count);



  MMCWriteCmd(CMD12,0xFF,0x00);                      // CMD12发送停止命令



  return 0;
}

使用特权

评论回复
6
北京户口|  楼主 | 2010-7-19 20:34 | 只看该作者
**************************************************************************************
* FunctionName : MMCWriteMultipleBlock()
* Description : 把buffer中的数据写入一个扇区中
* EntryParameter : NO
* ReturnValue : NO
**************************************************************************************/
uint8_t MMCWriteMultipleBlock(uint32_t addr,uint8_t *buf,uint8_t count)
{
uint16_t i;
uint8_t tmp;

if (MMCWriteCmd(CMD25,0xFF,addr) != 0x00) // 发送CMD25到SD卡中去
{
return 1; // 写入失败
}

do
{
SSPTransceiver(0xFC); // Send start block token 0xfc (0x11111100)

for (i=0; i<512; i++) // 写入数据
{
SSPTransceiver(*buf++);
}

SSPTransceiver(0xFF); // 写入CRC字节
SSPTransceiver(0xFF);
tmp = SSPTransceiver(0xFF); // 读取XXX0 0101字节
tmp &= 0x1F;

if (tmp != 0x05)
{
return 1; // 写入失败
}

while (SSPTransceiver(0xFF) == 0x00)
{
; // BUSY等待
}
}while (--count);

SSPTransceiver(0xFD); // send 'stop transmission token'
while (SSPTransceiver(0xFF) == 0x00)
{
; // BUSY等待
}

return 0;
}

使用特权

评论回复
7
北京户口|  楼主 | 2010-7-19 20:35 | 只看该作者
添加的SD卡多扇区读写函数:



/**************************************************************************************



* FunctionName : MMCReadMultipleBolck()



* Description :      读取一扇区数据到buffer缓冲中



* EntryParameter : NO * ReturnValue :



返回操作状态:失败-1;成功-0



**************************************************************************************/



uint8_t MMCReadMultipleBolck(uint32_t addr,uint8_t *buf,uint8_t count)



{



uint16_t i;



if (MMCWriteCmd(CMD18,0xFF,addr) != 0x00) // 发送CMD18



{



return 1; // 读取失败



}







do



{



while (SSPTransceiver(0xFF) != 0xFE)



{



; // 等待数据接受开始,受到0xFE表示开始



}



for (i=0; i<512; i++) // 读取数据



{ *buf++ = SSPTransceiver(0xFF); }



SSPTransceiver(0xFF); // 取走CRC字节



SSPTransceiver(0xFF);



}while (--count);



MMCWriteCmd(CMD12,0xFF,0x00); // CMD12发送停止命令



return 0;



}







/**************************************************************************************



* FunctionName : MMCWriteMultipleBlock()



* Description : 把buffer中的数据写入一个扇区中



* EntryParameter : NO



* ReturnValue : NO



**************************************************************************************/



uint8_t MMCWriteMultipleBlock(uint32_t addr,uint8_t *buf,uint8_t count)



{



uint16_t i; uint8_t tmp;







if (MMCWriteCmd(CMD25,0xFF,addr) != 0x00) // 发送CMD25到SD卡中去



{



return 1; // 写入失败



}







do



{



SSPTransceiver(0xFC); // Send start block token 0xfc (0x11111100)



for (i=0; i<512; i++) // 写入数据



{



SSPTransceiver(*buf++);



}







SSPTransceiver(0xFF); // 写入CRC字节



SSPTransceiver(0xFF);



tmp = SSPTransceiver(0xFF); // 读取XXX0 0101字节



tmp &= 0x1F;



if (tmp != 0x05)



{



return 1; // 写入失败



}



while (SSPTransceiver(0xFF) == 0x00)



{



; // BUSY等待



}



}while (--count);



SSPTransceiver(0xFD); // send 'stop transmission token'



while (SSPTransceiver(0xFF) == 0x00)



{



; // BUSY等待



}



return 0;



}

使用特权

评论回复
8
北京户口|  楼主 | 2010-7-19 20:35 | 只看该作者
移植代码:







/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/



DSTATUS disk_initialize (
BYTE drv            /* Physical drive nmuber (0..) */
)
{
DSTATUS stat;



if (drv)
{
  return STA_NOINIT;         /* Supports only single drive */
}



    if (!MMCInit())                 /* Initialization succeded */
{
     stat &= ~STA_NOINIT;        /* Clear STA_NOINIT */
}



return stat;
}







/*-----------------------------------------------------------------------*/
/* Return Disk Status                                                    */
/*-----------------------------------------------------------------------*/



DSTATUS disk_status (
BYTE drv  /* Physical drive nmuber (0..) */
)
{
DSTATUS stat;



if (drv)
{
  return STA_NOINIT;     /* Supports only single drive */
}
else
{
  stat = 0;
}



return stat;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/



DRESULT disk_read (
BYTE drv,  /* Physical drive nmuber (0..) */
BYTE *buff,  /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count  /* Number of sectors to read (1..255) */
)
{
if (drv || (!count))
{
  return RES_PARERR;
}
//if (Stat & STA_NOINIT) return RES_NOTRDY;



if (count == 1)  /* Single block read   */
{
   if (!MMCReadSingleBolck(sector,buff))
   {
     count = 0;
   }
}
else             /* Multiple block read */
{
  if(!MMCReadMultipleBolck(sector,buff,count))
  {
        count = 0;
  }
}



return (count ? RES_ERROR : RES_OK);
}

使用特权

评论回复
9
北京户口|  楼主 | 2010-7-19 20:36 | 只看该作者
/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
/* The FatFs module will issue multiple sector transfer request
/  (count > 1) to the disk I/O layer. The disk function should process
/  the multiple sector transfer properly Do. not translate it into
/  multiple single sector transfers to the media, or the data read/write
/  performance may be drasticaly decreased. */



#if _READONLY == 0
DRESULT disk_write (
BYTE drv,   /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector,  /* Sector address (LBA) */
BYTE count   /* Number of sectors to write (1..255) */
)
{
if (drv || (!count))
{
  // translate the reslut code here
  return RES_PARERR;
}
//if (Stat & STA_NOINIT) return RES_NOTRDY;



if (count == 1)  /* Single block write */
{
   if (!MMCWriteSingleBlock(sector,buff))
   {
     count = 0;
   }
}
else             /* Multiple block write */
{
  if(!MMCWriteMultipleBlock(sector,buff,count))
  {
        count = 0;
  }
}



return (count ? RES_ERROR : RES_OK);
}
#endif /* _READONLY */







/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/



DRESULT disk_ioctl (
BYTE drv,  /* Physical drive nmuber (0..) */
BYTE ctrl,  /* Control code */
void *buff  /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16];
DWORD csize;



if (drv)
{
  return RES_PARERR;
}
//if (stat & STA_NOINIT) return RES_NOTRDY;



res = RES_ERROR;
//if (Stat & STA_NOINIT) return RES_NOTRDY;



switch (ctrl)
{
     case CTRL_SYNC       : res = RES_OK; break;
     case GET_SECTOR_COUNT: /* Get number of sectors on the disk (WORD) */
          if((MMCWriteCmd(CMD9,0x95,0x00) == 0) && MMCCSD_CID(CMD9, csd))
          {
              if((csd[0] >> 6) == 1) /* SDC ver 2.00 */
              {
                  csize = csd[9] + ((WORD)csd[8] << 8) + 1;
                  *(DWORD*)buff = (DWORD)csize << 10;
              }
              else /* MMC or SDC ver 1.XX */
              {
                  n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                  csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
                  *(DWORD*)buff = (DWORD)csize << (n - 9);
              }
              res = RES_OK;
          }
          break;



     case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */
                      *(WORD*)buff = 512;
                      res = RES_OK;
                      break;



     case GET_BLOCK_SIZE  : if ((MMCWriteCmd(CMD9,0x95,0x00) == 0) && MMCCSD_CID(CMD9, csd)) /* Read CSD */
                         {
                          *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
                          res = RES_OK;
                         }
                            break;



     default              : res = RES_PARERR; break;
}



return res;
}




编写好以上代码后就可以用API函数进行文件操作了。

使用特权

评论回复
10
北京户口|  楼主 | 2010-7-19 20:36 | 只看该作者
FatFs API函数应用之一







// 文件读取函数







/**************************************************************************************
* FunctionName   : AppFileRead()
* Description    : 读取文件
* EntryParameter : fileName - 需要读取的文件名或路径
* ReturnValue    : 成功返回-0;失败返回-1
**************************************************************************************/
uint8_t AppFileRead(const TCHAR *fileName)
{
FATFS fs;               /*Work area (file system object) for logical drive*/
    FIL file;               /*file objects*/
UINT  br;               /*File R/W count*/
//FRESULT res;          /* FatFs function common result code */




/*Register a work area for logical drive 0*/
    f_mount(0, &fs);



    /*Create file*/
    if(f_open(&file, fileName, FA_READ))
    {
  return 1;
    }
else
{
  do
  {
   AppClearBuffer(0x00);   // 缓冲清零
   if(f_read(&file, Buffer, 512, &br))
   {
    return 1;
   }
   else
   {
    //UARTSend(Buffer, 512);
    UARTSendString((const CHAR *)Buffer);  // 发送读取文件
   }
  } while (br);  // 判断是否读完(br == 0,表示读取完成)



  /*Close all files*/
      f_close(&file);    // 关闭文件,必须和f_open函数成对出现
}



    /*Unregister a work area before discard it*/
    f_mount(0, 0);



    return 0;
}







在编写这个函数时要注意顺序:



1.  f_mount(0, &fs)                                        // 申请空间



2.  f_open(&file, fileName, FA_READ)          // 打开文件



3.  f_read(&file, Buffer, 512, &br)                 // 读文件
4.  f_close(&file)                                           // 关闭文件



5.  f_mount(0, 0)                                          // 释放空间

使用特权

评论回复
11
北京户口|  楼主 | 2010-7-19 20:36 | 只看该作者
FatFs API函数应用之二







文件扫描











/**************************************************************************************
* FunctionName   : AppScanFiles()
* Description    : 扫描文件
* EntryParameter : path - 路径
* ReturnValue    : 成功返回-0;失败返回-1
**************************************************************************************/
FRESULT AppScanFiles(CHAR* path)
{
FATFS fs;               /*Work area (file system object) for logical drive*/
    FRESULT res;
    FILINFO fno;
    DIR dir;
    int length;             // 目录长度
    CHAR *fn;
    CHAR pathName[100];     // 存放目录



#if _USE_LFN
    static char lfn[_MAX_LFN * (_DF1S ? 2 : 1) + 1];
    fno.lfname = lfn;
    fno.lfsize = sizeof(lfn);
#endif



    f_mount(0, &fs);                               // 一定不能少



strcpy(pathName,path);
res = f_opendir(&dir, pathName);



    res = f_opendir(&dir, path);                   // 打开目录
    if (res == FR_OK)
    {
     length = strlen(path);                     // 获取目录长度
        for (;;)
        {
            res = f_readdir(&dir, &fno);           // 读目录
            if (res != FR_OK || fno.fname[0] == 0)
            {
             break;
            }



            if (fno.fname[0] == '.')               // 一个点代表当前目录,两个点代表上级目
            {
             continue;
            }



#if _USE_LFN   // 长文件名
            fn = *fno.lfname ? fno.lfname : fno.fname;
#else          // 短文件名
            fn = fno.fname;
#endif



            if (fno.fattrib & AM_DIR)             // Directory
            {
                //sprintf(&path[length], "/%s", fn);



             strcat(pathName,"/");
             strcat(pathName,fn);
             res = AppScanFiles(pathName);
                //res = AppScanFiles(path);
                if (res != FR_OK) break;
                path[length] = 0;
            }
            else
            {
             UARTSendString(fn);                // 串口输出文件名
            }
        }
    }



    f_mount(0, 0);
    return res;
}







说明:此函数是通过FATFS的例程更改的。这个例程有几个问题:



1. f_mount();没有调用这个函数,所以总是会出错的。



2. sprintf(&path[length], "/%s", fn);这个库函数不能调用,否则会出错。



3. 用strcpy()和strcat()代替sprintf()的功能。

使用特权

评论回复
12
北京户口|  楼主 | 2010-7-19 20:37 | 只看该作者
实验结果:


2.jpg (29.52 KB)
2010-5-29 16:29






1.jpg (49.16 KB)
2010-5-29 16:26

使用特权

评论回复
13
北京户口|  楼主 | 2010-7-19 20:37 | 只看该作者
FatFs API函数应用之三







写文件







/**************************************************************************************
* FunctionName   : AppFileWrite()
* Description    : 写一个文件
* EntryParameter : fileName - 需要读取的文件名或路径;buf - 写入的数据;modeFlags - 写入模式
* ReturnValue    : 成功返回-0;失败返回-1
**************************************************************************************/
// modeFlags : FA_OPEN_ALWAYS / FA_CREATE_NEW / FA_CREATE_ALWAYS
uint8_t AppFileWrite(const TCHAR *fileName, uint8_t *buf,BYTE modeFlags)
{
FATFS fs;               /*Work area (file system object) for logical drive*/
    FIL file;               /*file objects*/
UINT  bw;               /*File R/W count*/



/*Register a work area for logical drive 0*/
    f_mount(0, &fs);



    /*Create file*/
    if(f_open(&file, fileName, modeFlags|FA_WRITE))
    {
  return 1;
    }
else
{
  do
  {
   if(f_write(&file, buf, 512, &bw))
   {
    return 1;
   }
  } while (bw < 512);  // 判断是否读完(bw < 512,表示写入完成)



  /*Close all files*/
      f_close(&file);      // 关闭文件,必须和f_open函数成对出现
}



    /*Unregister a work area before discard it*/
    f_mount(0, 0);



    return 0;
}

使用特权

评论回复
14
北京户口|  楼主 | 2010-7-19 20:37 | 只看该作者
程序还没有完成,还有些函数需要编写。

使用特权

评论回复
15
北京户口|  楼主 | 2010-7-19 20:38 | 只看该作者
FatFs API函数应用之四







文件删除







/**************************************************************************************
* FunctionName   : AppDeleteFile()
* Description    : 删除一个文件
* EntryParameter : fileName - 需要删除的文件
* ReturnValue    : 返回操作状态
**************************************************************************************/
FRESULT AppDeleteFile(const TCHAR *fileName)
{
FATFS fs;
FRESULT res;



res = f_mount(0,&fs);
if (res != FR_OK)
{
  return res;
}



res = f_unlink(fileName);  // removes an object



f_mount(0,NULL);



return res;
}

使用特权

评论回复
16
北京户口|  楼主 | 2010-7-19 20:38 | 只看该作者
FatFs API函数应用之五









新建文件







/**************************************************************************************
* FunctionName   : AppCreatFile()
* Description    : 创建一个文件
* EntryParameter : fileName - 需要创建的文件名,8+3格式
* ReturnValue    : 返回操作状态
**************************************************************************************/
FRESULT AppCreatFile(const TCHAR *fileName)
{
FIL file;
FATFS fs;
uint8_t res;



res = f_mount(0,&fs);
if (res != FR_OK)
{
  return res;
}



res = f_open(&file, fileName, FA_READ|FA_WRITE|FA_CREATE_NEW);



if (res == FR_OK)
{
  res = f_close(&file);
}



f_mount(0, 0);
return res;
}

使用特权

评论回复
17
北京户口|  楼主 | 2010-7-19 20:38 | 只看该作者
FatFs API函数应用之六







新建目录











/**************************************************************************************
* FunctionName   : AppCreatDir()
* Description    : 创建目录
* EntryParameter : path - 目录路径
* ReturnValue    : 操作结果
**************************************************************************************/
FRESULT AppCreatDir(const TCHAR *path)
{
FATFS fs;
FRESULT res;



res = f_mount(0,&fs);
if (res != FR_OK)
{
  return res;
}



res = f_mkdir(path);



f_mount(0,NULL);



return res;
}

使用特权

评论回复
18
北京户口|  楼主 | 2010-7-19 20:38 | 只看该作者
FatFs API函数应用之七




获取SD卡信息







/**************************************************************************************
* FunctionName   : GetDiskInfo()
* Description    : 磁盘信息
* EntryParameter : totSpace - 总空间;freSpace - 剩余空间                                   单位MB
* ReturnValue    : 返回操作状态
**************************************************************************************/
FRESULT GetDiskInfo(uint32_t *totSpace,uint32_t *freSpace)
{
FATFS fs;
FATFS *fls = &fs;
FRESULT res;
DWORD fre_clust,tot_sect,fre_sect;



f_mount(0,&fs);



res = f_getfree("0:",&fre_clust,&fls);             // 必须是根目录,默认磁盘0
if (res == FR_OK)
{
  tot_sect = (fls->n_fatent - 2) * fls->csize;   // 总扇区数
  fre_sect = fre_clust * fls->csize;             // 可用的扇区数



  *totSpace = tot_sect/2/1024;                   // 计算空间,单位MB
  *freSpace = fre_sect/2/1024;
}



f_mount(0,NULL);



return res;
}

使用特权

评论回复
19
我是土匪| | 2010-7-20 09:23 | 只看该作者
很好,收藏!

使用特权

评论回复
20
yytdragon| | 2010-7-20 10:57 | 只看该作者
mark!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

107

主题

521

帖子

1

粉丝