小猫爪:动手笔记02-FatFs移植

1 前言

今天我要移植一个简单的文件系统FatFs,至于文件系统是啥,FatFs是啥我就不多逼逼了,下面直接开干。这里我使用的平台为RT1050,开发环境为KEIL。

2 准备

2.1 下载FatFs源码

下载地址:http://elm-chan.org/fsw/ff/00index_e.html
下载后解压打开:
在这里插入图片描述一览无余,一眼就看到了source文件夹,打开一看:
在这里插入图片描述看到readme就要打开看一看:

FatFs Module Source Files R0.14a


FILES

  00readme.txt   This file.
  00history.txt  Revision history.
  ff.c           FatFs module.
  ffconf.h       Configuration file of FatFs module.
  ff.h           Common include file for FatFs and application module.
  diskio.h       Common include file for FatFs and disk I/O module.
  diskio.c       An example of glue function to attach existing disk I/O module to FatFs.
  ffunicode.c    Optional Unicode utility functions.
  ffsystem.c     An example of optional O/S related functions.


  Low level disk I/O module is not included in this archive because the FatFs
  module is only a generic file system layer and it does not depend on any specific
  storage device. You need to provide a low level disk I/O module written to
  control the storage device that attached to the target system.

可以看到里面对每一个文件都作了非常详细的说明。这里简单的给每个文件解释一下:
integer.h:文件中包含了一些数值类型定义。
diskio.c:包含底层存储介质的操作函数,这些函数需要用户自己实现,主要添加
底层驱动函数。
ff.c: FatFs 核心文件,文件管理的实现方法。该文件独立于底层介质操作文件的
函数,利用这些函数实现文件的读写。
ffunicode.c:包含了多语言支持的编码格式,例如简体中文936 编码、英文437 编
码等。
ffconf.h:这个头文件包含了对FatFs 功能配置的宏定义,通过修改这些宏定义就可
以裁剪FatFs 的功能。

需要重点修改的文件就是diskio.c和ffconf.h。diskio.c文件就是FatFs应用层程序
与MCU的接口,ffconf.h文件可以配置其功能。

2.2 准备待移植工程

准备一个可以对SD卡正常读写的工程,我已经偷偷地准备好了,下面砸门正式开始吧。

3 移植

3.1 添加文件

复制source文件夹至工程下并改名fatfs,在工程中添加新分组FatFs,并且添加fatfs文件夹下的所有文件至新的分组,如下:
在这里插入图片描述
随后将fatfs路径包含进编译路径中,如下:

在这里插入图片描述

3.2 编译

文件已经添加完毕,废话不多说,直接编译。

  1. 出错:有很多错误和警告。
    在这里插入图片描述原因:因为这些函数就是FatFs接口函数,需要使用者去定义,所以这些函数没有定义。
    修改:可以直接删掉注释掉,或者写一个空函数放在那。

  2. 出错:flexspi_nor_debug\sdcard_polling.out: Error: L6218E: Undefined symbol get_fattime (referred from ff.o).
    原因:get_fattime函数没有定义,这是FatFs时间戳功能,通常需要MCU里的RTC时钟来支持,这里我不使用时间戳。
    修改:找到ffconf.h中的FF_FS_NORTC,将其改为1,如下:

#define FF_FS_NORTC		1

废话不多说,再次编译。

没有错误,成功移植,那是不可能的。

3.3 完善接口

关键来了,需要完善接口,那么需要完善的接口有哪些呢。打开diskio.h文件,找到以下:

DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

可以看到FatFs的接口函数一共有五个,下面则需要将这五个函数与我们自己的MCU的SD卡驱动对接,完成接口的定义。

(1)添加设备号
首先找到diskio.c中设备号定义:

/* Definitions of physical drive number for each drive */
#define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */

可以看到并没有SD卡,所以需要在后面加一个SD卡的设备号,添加完如下:

/* Definitions of physical drive number for each drive */
#define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */
#define DEV_SD		3	/* Example: Map USB MSD to physical drive 2 */

同时还需要将ffconf.h文件中的FF_VOLUMES参数改成4。

#define FF_VOLUMES		4

因为总共有4个设备号。

(2)添加接口函数
在接口函数中,照葫芦画瓢,添加属于SD卡的接口函数,我这里就以disk_initialize举一个例子,修改完如下:

DSTATUS SD_disk_initialize(void)
{
    
    

}
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
    
    
	DSTATUS stat;
	int result;

	switch (pdrv) {
    
    
	case DEV_RAM :
		//result = RAM_disk_initialize();

		// translate the reslut code here

		return stat;

	case DEV_MMC :
		//result = MMC_disk_initialize();

		// translate the reslut code here

		return stat;

	case DEV_USB :
		//result = USB_disk_initialize();

		// translate the reslut code here

		return stat;
	case DEV_SD :
		stat= SD_disk_initialize();

		return stat;	
	}
	return STA_NOINIT;
}

