[原创] LPC1114 FAT下读写SD卡

zhaojun_xf   2010-5-20 07:57 楼主

前一贴里利用的是SSP总线下进行扇区的读写,这一贴将在FAT文件系统下进行SD卡的读写。

 

       本来想自己写个简单的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。开源,免费,高效!

 

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

[ 本帖最后由 zhaojun_xf 于 2010-5-20 08:43 编辑 ]

回复评论 (83)

      网上移植的文字很多,大家可以参考一下: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就行。

     

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

     

     

    [ 本帖最后由 zhaojun_xf 于 2010-5-20 14:34 编辑 ]
    点赞  2010-5-20 07:58
    为了移植FatFs提高SD卡的读写速度,特定编写一个多扇区读写函数。

    [ 本帖最后由 zhaojun_xf 于 2010-5-20 22:22 编辑 ]
    点赞  2010-5-20 07:58
    移植代码待续……

    [ 本帖最后由 zhaojun_xf 于 2010-5-20 22:26 编辑 ]
    点赞  2010-5-20 07:58
    占位
    点赞  2010-5-20 10:32

    /**************************************************************************************
    * 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;
    }

    点赞  2010-5-21 06:13
    /**************************************************************************************
    * 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;
    }
    点赞  2010-5-21 06:41
    早上发的两贴看看是没有了。
    点赞  2010-5-21 08:40

    应用程序接口:

    [ 本帖最后由 zhaojun_xf 于 2010-5-21 16:58 编辑 ]
    • 1.bmp
    点赞  2010-5-21 16:55

    添加的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;

    }

    [ 本帖最后由 zhaojun_xf 于 2010-5-22 08:33 编辑 ]
    点赞  2010-5-21 22:39

    移植代码:

     

    /*-----------------------------------------------------------------------*/
    /* 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);
    }

    [ 本帖最后由 zhaojun_xf 于 2010-5-25 07:55 编辑 ]
    点赞  2010-5-25 07:53

    /*-----------------------------------------------------------------------*/
    /* 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函数进行文件操作了。

    点赞  2010-5-25 07:55
    已经有一定的进展!!!!!!!!!!
    点赞  2010-5-25 13:32

    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)                                          // 释放空间

     

     

     

    [ 本帖最后由 zhaojun_xf 于 2010-5-26 07:51 编辑 ]
    点赞  2010-5-26 07:49
    好东西,收藏之.........
    不断地学习,才会有创新! 淘宝小店:手机、qq点卡、游戏点卡自动充值 http://shop63727265.taobao.com/
    点赞  2010-5-26 13:45
    有空试试,谢谢!
    动手创造个性自我 http://home.eeworld.com.cn/?95709
    点赞  2010-5-27 17:35

    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()的功能。

     

    [ 本帖最后由 zhaojun_xf 于 2010-5-29 14:28 编辑 ]
    点赞  2010-5-29 13:08

    实验结果:

    2.jpg

     

    1.jpg

    [ 本帖最后由 zhaojun_xf 于 2010-5-29 16:29 编辑 ]
    点赞  2010-5-29 16:26

    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;
    }

    点赞  2010-5-30 07:17

    lpc1114

    楼主,能否提供移植SD卡例程,谢谢!
    点赞  2010-6-2 14:51
    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
      写回复