STM32的FATFS文件系统移植笔记一、序言经常在网上、群里看到很多人问关于STM32的FATFS文件系统移植的问题,刚好自己最近也在调试这个程序,为了让大家少走弯路,我把我的调试过程和方法也贡献给大家。
二、FATFS简介FatFs Module是一种完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计。
它完全用标准C语言编写,所以具有良好的硬件平台独立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列单片机上而只需做简单的修改。
它支持FATl2、FATl6和FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。
三、移植准备1、FATFS源代码的获取,可以到官网下载:/fsw/ff/00index_e.html最新版本是R0.09版本,我们就移植这个版本的。
2、解压文件会得到两个文件夹,一个是doc文件夹,这里是FATFS的一些使用文档和说明,以后在文件编程的时候可以查看该文档。
另一个是src文件夹,里面就是我们所要的源文件。
3、建立一个STM32的工程,为方便调试,我们应重载printf()底层函数实现串口打印输出。
可以参考已经建立好的printf()打印输出工程:.viewtool./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卡文件系统相关信息6.复制代码<IGNORE_JS_OP>图13、初步实现以上五个函数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. }复制代码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.33. default:34. return RES_ERROR;35.}36. }复制代码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获取卡的类型,不同类型的卡其初始化方式有所不同。