同理将diskio.c下所有的函数都添加属于SD卡的接口函数。

(3)完善添加的接口函数
上面一共添加了五个函数,分别是SD_disk_status,SD_disk_initialize,SD_disk_read,SD_disk_write,disk_ioctl下面就需要将这个五个函数与MCU的SD卡驱动函数联系在一起。

  1. SD_disk_status:返回当前SD卡的状态,可有可无的东西,直接不写,直接返回RES_OK。
DSTATUS sd_disk_status(BYTE pdrv)
{
    
    
    return RES_OK;
}
  1. SD_disk_initialize:初始化SD卡,就是完成一些SD卡的初始化
DSTATUS SD_disk_initialize(void)
{
    
    
    /* power off card */
    SD_SetCardPower(&g_sd, false);
    /* wait card insert */
    SD_PollingCardInsert(&g_sd, kSD_Inserted);
    /* power on the card */
    SD_SetCardPower(&g_sd, true);
    /* Init card. */
    if (SD_CardInit(&g_sd))
    {
    
    
        return STA_NODISK;
    }
    return RES_OK;
}
  1. SD_disk_read:读取SD卡数据,就是SD卡读数据的函数
DRESULT SD_disk_read(BYTE* buff, LBA_t sector, UINT count)
{
    
    
    if (kStatus_Success != SD_ReadBlocks(&g_sd, buff, sector, count))
    {
    
    
        return RES_ERROR;
    }

    return RES_OK;
}
  1. SD_disk_write:写SD卡数据,就是往SD卡里写数据的函数
DRESULT SD_disk_write( const BYTE* buff, LBA_t sector, UINT count)
{
    
    
    if (kStatus_Success != SD_WriteBlocks(&g_sd, buff, sector, count))
    {
    
    
        return RES_ERROR;
    }

    return RES_OK;
}
  1. SD_disk_ioctl:用来扩展对存储器的操作,比如获得存储器大小等,所有的操作在diskio.h中都有定义。取出FatFs通用的命令如下:
/* Generic command (Used by FatFs) */
#define CTRL_SYNC			0	/* Complete pending write process (needed at FF_FS_READONLY == 0) */
#define GET_SECTOR_COUNT	1	/* Get media size (needed at FF_USE_MKFS == 1) */
#define GET_SECTOR_SIZE		2	/* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
#define GET_BLOCK_SIZE		3	/* Get erase block size (needed at FF_USE_MKFS == 1) */
#define CTRL_TRIM			4	/* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
名称 描述 条件
CTRL_SYNC 写操作同步命令 FF_FS_READONLY == 0
GET_SECTOR_COUNT 获取扇区数量 FF_USE_MKFS == 1
GET_SECTOR_SIZE 获取扇区大小 FF_MAX_SS != FF_MIN_SS
GET_BLOCK_SIZE 获取块大小 FF_USE_MKFS == 1
CTRL_TRIM 发布TRIM命令 FF_USE_TRIM == 1

可以看到这些命令是否需要由一些宏定义来进行定义的,那这些宏定义代表着什么呢?这些宏定义在ffconf.h里面都可以找到,并且每一个宏定义的具体意义都标注的很清楚,找到这些宏定义后发现在它的原始配置中只有|FF_FS_READONLY == 0满足条件,所以我们只需要完成CTRL_SYNC这个命令(当然也可以直接将有效宏定义直接使能,但是这篇文件侧重的是怎样移植FatFs,所以就完全按照它的默认配置来吧,如果想了解更多,可以互相讨论)。所以我的SD_disk_ioctl代码如下:

DRESULT SD_disk_ioctl(BYTE cmd, void* buff)
{
    
    
    DRESULT result = RES_OK;

    switch (cmd)
    {
    
    
        case CTRL_SYNC:
            result = RES_OK;
            break;
        default:
            result = RES_PARERR;
            break;
    }

    return result;
}

为了方便操作,我使能了FatFs相对路径的功能,也就是ffconf.h文件下的FF_FS_RPATH,修改成如下:

#define FF_FS_RPATH		2
/* This option configures support for relative path.
/
/   0: Disable relative path and remove related functions.
/   1: Enable relative path. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
*/

到这里就完成了所有的移植工作了,接下来运行它。

4 运行

修改main函数,测试刚刚移植的FatFs,代码如下(当然了,我也是Ctrl+CV老手了,直接Copy不香吗?):

