Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
Filesystem
[新手试水] LPC1768_Nano3_FATFS
发布于 2018-03-27 19:03:03 浏览:3806
订阅该版
* 本帖最后由 wlof 于 2018-3-27 19:03 编辑 * **第八章 添加FATFS**[align=center]Wlof摘要:本文首先介绍如何将FTAFS组件添加到RTT_Nano3上使用,然后对FATFS使用过程中应当注意的事项进行了说明,针对SD热插拔情况下的使用给了出了相应的处理方法。 **8.1 起源**数据存储和导出功能在很多设备上都有了,数据保存在Flash芯片上,然后通过SD卡导出来,这种应用非常常见,主要用来验证数据的一致性使用,和对数据文件进行配置,应用程序进行升级等等操作,都可能要用到SD卡,主要是它便宜,容量大,低级的单片机可以用SPI接口进行操作等。本文在前节添加完DFS的基础上进行操作,为什么不合并为一个呢?主要也是考虑到,那个DFS不是专门为FATFS而做的,它下面有很多很多的文件系统呢!搞在一起,会让人误解,故而分之为二。 **8.2 FATFS和elmfat.c**位于dfs\filesystems\elmfat下,fatfs这个东西,在其官方网上就可下载到最新版本,目前是0.13a,RTT中带的目录版本是0.12b。以前对于fatfs的使用,是**将deskio.c文件进行改写来完成的**。而RTT这里已经都做好了呢,我们直接用就可以了,当然如果想要用最新fs,可能还要自己花点功夫去弄一下,等以后有空了,我也好好看一下,怎么回事。 怎么理解这个**ff.c和elmfat.c**的关系呢?我们打开看一下就知道了,那个elmfat.c中其实是实现了ff.c的接口的了,就是那些**disk**的函数,以前我们就是写那些函数完成移植的。它还包含了其他函数,这些函数是干什么的呢?OMG,看了一下,它又调用了fatfs的函数,**相当于对fatfs进行了一次封装**,简单的说,它就是一个中间件,中间接口层,肯定是用来和那个dfs接口匹配的。这样做有什么好处呢?上层是操作系统,下层是fatfs人家也称为文件系统,也就是说两个东西各成系统,维护都搞得好好的了呢,让谁变动一下都难受。怎么办呢?就搞这么一个中间接口层的东东,接口不一样是不是?没有有关系,转换一下呗。以上是个人看法仅供参考。反正这种中间接口(个人取的名字不知道对不对,反正就是那个意思,**你懂的**[好像这个词已不流行了])的技术,好像到处都在用,不用可惜了,自己写很多代码。 **8.3 添加FS去工程(#)**废话不多说,工程中新建一个文件夹,**叫Fatfs用来专门放ff.c和elmfat.c这两个文件**,其他的文件后面再说,别着急嘛,我底层驱动都还没有加呢,急什么呢?想一下,如果就这样进行编译的话,会有什么结果?有人说要出错了。因为没有添加底层驱动呢?实际上呢,并非如此,编译没有问题,为什么呢?有人说没有开启FATFS的支持,是不是呢?试一下,打开**FATFS**支持,沿用前章的结论,想也不用想,Keil中的config模板中没有,原因仔细看上一节的文章。手动添加进去,非得搞出个错误出来,咱们才能顺藤摸瓜。 **#define RT_USING_DFS_ELMFAT** 编译,怎么样,我擦!成功了!!!是不是要崩溃了?底层驱动都没有的,怎么就成功了呢? 这个地方我们思考一下,ff.c的底层驱动是那disk一大套函数,我们在elmfat.c中不是给人家提供了吗?所以,不管怎么玩,编译都成功。怎么办? 打开elmfat.c看一下那些disk开头的函数是怎么写的,这里再说明一下,那个disk开头的函数就是ff.c的驱动,所有和硬件相关的操作都在那里。我们打个个读函数看一下,我的天呢,从设备驱动过来的。用以前的思路去找出问题了!!!没有找到错误,进行不下去了!!! **8.4 山穷水尽疑无路**没法子了吗?不是从设备驱动过来吗?那这个设备到底是谁,咱得弄清楚,才能摸瓜摸下去呢。 ```/* Read Sector(s) */ DRESULT disk_read (BYTE drv, BYTE* buff, DWORD sector, UINT count) { rt_size_t result; rt_device_t device = disk[drv];//找到了,它在这里!!! result = rt_device_read(device, sector, buff, count); if (result == count) { return RES_OK; } return RES_ERROR; } ```好家伙,disk[]这个,继续查!文件头部有一个定义:```static rt_device_t disk[_VOLUMES] = {0};```再查,一定要找到它写入值的地方才行。发现```int dfs_elm_mount(struct dfs_filesystem *fs, unsigned long rwflag, const void *data) { .... /* save device */ disk[index] = fs->dev_id;//这里有这么一段,是不是有点意思了? .... } ```原来它在这里保存了一下子呢,也就是说上层应用调用这函数时把它搞进来了,看一下哪里调用它了? ```static const struct dfs_filesystem_ops dfs_elm = { "elm", DFS_FS_FLAG_DEFAULT, &dfs_elm_fops, dfs_elm_mount, dfs_elm_unmount, dfs_elm_mkfs, dfs_elm_statfs, dfs_elm_unlink, dfs_elm_stat, dfs_elm_rename, }; ```好家伙,封装了,这可怎么办?好在它下面就是调用的函数呢。 ```int elm_init(void) { /* register fatfs file system */ dfs_register(&dfs_elm); return 0; } ```再进去看一下,【搞到现在,是不是后悔没有看文档,想要放弃了?为什么呢?过去就变成指针了,不好找了,嘿嘿】在**dfs_fs.c**里面有一个函数int dfs_register(const struct dfs_filesystem_ops *ops)完蛋了,不知道上哪里找去了。 **8.5 柳暗花明** 【搞这么费劲,看文档好了,早干麻去了?】别灰心,之前不是说那个elm.c是中间的接口层吗?那么操作系统那边实现,肯定得在对应的接口才对呀,刚才我们找到了一个mout,那看一下那个dfs.c中有没有呢?```int dfs_mount(const char *device_name, const char *path, const char *filesystemtype, unsigned long rwflag, const void *data) { const struct dfs_filesystem_ops **ops; ……….. /* call mount of this filesystem */ if ((*ops)->mount(fs, rwflag, data) < 0)//这个地方是不是有点意思 { /* close device */ if (dev_id != NULL) rt_device_close(fs->dev_id); /* mount failed */ dfs_lock(); /* clear filesystem table entry */ memset(fs, 0, sizeof(struct dfs_filesystem)); goto err1; } …………… } ```简单地说,那边注册好函数后,拿过来这里用,实际是调用刚才弄好的mount,回去看一下是什么```int dfs_elm_mount(struct dfs_filesystem *fs, unsigned long rwflag, const void *data) { 。。。。。 disk[index] = fs->dev_id; 。。。。。 } ```很像吧,应该就是它了。现在,就是要搞清楚,这个堆参数都搞了什么,那个fs是哪个妖精妹子?快快现出原形来。发现有一行: ```/* register file system */ fs->path = fullpath; fs->ops = *ops; fs->dev_id = dev_id;//放开那个妹子,去找dev_id那个小伙子 ``` 哭喊吧,哀求吧,然后死吧,老子找到它了!在函数头部,下面一点就是了``` /* open specific device */ if (device_name == NULL) { /* which is a non-device filesystem mount */ dev_id = NULL; } else if ((dev_id = rt_device_find(device_name)) == NULL)//妖精打扮得这么好看 { /* no this device */ rt_set_errno(-ENODEV); return -1; } ``` **8.6 晴天霹雳** 看一下那个device_name是哪里来的?又完了,外部传进来的,要累死了呀。【看文档多好】。intdfs_mount**(**const char *****device_name**,** const char *****path**,** const char *****filesystemtype**,** unsigned long rwflag**,** const void *****data**)** 总之,那个设备是外部注册好了的东西,你注册了什么这里就是什么了啦。 **8.7 告一段落**至此,已经知道那个设备是外部注册好后,然后通过名字找到的,所以呢,外部的驱动最主要的任务就是完成这个设备的注册。而且必须满足一下的顺序。 1、外部设备注册。【不注册好,那个name怎么找】2、文件系统注册。【dfs文件系统不注册还怎么玩?】 3、dfs_mount。【真枪实弹地干呢!!!】 **8.8 前面都是废话** 是的,如果你看过文档的话,不必那么麻烦了。我写这个文档本来想,简单明了的。如果只是想用它的话,完全可以不用理解它是怎么回事。按8.9节之后的操作去搞,主要是后面的操作太简单了,不写一点分析查找的话,浪费你们的上网流量了。 **8.9 底层驱动(#)**其实这里真的没有什么好说的,那个lpc176xbsp的文件都写好的了,添加到工程,直接用就可以了。先说一下文件在哪,bsp下的sd.c sd.h spi.c spi.h,直接复制过来,为了避免和别的文件重名,这里将spi.c改成spi_sd.c,包含文件也要进行修改。工程中新建一个文件夹叫SPI_SD添加到sd.c和spi_sd.c两个文件。 然后,直接编译,如果有错的话,注意看一下你的文件名是不是错了。【真的没有什么好说的,那个Spi文件基本上就是参考网上的例程进行编写就可以了】 **8.10 好像宏没开全** 来吧,打开它! ```#define RT_USING_DFS #define DFS_USING_WORKDIR #define RT_USING_DFS_ELMFAT #define RT_DFS_ELM_MAX_SECTOR_SIZE 512 #define RT_DFS_ELM_DRIVES 2 #define RT_DFS_ELM_REENTRANT #define RT_DFS_ELM_USE_LFN 3 #define RT_DFS_ELM_MAX_LFN 64 #define RT_DFS_ELM_CODE_PAGE 437 //936 ```编译,出错了!.\Objects\CRSystem_RTT3.axf:Error: L6218E: Undefined symbol ff_convert (referred from ff.o)..\Objects\CRSystem_RTT3.axf:Error: L6218E: Undefined symbol ff_wtoupper (referred from ff.o). 这两个函数没有定义?怎么办呢?用过fatfs的同学都知道,那个optoin中的文件加进来就可以了。为了方便直接使用,也对那个文件进行修改一下,让它选择性编译,省得提示这错那错的。 437对应英文,使用ccsbcs.c,936对应英文,使用cc936.c,这里要说明的一下的是,如果不要求长文件名支持的话,可以不用option中文件。分别在它们的头上加上编译条件。将两个文件直接加进工程中的FatFs文件夹里面。 **ccsbcs.c #if((_USE_LFN != 0) && (_CODE_PAGE != 936))** **cc936.c #if((_USE_LFN != 0) && (_CODE_PAGE == 936))** 好了,保存编译。至此,所有底层的东西都齐了,就差上层应用了。 **8.11 初始化编写**前面有说过,它的顺序,由于spi这里不是设备驱动的写法搞的,我们对它进行显式的初始化。0、spi硬件初始化,SD卡初始化。1、外部设备注册。【不注册好,那个name怎么找】2、文件系统注册。【dfs文件系统不注册还怎么玩?】 3、dfs_mount。【真枪实弹地干呢!!!】 ```void rt_dfs_init_ex(uint8_t debug) { #if defined(RT_USING_DFS) && defined(RT_USING_DFS_ELMFAT) rt_hw_sdcard_init(); /* initialize the device file system */ dfs_init(); /* initialize the elm chan FatFS file system*/ elm_init(); /* mount sd card fat partition 1 as root directory */ if (dfs_mount("sd0", "/", "elm", 0, 0) == 0) { rt_kprintf("File System initialized!
"); } else { rt_kprintf("File System initialzation failed!
"); } #endif /* RT_USING_DFS && RT_USING_DFS_ELMFAT */ } ```这里要注意一下,这个初始化代码最好加到任务里面去,申请的内存空间有点大,弄不好就坏事了。 **8.12 应用编写** ```static void save_thread_entry(void* parameter) { uint8_t opt = 0; #if defined(RT_USING_DFS) && defined(RT_USING_DFS_ELMFAT) extern void rt_dfs_init(void); rt_dfs_init(); #endif while(1) { if(opt == 1) //read,这样写,可以由仿真器对其进行控制。 { int fd = 0,size; char s[] = "RT-Thread Programmer!
"; opt = 0; //rt_flash_read(rd_addr,rdbuf,rd_sect); fd = open("/append.txt", O_APPEND | O_RDWR | O_CREAT); if (fd >= 0) { size = write(fd, s, sizeof(s)); fsync(fd); close(fd); } } else if(opt == 2) //write { int fd = 0,size; char buffer[80] = {0}; opt = 0; fd = open("/fuck.txt", O_RDONLY); if (fd >= 0) { size=read(fd, buffer, sizeof(buffer)); close(fd); } opt = 0; } rt_thread_delay(100); // } } ```当然,可以直接使用Shell命令也是可以的啦。 **8.13 发现问题**1、SD卡写入失败2、SD卡写入时间有问题3、SD卡热插拔问题 这些问题的发现也是有顺序的,最早发现其数据写入失败,最后定位到驱动代码有BUG。解决完后,发现时间写入有问题,发现是获取时间的代码有问题。最后发现热插拔问题,插拔后,读不了啦。 **8.13.1 SD驱动修正**其根本原因在于那个FIFO的使用,在读取的时候是没有问题,但是写入时,是错误的。```static bool LPC17xx_SD_WirteDataBlock(const uint8_t *buff, uint8_t token) { uint8_t resp, i; i = i; // avoid warning LPC17xx_SPI_SendByte(token); /* send data token first*/ if (token != TOKEN_STOP_TRAN) { /*---------------------------------这个地方要注掉,测试时使用FIFO写入失败,不知道为什么---------------------------------*/ //#ifdef USE_FIFO // LPC17xx_SPI_SendBlock_FIFO(buff); //#else /* Send data. */ //for (i = 512 / 4; i ; i--) for (i = 128; i ; i--) { LPC17xx_SPI_SendByte(*buff++); LPC17xx_SPI_SendByte(*buff++); LPC17xx_SPI_SendByte(*buff++); LPC17xx_SPI_SendByte(*buff++); } //#endif /* USE_FIFO */ LPC17xx_SPI_SendByte(0xFF); /* 16-bit CRC (Dummy) */ LPC17xx_SPI_SendByte(0xFF); resp = LPC17xx_SPI_RecvByte(); /* Receive data response */ if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ return false; if (LPC17xx_SD_WaitForReady() == false) /* Wait while Flash Card is busy. */ return false; } return true; } ``` **8.13.2 SD写入时间**这里还有一个小问题,你把那个usemicrolib勾上试一下,是不是编译都出错了?.\Objects\CRSystem_RTT3.axf:Error: L6218E: Undefined symbol time (referred from dfs_elm.o).这个函数的时间函数没有搞呢,其实可以使用RTC组件的,但是由于那个RTC实在是太简单了,何必呢?直接从周立功提供的代码里复制几个函数就搞定了。 RTC.c中添加```#include "time.h" void rt_rtc_gettime(struct tm * ctime) { ctime->tm_sec = LPC_RTC->SEC; ctime->tm_min = LPC_RTC->MIN; ctime->tm_hour = LPC_RTC->HOUR; ctime->tm_wday = LPC_RTC->DOW; ctime->tm_mday = LPC_RTC->DOM; ctime->tm_mon = LPC_RTC->MONTH; ctime->tm_yday = LPC_RTC->DOY; ctime->tm_year = LPC_RTC->YEAR; } ```dfs_elm.c中替换函数```DWORD get_fattime(void) { extern void rt_rtc_gettime(struct tm * ctime); struct tm tm_now; DWORD fat_time; rt_rtc_gettime(&tm_now); fat_time = (DWORD)(tm_now.tm_year - 1980) << 25 | (DWORD)(tm_now.tm_mon) << 21 | (DWORD)tm_now.tm_mday << 16 | (DWORD)tm_now.tm_hour << 11 | (DWORD)tm_now.tm_min << 5 | (DWORD)tm_now.tm_sec >> 1; return fat_time; } ```**8.13.3 SD热插拔**谁也避免不了这个插拔问题,怎么解决呢?很简单。就是检测SD卡有没有被拔出,如果被拔出了,那就卸载它,如果插入了就初始化装载它,搞个状态机就成了。随便定义一个变量或是结构体(美女包装一下更好看),程序中要不断得检测CD脚状态,主要是由于LPC这个脚上没有中断的,接到了P4.29上,这事闹的,不然用中断来做很好的。 在SD.c中添加以下内容:```typedef struct { uint8_t sd_card_insert; uint8_t sd_card_init; uint8_t sd_elmfs_init; uint8_t sd_card_mount; }rt_sd_cardmgr_t; rt_sd_cardmgr_t g_sd_mgr; uint32_t rt_lpc_sd_check_insert(void) { /* Read CardDetect and WriteProtect SD card socket pins. */ if (!(LPC_GPIO4->FIOPIN & (1 << 29))) { g_sd_mgr.sd_card_insert = 1; } else { g_sd_mgr.sd_card_insert = 0; } return g_sd_mgr.sd_card_insert; } void sd_card_auto_check(void) { extern void rt_dfs_init_ex(uint8_t debug); if(g_sd_mgr.sd_card_mount == 0) { if(rt_lpc_sd_check_insert() == 1) { rt_dfs_init_ex(1); } } else { if(rt_lpc_sd_check_insert() == 0) { uint8_t i = 0,stat= 0; for(i=0;i<4;i++) { rt_sdcard_delay(5); stat |= (rt_lpc_sd_check_insert() << i); } if(stat == 0) { dfs_unmount("/"); rt_device_unregister(&sdcard_device); g_sd_mgr.sd_card_mount = 0; } } } } ```然后修改初始化函数,在初始化函数中对变量进行修改。```void rt_dfs_init_ex(uint8_t debug) { #if defined(RT_USING_DFS) && defined(RT_USING_DFS_ELMFAT) rt_hw_sdcard_init(); /* initialize the device file system */ dfs_init(); /* initialize the elm chan FatFS file system*/ elm_init(); /* mount sd card fat partition 1 as root directory */ if (dfs_mount("sd0", "/", "elm", 0, 0) == 0) { g_sd_mgr.sd_card_mount = 1;//add here if(debug) rt_kprintf("File System initialized!
"); } else { g_sd_mgr.sd_card_mount = 0; //add here if(debug) rt_kprintf("File System initialzation failed!
"); } #endif /* RT_USING_DFS && RT_USING_DFS_ELMFAT */ } ```然后,一定要在主循环中调用sd_card_auto_check.```while(1) { sd_card_auto_check();//这里一定要调用,因为没有中断,只能是轮了 if(opt == 1) //read,这样写,可以由仿真器对其进行控制。 { ```**8.14 小结** 本章主要介绍了如何在dfs架构下,添加FatFs,然后对文件系统进行了一定的讲解,针对SD卡应用过程中发现的问题,也给出了修正方法和示例。总体来说,RTT下的设备使用还是比较简单的,但是一定要注意,bsp下的代码也是人写的,不是机器写的,存在不足和缺陷是肯定的。希望使用的朋友们,一定要仔细调试,否则加到自己的代码中,人都会崩溃。不劳而获的人是可耻的,一定要动手去试。也可能是由于这个原因,造成了人们基本上都是使用自己现成的代码来完成底层驱动,并没有完全使用RTT的驱动框架。 ================本章完================= 文档下载:![RT_Nano_V3初级教程_8 添加FATFS.pdf](/uploads/201803/27/190219ig7ymg7n711dnnc7.attach)
查看更多
4
个回答
默认排序
按发布时间排序
nongxiaoming
2018-03-27
rt-thread大师兄
不错的教程
armink
2018-03-27
这家伙很懒,什么也没写!
RTT 3.0.2 支持软件模拟 RTC 也挺方便的。把时间系统和 RTC 关联上才是比较优雅的解决方法。 另外,可以尝试 env 工具,通过 menuconfig 图形界面来完成文件系统的相关配置。
wlof
2018-03-27
这个家伙不懒,什么也没写
>不错的教程 --- 谢谢,我把使用心得和分析记录下来,省得以后忘记掉。
wlof
2018-03-27
这个家伙不懒,什么也没写
>RTT 3.0.2 支持软件模拟 RTC 也挺方便的。把时间系统和 RTC 关联上才是比较优雅的解决方法。 > >另外,可以尝 ... --- 恩,好的谢谢。回头我试试下rtt自带的rtc,记得它里面实现了那个time函数。 env是一种挺好的工具,工程基本不用自己动手建。
撰写答案
登录
注册新账号
关注者
0
被浏览
3.8k
关于作者
wlof
这个家伙不懒,什么也没写
提问
24
回答
64
被采纳
0
关注TA
发私信
相关问题
1
字库除了SD卡和http还有别的方式导入吗
2
如何同时使用RTT文件系统于NAND Flash 和 SD卡,针对Realboard4088?
3
EasyFlash好棒
4
SD卡挂在的SPI总线又要加个DA设备
5
sd卡与flash 同时挂载 挂载路径shell怎么区分 访问
6
请教ramfs使用方法
7
rtt stable2.0.x版本 sd卡及文件系统
8
nandflash文件系统UFFS请教
9
关于SD卡挂载文件系统的问题(已解决)
10
Yaffs采用GPL2许可
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
CherryUSB的bootuf2配置
2
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
3
GD32F450 片内 flash驱动适配
4
STM32H7R7运行CherryUSB
5
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
7
个答案
2
次被采纳
a1012112796
12
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
lizimu
2
篇文章
7
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部