【IoT】FATFS 文件系统 SPI 方式移植

2019-04-15 14:16发布

1、FATFS 简介 FatFs Module是一种完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计。 完全用标准C语言编写,所以具有良好的硬件平台立性,只需要简单修改就可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列嵌入式设备。 支持FATl2、FATl6和FAT32,支持多个存储媒介、有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。 2、移植准备 1)FATFS源代码的获取,可以到官网下载:http://elm-chan.org/fsw/ff/00index_e.html 最新版本是R0.09版本; 2)解压文件会得到两个文件夹,一个是doc文件夹,这里是FATFS的一些使用文档和说明,以后在文件编程的时候可以查看该文档。 另一个是src文件夹,里面就是我们所要的源文件。 3)建立一个STM32的工程,为方便调试,我们应重载printf()底层函数实现串口打印输出,可以参考已经建立好的printf()打印输出工程。 3、移植 1)在已经建立好的工程目录User文件夹下新建两个文件夹,FATFS_V0.09和SPI_SD_Card,FATFS_V0.09用于存放FATFS源文件,SPI_SD_Card用于存放SPI的驱动文件。 2)将ff.c添加到工程文件夹中,并新建diskio.c文件,在diskio.c文件中实现五个函数: DSTATUS disk_initialize (BYTE);//SD卡的初始化 DSTATUS disk_status (BYTE);//获取SD卡的状态,这里可以不用管 DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);//从SD卡读取数据 DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);//将数据写入SD卡,若该文件系统为只读文件系统则不用实现该函数 DRESULT disk_ioctl (BYTE, BYTE, void*);//获取SD卡文件系统相关信息 3)函数实现 FATFS初始化函数: /* Physical drive nmuber (0..) */ DSTATUS disk_initialize ( BYTE drv ) { switch (drv) { case 0 : return RES_OK; case 1 : return RES_OK; case 2 : return RES_OK; case 3 : return RES_OK; default: return STA_NOINIT; } }
FATFS状态获取函数: /* Physical drive nmuber (0..) */ DSTATUS disk_status ( BYTE drv ) {         switch (drv)         {                 case 0 :                         return RES_OK;                 case 1 :                         return RES_OK;                 case 2 :                         return RES_OK;                 default:                         return STA_NOINIT;         } } FATFS底层读数据函数: 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( !count )    {            return RES_PARERR;  /* count不能等于0,否则返回参数错误 */    }    switch (drv)    {            case 0:                if(count==1)            /* 1个sector的读操作 */                {                            return RES_OK;                }                else                    /* 多个sector的读操作 */                {                            return RES_OK;                }            case 1:                if(count==1)            /* 1个sector的读操作 */                {                            return RES_OK;                }                else                    /* 多个sector的读操作 */                {                            return RES_OK;                }            default:                    return RES_ERROR;    } } FATFS底层写数据函数: 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( !count )   {           return RES_PARERR;  /* count不能等于0,否则返回参数错误 */   }   switch (drv)   {           case 0:               if(count==1)            /* 1个sector的写操作 */               {                           return RES_OK;               }               else                    /* 多个sector的写操作 */               {                           return RES_OK;               }           case 1:               if(count==1)            /* 1个sector的写操作 */               {                           return RES_OK;               }               else                    /* 多个sector的写操作 */               {                           return RES_OK;               }           default:return RES_ERROR;   } } FATFS磁盘控制函数: DRESULT disk_ioctl (                 BYTE drv,                /* Physical drive nmuber (0..) */                 BYTE ctrl,                /* Control code */                 void *buff                /* Buffer to send/receive control data */         ) {         if (drv==0)         {                 switch (ctrl)                 {                         case CTRL_SYNC :                                 return RES_OK;                         case GET_SECTOR_COUNT :                         return RES_OK;                         case GET_BLOCK_SIZE :                         return RES_OK;                         case CTRL_POWER :                                 break;                         case CTRL_LOCK :                                 break;                         case CTRL_EJECT :                                 break;                 /* MMC/SDC command */                         case MMC_GET_TYPE :                                 break;                         case MMC_GET_CSD :                                 break;                         case MMC_GET_CID :                                 break;                         case MMC_GET_OCR :                                 break;                         case MMC_GET_SDSTAT :                                 break;                 }     }else if(drv==1){                 switch (ctrl)                 {                         case CTRL_SYNC :                                 return RES_OK;                         case GET_SECTOR_COUNT :                         return RES_OK;                         case GET_SECTOR_SIZE :                                 return RES_OK;                         case GET_BLOCK_SIZE :                         return RES_OK;                         case CTRL_POWER :                                 break;                         case CTRL_LOCK :                                 break;                         case CTRL_EJECT :                                 break;                 /* MMC/SDC command */                         case MMC_GET_TYPE :                                 break;                         case MMC_GET_CSD :                                 break;                         case MMC_GET_CID :                                 break;                         case MMC_GET_OCR :                                 break;                         case MMC_GET_SDSTAT :                                 break;                 }         }         else{                 return RES_PARERR;         }         return RES_PARERR; } 以上函数都只是实现一个框架,并没有做实际的事情,下一步就需要把操作SD卡的程序填充在这个框架里面。 4)实现 disk_initialize() 函数 该函数在挂载文件系统的时候会被调用,主要是实现读写SD卡前对SD卡进行初始化,根据SD卡的传输协议,我们按照如下步骤初始化SD卡: a、判断SD卡是否插入,可以通过检查SD卡卡座的CD脚电平进行判断,一般插入卡后该引脚会变成低电平; b、稍微延时一段时间后发送至少74个时钟给SD卡; c、发送CMD0命令给SD卡,直到SD卡返回0x01为止,这里可以循环多次发送;   程序如下: /* Start send CMD0 till return 0x01 means in IDLE state */                 for(retry=0; retry<0xFFF; retry++)                 {                         r1 = MSD0_send_command(CMD0, 0, 0x95);                         if(r1 == 0x01)                         {                                 retry = 0;                                 break;                         }                 } d、发送CMD8获取卡的类型,不同类型的卡其初始化方式有所不同; e、根据卡的类型对卡进行初始化。具体初始化方式可以参考附件程序; 注:在初始化SD卡之前应该初始化SPI接口和相关的管脚。 实现后的程序如下: DSTATUS disk_initialize (                 BYTE drv                                /* Physical drive nmuber (0..) */         )         {                 int Status;                 switch (drv)                 {                         case 0 :                                 Status = MSD0_Init();                                 if(Status==0){                                         return RES_OK;                                 }else{                                         return STA_NOINIT;                                 }                         case 1 :                                 return RES_OK;                         case 2 :                                 return RES_OK;                         case 3 :                                 return RES_OK;                         default:                                 return STA_NOINIT;                 }         } MSD0_Init()函数在SPI_MSD0_Driver.c文件中实现。 5)实现disk_read()函数 该函数是读取SD卡扇区数据的函数,根据SD卡数据传输协议可知有读取单扇区和读取多扇区两种操作模式,为提高读文件的速度应该实现读取多扇区函数。 实现后的程序如下: 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) */         )         {                 int Status;                 if( !count )                 {                         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */                 }                 switch (drv)                 {                         case 0:                             if(count==1)            /* 1个sector的读操作 */                             {                                         Status =  MSD0_ReadSingleBlock( sector ,buff );                                         if(Status == 0){                                                 return RES_OK;                                         }else{                                                 return RES_ERROR;                                         }                             }                             else                    /* 多个sector的读操作 */                             {                                         Status = MSD0_ReadMultiBlock( sector , buff ,count);                                         if(Status == 0){                                                 return RES_OK;                                         }else{                                                 return RES_ERROR;                                         }                             }                         case 1:                             if(count==1)            /* 1个sector的读操作 */                             {                                         return RES_OK;                             }                             else                    /* 多个sector的读操作 */                             {                                         return RES_OK;                             }                         default:                                 return RES_ERROR;                 }         } MSD0_ReadSingleBlock()和MSD0_ReadMultiBlock()函数都是SD卡操作的底层函数,我们在SPI_MSD0_Driver.c文件中实现。 6)实现disk_write()函数 该函数主要实现对SD卡进行写数据操作,和读数据操作一样也分单块写和多块写,建议实现多块写的方式,这样可以提高写数据速度。 实现后的程序如下: 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) */         )         {                 int Status;                 if( !count )                 {                         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */                 }                 switch (drv)                 {                         case 0:                             if(count==1)            /* 1个sector的写操作 */                             {                                         Status = MSD0_WriteSingleBlock( sector , (uint8_t *)(&buff[0]) );                                         if(Status == 0){                                                 return RES_OK;                                         }else{                                                 return RES_ERROR;                                         }                             }                             else                    /* 多个sector的写操作 */                             {                                         Status = MSD0_WriteMultiBlock( sector , (uint8_t *)(&buff[0]) , count );                                         if(Status == 0){                                                 return RES_OK;                                         }else{                                                 return RES_ERROR;                                         }                             }                         case 1:                             if(count==1)            /* 1个sector的写操作 */                             {                                         return RES_OK;                             }                             else                    /* 多个sector的写操作 */                             {                                         return RES_OK;                             }                         default:return RES_ERROR;                 }         } MSD0_WriteSingleBlock()和MSD0_WriteMultiBlock()函数都是SD卡操作的底层函数,我们在SPI_MSD0_Driver.c文件中实现。 7)实现disk_ioctl()函数 该函数在磁盘格式化、获取文件系统信息等操作时会被调用。 实现后的程序如下: DRESULT disk_ioctl (                 BYTE drv,                /* Physical drive nmuber (0..) */                 BYTE ctrl,                /* Control code */                 void *buff                /* Buffer to send/receive control data */         )         {                 if (drv==0)                 {                         MSD0_GetCardInfo(&SD0_CardInfo);                         switch (ctrl)                         {                                 case CTRL_SYNC :                                         return RES_OK;                                 case GET_SECTOR_COUNT :                                         *(DWORD*)buff = SD0_CardInfo.Capacity/SD0_CardInfo.BlockSize;                                 return RES_OK;                                 case GET_BLOCK_SIZE :                                         *(WORD*)buff = SD0_CardInfo.BlockSize;                                 return RES_OK;                                 case CTRL_POWER :                                         break;                                 case CTRL_LOCK :                                         break;                                 case CTRL_EJECT :                                         break;                         /* MMC/SDC command */                                 case MMC_GET_TYPE :                                         break;                                 case MMC_GET_CSD :                                         break;                                 case MMC_GET_CID :                                         break;                                 case MMC_GET_OCR :                                         break;                                 case MMC_GET_SDSTAT :                                         break;                         }             }else if(drv==1){                         switch (ctrl)                         {                                 case CTRL_SYNC :                                         return RES_OK;                                 case GET_SECTOR_COUNT :                                 return RES_OK;                                 case GET_SECTOR_SIZE :                                         return RES_OK;                                 case GET_BLOCK_SIZE :                                 return RES_OK;                                 case CTRL_POWER :                                         break;                                 case CTRL_LOCK :                                         break;                                 case CTRL_EJECT :                                         break;                         /* MMC/SDC command */                                 case MMC_GET_TYPE :                                         break;                                 case MMC_GET_CSD :                                         break;                                 case MMC_GET_CID :                                         break;                                 case MMC_GET_OCR :                                         break;                                 case MMC_GET_SDSTAT :                                         break;                         }                 }                 else{                         return RES_PARERR;                 }                 return RES_PARERR;         } MSD0_GetCardInfo()函数也在SPI_MSD0_Driver.c文件中实现,其中SD0_CardInfo为PMSD_CARDINFO类型的全局变量,它在SPI_MSD0_Driver.h文件中被定义。 8)到此diskio.c这个文件中的所有函数就已经实现,下一步就是实现SPI_MSD0_Driver.c文件中的相关函数。 4、文件系统测试 1)测试写文件 测试代码如下:        printf("write file test...... ");         res = f_open(&fdst, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);         if(res != FR_OK){                 printf("open file error : %d ",res);         }else{                 res = f_write(&fdst, textFileBuffer, sizeof(textFileBuffer), &bw);               /* Write it to the dst file */                 if(res == FR_OK){                         printf("write data ok! %d ",bw);                 }else{                         printf("write data error : %d ",res);                 }                 /*close file */                 f_close(&fdst);         } 注意:成功打开文件后一定要调用f_close()函数,否则数据无法写入SD卡中。 2)测试读文件         printf("read file test...... ");         res = f_open(&fsrc, "0:/test.txt", FA_OPEN_EXISTING | FA_READ);         if(res != FR_OK){                 printf("open file error : %d ",res);         }else{                 res = f_read(&fsrc, buffer, sizeof(textFileBuffer), &br);     /* Read a chunk of src file */                 if(res==FR_OK){                         printf("read data num : %d ",br);                         printf("%s ",buffer);                 }else{                         printf("read file error : %d ",res);                 }                 /*close file */                 f_close(&fsrc);         }   refer: http://bbs.21ic.com/icview-883729-1-1.html