int main()
{
    
    
    FRESULT error;
    DIR directory; /* Directory object */
    FILINFO fileInformation;
    UINT bytesWritten;
    UINT bytesRead;
    const TCHAR driverNumberBuffer[3U] = {
    
    '3', ':', '/'};
    volatile bool failedFlag           = false;
    char ch                            = '0';
    BYTE work[FF_MAX_SS];

    BOARD_ConfigMPU();
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    PRINTF("\r\nFATFS example to demonstrate how to use FATFS with SD card.\r\n");

    PRINTF("\r\nPlease insert a card into board.\r\n");

    if (sdcardWaitCardInsert() != kStatus_Success)
    {
    
    
        return -1;
    }

    if (f_mount(&g_fileSystem, driverNumberBuffer, 0U))
    {
    
    
        PRINTF("Mount volume failed.\r\n");
        return -1;
    }

    error = f_chdrive((char const *)&driverNumberBuffer[0U]);
    if (error)
    {
    
    
        PRINTF("Change drive failed.\r\n");
        return -1;
    }

    PRINTF("\r\nCreate directory......\r\n");
    error = f_mkdir(_T("/dir_1"));
    if (error)
    {
    
    
        if (error == FR_EXIST)
        {
    
    
            PRINTF("Directory exists.\r\n");
        }
        else
        {
    
    
            PRINTF("Make directory failed.\r\n");
            return -1;
        }
    }

    PRINTF("\r\nCreate a file in that directory......\r\n");
    error = f_open(&g_fileObject, _T("/dir_1/f_1.dat"), (FA_WRITE | FA_READ | FA_CREATE_ALWAYS));
    if (error)
    {
    
    
        if (error == FR_EXIST)
        {
    
    
            PRINTF("File exists.\r\n");
        }
        else
        {
    
    
            PRINTF("Open file failed.\r\n");
            return -1;
        }
    }

    PRINTF("\r\nCreate a directory in that directory......\r\n");
    error = f_mkdir(_T("/dir_1/dir_2"));
    if (error)
    {
    
    
        if (error == FR_EXIST)
        {
    
    
            PRINTF("Directory exists.\r\n");
        }
        else
        {
    
    
            PRINTF("Directory creation failed.\r\n");
            return -1;
        }
    }

    PRINTF("\r\nList the file in that directory......\r\n");
    if (f_opendir(&directory, "/dir_1"))
    {
    
    
        PRINTF("Open directory failed.\r\n");
        return -1;
    }

    for (;;)
    {
    
    
        error = f_readdir(&directory, &fileInformation);

        /* To the end. */
        if ((error != FR_OK) || (fileInformation.fname[0U] == 0U))
        {
    
    
            break;
        }
        if (fileInformation.fname[0] == '.')
        {
    
    
            continue;
        }
        if (fileInformation.fattrib & AM_DIR)
        {
    
    
            PRINTF("Directory file : %s.\r\n", fileInformation.fname);
        }
        else
        {
    
    
            PRINTF("General file : %s.\r\n", fileInformation.fname);
        }
    }

    memset(g_bufferWrite, 'a', sizeof(g_bufferWrite));
    g_bufferWrite[BUFFER_SIZE - 2U] = '\r';
    g_bufferWrite[BUFFER_SIZE - 1U] = '\n';

    PRINTF("\r\nWrite/read file until encounters error......\r\n");
    while (true)
    {
    
    
        if (failedFlag || (ch == 'q'))
        {
    
    
            break;
        }

        PRINTF("\r\nWrite to above created file.\r\n");
        error = f_write(&g_fileObject, g_bufferWrite, sizeof(g_bufferWrite), &bytesWritten);
        if ((error) || (bytesWritten != sizeof(g_bufferWrite)))
        {
    
    
            PRINTF("Write file failed. \r\n");
            failedFlag = true;
            continue;
        }

        /* Move the file pointer */
        if (f_lseek(&g_fileObject, 0U))
        {
    
    
            PRINTF("Set file pointer position failed. \r\n");
            failedFlag = true;
            continue;
        }

        PRINTF("Read from above created file.\r\n");
        memset(g_bufferRead, 0U, sizeof(g_bufferRead));
        error = f_read(&g_fileObject, g_bufferRead, sizeof(g_bufferRead), &bytesRead);
        if ((error) || (bytesRead != sizeof(g_bufferRead)))
        {
    
    
            PRINTF("Read file failed. \r\n");
            failedFlag = true;
            continue;
        }

        PRINTF("Compare the read/write content......\r\n");
        if (memcmp(g_bufferWrite, g_bufferRead, sizeof(g_bufferWrite)))
        {
    
    
            PRINTF("Compare read/write content isn't consistent.\r\n");
            failedFlag = true;
            continue;
        }
        PRINTF("The read/write content is consistent.\r\n");

        PRINTF("\r\nInput 'q' to quit read/write.\r\nInput other char to read/write file again.\r\n");
        ch = GETCHAR();
        PUTCHAR(ch);
    }
    PRINTF("\r\nThe example will not read/write file again.\r\n");

    if (f_close(&g_fileObject))
    {
    
    
        PRINTF("\r\nClose file failed.\r\n");
        return -1;
    }

    while (true)
    {
    
    
    }
}

END

猜你喜欢

转载自blog.csdn.net/Oushuwen/article/details/112469919