在STM32中移植FATFS文件系统
更新时间:2023-05-08 07:36:01 阅读量: 实用文档 文档下载
.
. STM32的FATFS文件系统移植笔记
一、序言
经常在网上、群里看到很多人问关于STM32的FATFS文件系统移植的问题,刚好自己最近也在调试这个程序,为了让大家少走弯路,我把我的调试过程和方法也贡献给大家。
二、FATFS简介
FatFs Module是一种完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C语言编写,所以具有良好的硬件平台独立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列单片机上而只需做简单的修改。它支持FATl2、FATl6和FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。
三、移植准备
1、FATFS源代码的获取,可以到官网下载:966c28fbbdd126fff705cc1755270722182e59ed/fsw/ff/00index_e最新版本是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.
复制代码
.
图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. }
复制代码
.
. 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获取卡的类型,不同类型的卡其初始化方式有所不同。
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.
44. default:
45. return RES_ERROR;
46. }
47. }
.
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
六、中文长文件名支持
1、要支持长文件名需要在ffconf.h文件中修改两个宏定义。如下为我们修改后的宏定义。.
.
#define _CODE_PAGE 936
#define _USE_LFN 1 /* 0 to 3 */
2、添加支持中文编码的文件
重新编译会发现有如图3的错误。原因是要支持中文文件名需要包含另外一个文件cc936.c,该文件在FATFS文件系统源码的.\src\option目录下,将它添加到工程文件目录FATFS中。如图4是我们添加文件后的工程文件结构,再次编译就通过了。如图5所示。我们发现增加这个文件后代码量增加了很多,主要原因是这个文件是我们支持中文所需要的中文编码文件。
图3
图4
.
.
图5
3、再次下载到板子中运行,发现中文的长文件名显示正常了。如图6所示。
4、若不需要支持中文长文件名而只支持英文长文件名则可以将宏定义做如下修改:
#define _CODE_PAGE 437
#define _USE_LFN 1 /* 0 to 3 */
同时将ccsbcs.c添加到工程目录中,这样就可以减小很多大代码量。将程序下载板子后再次运行结果如图7所示,可以看到可以支持英文的长文件名。
.
.
.
正在阅读:
在STM32中移植FATFS文件系统05-08
《计算机网络》期末考试复习的题112-09
乒乓球历险记作文500字07-13
汽车制造工艺学习题集11-26
2015-2016年文化传媒行业分析报告09-13
东孟小学思政课实施方案三篇05-08
山西省第十三届初中生物竞赛试题及答案06-02
惠斯登电桥测电阻实验的不确定度分析06-07
2016年9月统考英语B英文写作10-19
- 教学能力大赛决赛获奖-教学实施报告-(完整图文版)
- 互联网+数据中心行业分析报告
- 2017上海杨浦区高三一模数学试题及答案
- 招商部差旅接待管理制度(4-25)
- 学生游玩安全注意事项
- 学生信息管理系统(文档模板供参考)
- 叉车门架有限元分析及系统设计
- 2014帮助残疾人志愿者服务情况记录
- 叶绿体中色素的提取和分离实验
- 中国食物成分表2020年最新权威完整改进版
- 推动国土资源领域生态文明建设
- 给水管道冲洗和消毒记录
- 计算机软件专业自我评价
- 高中数学必修1-5知识点归纳
- 2018-2022年中国第五代移动通信技术(5G)产业深度分析及发展前景研究报告发展趋势(目录)
- 生产车间巡查制度
- 2018版中国光热发电行业深度研究报告目录
- (通用)2019年中考数学总复习 第一章 第四节 数的开方与二次根式课件
- 2017_2018学年高中语文第二单元第4课说数课件粤教版
- 上市新药Lumateperone(卢美哌隆)合成检索总结报告
- 移植
- 文件
- 系统
- STM32
- FATFS
- 人教部编版八年级语文上册第六单元第22课《孟子三章》复习知识点总结整理
- 承重脚手架方案模板
- 北师大版初一数学下册5.3简单的轴对称图形(2)
- 2019-2020年七年级上学期第二次月考英语试卷
- 初中语文诗歌鉴赏+文言文阅读练习题及答案及解析
- GF-1000 GSM MODEM短信收发-例程
- 国企入党思想汇报范文3篇
- 幼儿建构游戏中教师的指导策略资料
- 一元一次方程30道题含答案资料
- 医保基金控费工作汇报(共4篇)
- 高级中学信息技术考点完整
- 最全面保洁工作手册
- 工程项目施工成本控制管理制度
- 2020秋季秋中心小学安全工作计划
- 长丰县中小学教学视导工作实施方案
- 评测绅宝X25 1 5L 颜值不错 内饰像奔驰
- 另类心电图学讲义11
- 我血泪教训,重要事情说三遍,不要让婆家帮忙装修,亮瞎我双眼了大全
- 最新中职英语高考复习模拟试题:阅读理解(I4)
- 日照小学教师招聘2017年考试真题及答案解析【word版】.docx