搞单片机辣么久还没接触过文件系统,手上的硬件资源也比较少,星期五的时候想到可以文件系统移植在Windows的,于是就开干了。
先在网上搜了一下文章,咦,有一个前辈移植过,那我就参照一下吧,O(∩_∩)O~ ps:文章地址http://blog.csdn.net/fzu_dianzi/article/details/6586255
在pc上移植FatFs,我的想法是,创建一个文件,使用该文件当做一个存储设备提供给FatFs,就可以实现一些文件系统的操作,例如:创建文件,修改文件等。
移植第一步:下载源码。先去http://elm-chan.org/fsw/ff/00index_e.html下载FatFs的源代码,我下载的是FatFs R0.12b。下载下来里面有一个src,里面放的就是源码啦,在src目录里面还有一个option文件夹,我看了一下,里面放的应该是字库文件。下面正式开始移植。
移植第二步:修改配置文件。将源码加入到工程之后,我们就要开始修改FatFs的配置文件,让它符合我们的实际情况与需求。打开ffconf.h,找到一下几个宏,并修改。
#define _USE_MKFS 1 /* 使能格式化 */
这个是使能格式化函数的宏,这个宏是要启用的,在第一次使用的时候是需要对存储设备进行格式化的,否则文件系统无法识别,就不能对设备进行操作了。
#define _CODE_PAGE <span style="white-space:pre"> </span>936 /* 选择中文 */
选择中文,如果你是外国朋友或者你需要中文外的其他文字的支持就根据源码的注释去选择并修改。
#define _FS_NORTC <span style="white-space:pre"> </span>1 /* 没有RTC */
这个宏是时钟的使能,如果你想使用系统的时钟就设为0,此时你需要去完成get_fattime()函数,返回系统时钟,类型为DWORD,共32位,其中bit31:25 年:用1980作为基准 (0..127),2016 -> 2016 - 1980 = 36;bit24:21 月:0-12;bit20:16 日:1-31;bit15:11 时: (0..23);bit10:5 分:0-59;bit4:0 秒:Second / 2 (0..29) 。
如果不使用时钟的话就是用默认的时间。
配置文件的修改到这里就可以满足我们的需求了,其他更多的宏有需要的就得去修改。
移植第三步:完成FatFs文件系统与底层相关的函数。全部都在diskio.c文件里头。
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
return RES_OK;
}
获得状态的函数我们直接让它返回OK就好了,我们现在是用文件模拟一个存储设备,这个不重要。
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
return RES_OK;
}
初始化函数,在这里我们不需要初始化,直接返回OK即可。
#define FILENAME "./test"
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, <span style="white-space:pre"> </span>/* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
FILE *Handle = NULL;
DWORD ulSeek = 0;
unsigned long nRead = 0;
int nSeekRes = 0;
Handle = fopen(FILENAME, "r+");
if (!Handle) {
printf(__FILE__":%d\n", __LINE__);
printf(FILENAME" open err!\n");
return RES_NOTRDY;
}
ulSeek = sector * _MAX_SS;
nSeekRes = fseek(Handle, ulSeek, SEEK_SET);
nRead = fread(buff, _MAX_SS, count, Handle);
if (nSeekRes || nRead == 0) {
printf(__FILE__":%d\n", __LINE__);
printf("read disk err!\n");
return RES_ERROR;
}
fclose(Handle);
return RES_OK;
}
完成读取扇区函数,在这里有两个地方要注意的,一个是fopen的模式,这个模式要用“r+”,且我们要一开始就创建好一个文件,让它去打开,因为r+是要求文件必须存在的;第二个地方是ulSeek = sector * _MAX_SS;要算出在文件中的偏移,所以要乘上_MAX_SS扇区大小才是实际的偏移。
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
写函数和读函数是一样的,就不浪费大家时间了。是我太懒O(∩_∩)O~
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
switch (cmd) {
case CTRL_SYNC:
break;
case GET_SECTOR_COUNT:
*(DWORD *)buff = 512;
break;
case GET_SECTOR_SIZE:
*(DWORD *)buff = 512;
break;
case GET_BLOCK_SIZE:
*(DWORD *)buff = 200;
break;
case CTRL_TRIM:
break;
}
return RES_OK;
}
关于这个ioctl呢,是一定要写的,一开始我只是让他返回ok,但是发现,在调用格式化函数的时候一直不行,后来跟踪进去发现在格式化的过程中,会通过ioctl获取GET_SECTOR_COUNT属性,用这个来计算填充量(还没有认真去分析,初步判断是这样)。如果没有这个属性将没有办法完成格式化。
移植第四步:编写主函数。
#include "src\diskio.h"
#include "src\ff.h"
#include "src\ffconf.h"
#include "src\integer.h"
#include <stdio.h>
void main(void)
{
FATFS fs;
FIL fil;
FRESULT res;
UINT bw;
BYTE work[_MAX_SS] = { 0 };
char buff[30] = { 0 };
f_mount(&fs, "", 0);
res = f_mkfs("", FM_FAT, 0, work, sizeof(work));
if (res) {
printf("mkfs error\n");
goto end;
}
res = f_open(&fil, "hello.txt", FA_CREATE_NEW | FA_WRITE);
if (res) {
printf("f_open hello.txt err\n");
goto end;
}
f_write(&fil, "Hello, World!\n", 15, &bw);
if (bw != 15) {
printf("f_write hello.txt err\n");
f_close(&fil);
goto end;
}
f_close(&fil);
res = f_open(&fil, "hello.txt", FA_READ | FA_OPEN_EXISTING);
if (res) {
printf("f_open hello.txt err\n");
goto end;
}
res = f_read(&fil, buff, 15, &bw);
if (res) {
printf("f_read err\n");
f_close(&fil);
goto end;
}
printf("buff: %s", buff);
f_close(&fil);
end:
getch(); //为了停留一下下,愚笨的办法
}
运行效果图: