【文件系统】FatFs文件系统在嵌入式芯片LPC18XX上的移植

目录

一 准备工作

二 FatFs源码移植

三 FatFs源码适配

四 文件系统测试


一 准备工作

    (1)FatFs驱动源码(CSDN下载:https://download.csdn.net/download/sinat_33408502/16091729)(官网下载:http://elm-chan.org/fsw/ff/00index_e.html);

    (2)芯片及其周边最小电路、烧写器等等(我这边用的芯片是LPC1857);

    (3)SPI FLASH芯片(我这边用的是S25FL256S);

 

二 FatFs源码移植

    文件系统对于嵌入式系统的重要性是不言而喻的,有了文件系统管理数据和外设变得方便许多,同时简化了应用的开发。今天我们来以在SPI_FLASH上建立文件系统为例,看看FATFS文件系统怎么移植和使用。

    FatFs是一个通用的嵌入式文件系统,对不同的平台支持很好,大到硬盘、U盘、存储卡,小到spi_flash芯片甚至单片机内部FLASH都可以使用FATFS。今天我们就在一个4M大小的SPI_FLASH( S25FL256S )上建立一个文件系统,主控制器是LPC1857。在做文件系统移植前,你需要把操作SPI_FLASH的驱动调通,能读写SPI_FLASH就可以了。

    下面是源码移植的步骤:

    (1)官网或者到我提供的资源路径下载FatFs的驱动源码;

    (2)文件解释如下图所示:

【文件系统】FatFs文件系统在嵌入式芯片LPC18XX上的移植_第1张图片

    其中,

diskio.c个diskio.h是和存储器读写控制相关的驱动接口,比如SPI_FLASH的读写函数接口,都要映射到这里面。必须的文件 

ff.h和ff.h是FATFS的核心文件,必须的文件

ffconf.h是FATFS的配置文件,用来裁剪FATFS,必须的文件

integer.h是FATFS所用到的数据类型定义,用以兼容不同字长CPU,必须的文件

ffsystem.c是一些关于在带操作系统平台中,使用的示例,可选文件

ffunicode.c是万国码编码文件,文件里主要是大数组定义,假如你需要让文件名支持中文就需要这个文件,这个文件会使代码空间急剧变大,可选文件

 

(3)将需要用到的文件添加到工程中,并链接头文件路径:

 

三 FatFs源码适配

为了使得移植过来的文件系统在自己系统中可用,还需要做一下适配工作:

(1)第一步修改ffconf.h文件(文件中各种宏的解释:https://blog.csdn.net/xiayufeng520/article/details/8830157)

在ffconf.h文件中可以配置文件系统的参数和功能,因为一开始我们的flash是没有文件系统的,需要自己格式化一下,所以需要有创建文件系统的功能,将FF_USE_MKFS宏置1,允许格式化:

#define FF_USE_MKFS		1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */

(2)在diskio.c中定义了几个磁盘设备,在对应的几个操作函数里面也有这几个设备对应的操作,但是我们只用一个spi-flash所以就只定义一个:

/* 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 SPI_FLASH		0	/* 外部FLASH */

(3)定义文件系统空间相关属性(扇区数、扇区大小、每个块包含多少扇区):

//SPI FLASH前8M用来存储图片,后面的24M用来作为文件系统
#define FLASH_SECTOR_COUNT  (6*1024)  //扇区个数6K,一共24M
#define FLASH_SECTOR_SIZE 	(4*1024)  //每个扇区4K
#define FLASH_BLOCK_SIZE	16        //每个BLOCK有16个扇区

(4)第四步就是最重要的修改diskio.c中的几个标准接口,这边先做一个列举,下面一一详述:

序号 接口名 说明
【1】 disk_status 获得磁盘状态
【2】 disk_initialize 磁盘初始化
【3】 disk_read 写扇区
【4】 disk_write 读扇区
【5】 disk_ioctl 其他函数
【6】 get_fattime 获得时间

 

【1】disk_status:直接返回成功即可

/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
    if(pdrv == SPI_FLASH) 
	{
        return RES_OK;
	}
}

【2】disk_initialize:spi-flash初始化

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	if(pdrv == SPI_FLASH) 
	{
		flashSSPInit();  //初始化 spi flash
		return RES_OK;
	}
	else
	{
		return RES_PARERR;
	}
}

【3】disk_read:以扇区为单位读,这边我对Flash的空间偏移了2048个扇区,即8M空间作为文件系统,前8M空间留作它用。

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
DRESULT FS_SpiFlash_Read(BYTE *buff, LBA_t sector, UINT count)
{
    int i,j;
    uint32_t addr = sector * FLASH_SECTOR_SIZE;
    
    for(i = 0;i < count;i ++)  //count个扇区
    {
        for(j = 0;j < 16;j ++)  //每次读256字节,分16次读完一个扇区4k
        {
            flash_read_data_uint8(addr, buff, 256);
            addr += 256;
            buff += 256;
        }
        sector ++;
    }
    
    /*for(i = 0;i < count;i ++)  //count个扇区
    {
        //一次读完一个4K扇区
        flash_read_data_uint8(sector*FLASH_SECTOR_SIZE, buff, FLASH_SECTOR_SIZE);
        sector ++;
        buff += FLASH_SECTOR_SIZE;
    }*/
}

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA */
	UINT count		/* Number of sectors to read */
)
{
	DRESULT res;

	if(pdrv == SPI_FLASH) 
	{
        sector += 2048;  //偏移8M
        FS_SpiFlash_Read((uint8_t *)buff, sector, count);//spi flash的读接口,注意函数参数类型一致性
        res = 0;
		return res;
	}
	else
	{
		return RES_PARERR;
	}
}

【4】disk_write:以扇区为单位写,这边我对Flash的空间偏移了2048个扇区,即8M空间作为文件系统,前8M空间留作它用。

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0

DRESULT FS_SpiFlash_Write(BYTE *buff, LBA_t sector, UINT count)
{
    int i,j;
    uint32_t addr = sector * FLASH_SECTOR_SIZE;
    
    for(i = 0;i < count; i ++)
    {
        flash_sector_erase(sector*FLASH_SECTOR_SIZE);  //写之前先擦除,否则挂载失败返回13
        for(j = 0;j < 16;j ++)  //每次写256字节,分16次读完一个扇区4k
        {
            flash_write_sector(addr, buff, 256);
            addr += 256;
            buff += 256;
        }
        sector ++;
    }
    
    /*for(i = 0;i < count; i ++)
    {
        //一次写完一个4K扇区
        flash_write_sector(sector*FLASH_SECTOR_SIZE, buff, FLASH_SECTOR_SIZE);  
        sector ++;
        buff += FLASH_SECTOR_SIZE;
    }*/
    
}

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
	DRESULT res;

	if(pdrv == SPI_FLASH) 
	{
        sector += 2048;  //偏移8M
		FS_SpiFlash_Write((uint8_t *)buff, sector, count);//spi flash的写接口,注意函数参数类型一致性
		res = 0;
		return res;
	}
	else
	{
		return RES_PARERR;
	}
}

【5】disk_ioctl:主要是获取一些磁盘的参数

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	if (pdrv == SPI_FLASH)
	{
		switch (cmd) 
		{
		 case CTRL_SYNC:
			 return RES_OK;
		 case GET_SECTOR_COUNT:  //扇区数量 
			 *(DWORD * )buff = FLASH_SECTOR_COUNT;
			 return RES_OK;
		 case GET_SECTOR_SIZE :  //扇区大小
			 *(WORD * )buff = FLASH_SECTOR_SIZE;//spi flash的扇区大小是 4K
			 return RES_OK;
		 case GET_BLOCK_SIZE :  //块大小
		 	 *(DWORD * )buff = FLASH_BLOCK_SIZE;
			 return RES_OK;
		 default:
		 	 return RES_PARERR;
		 }
	}
	else
	{
		return RES_PARERR;
	}
}

【6】get_fattime:文件系统时间的接口,直接返回0即可;

DWORD get_fattime(void)
{
    return 0;
}

至此,基本上FatFs文件系统在我自用的LPC平台上移植完毕。使用的是S25FL256S的后24M空间作为文件系统(总共32M,前8M有其他的用处),分为6K个扇区,每个扇区4K大小。

 

四 文件系统测试

在上面移植完成的基础之上,下面测试一下该文件系统的可用性:

FATFS fs;
FRESULT res;
BYTE work[FF_MAX_SS];
FIL filp;
BYTE buff[100];
UINT fnum;
BYTE temp;

DWORD fre_clust;
FATFS *pfs;
DWORD TOT_SIZE;
DWORD FRE_SIZE;



int main (void)
{
    int i = 0;
    BaseDataInit();
    SystemInit();                   //系统存储为小端模式
    
    /********************************SEGGER_RTT模块+单元测试模块********************************/
    #ifdef IS_UNITY_TEST
    SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
    printf("\r\n\r\n\r\n\r\n\r\n%s\t%s\r\n", __DATE__, __TIME__);  //打印程序编译的日期时间
    fflush(stdout);

    printf("TEST Start:\r\n");
    res=f_mount(&fs, "0:", 1);
	printf("\r\n[1]mount ret=%d", res);
	if (res == FR_NO_FILESYSTEM)
	{
		printf("\r\n[2]no file system, format");
 
		res = f_mkfs("0:", 0, work, sizeof(work));
        if (res == FR_OK)
		{
			printf("\r\n[2.1]format ok");
			res = f_mount(NULL, "0:", 1);
			res = f_mount(&fs, "0:", 1);
			printf(",mount again ret=%d", res);
		}
		else
		{
			printf("\r\n[2.2]format fail");
		}
	}
	else if (res != FR_OK)
	{
		printf("\r\n[1.1]file system mount fail");
	}
	else
	{
		printf("\r\n[1.2]file system mount ok");
	}
    
    if (res == FR_OK)
	{
		res = f_open(&filp, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
		if (res == FR_OK)
		{
			printf("\r\n[3.1]f_open ok, now write");
			memset(buff, 0x55, sizeof(buff));
			res = f_write(&filp, buff, sizeof(buff), &fnum);
			if (res == FR_OK)
			{
				printf("\r\n[4.1]write ok, real write len=%d, read...", fnum);
 
				
				temp = f_eof(&filp);
				printf("\r\nreach file end=%d", temp);
				printf("\r\nfile pointer back to 0");
				f_lseek(&filp, 0);
 
				memset(buff, 0, sizeof(buff));
				res = f_read(&filp, buff, sizeof(buff), &fnum);
				if (res == FR_OK)
				{
					printf("\r\n[5.1]read ok, real read len=%d, data=%d %d", fnum, buff[0], buff[99]);
				}
				else
				{
					printf("\r\n[5.2]read fail, res=%d", res);
				}
                
                printf("\r\n[5.3]f_size = %d", (int)f_size(&filp));
			}
			else
			{
				printf("\r\n[4.2]write fail");
			}
 
            //new
            pfs = &fs;
            res = f_getfree("0:", &fre_clust, &pfs);
            if(res == FR_OK)
            {
                TOT_SIZE = (pfs->n_fatent - 2) * pfs->csize; //总容量 单位byte;
                FRE_SIZE = fre_clust * pfs->csize; // 可用容量 单位byte;
                printf("\r\n[6.1]f_getfree ok, total=%d, free=%d", TOT_SIZE, FRE_SIZE);
            }
            else
            {
                printf("\r\n[6.2]f_getfree fail, res=%d", res);
            }
            //new end
            
			printf("\r\n[7]close file");
			f_close(&filp);
		}
		else
		{
			printf("\r\n[3.2]f_open fail");
		}
	}
	else
	{
		printf("\r\n[1.3]mount or format ret fail");
	}
    
    f_unlink("0:/test.txt");
    res = f_open(&filp, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
    printf("\r\n[5.4]f_size = %d", (int)f_size(&filp));
    f_close(&filp);
 
	getchar();
    
    Flash_SSP_Entry();
    return 0;
    #endif
}

其中,有几个注意点:

(i)诸如FATFS fs;FRESULT res;等变量的定义最好用全局变量,一开始用做局部变量太大了,导致程序跑死;

(ii)如果首次挂载失败,报错13(FR_NO_FILESYSTEM),可以尝试清空flash数据之后,重新格式化。格式化的时间可能会稍微长一点,但是格式化完成之后,在进行挂载,就会比较快了;

(iii)挂载失败,报错13(FR_NO_FILESYSTEM)的可能性,还在于写文件接口disk_write中没有事先清除扇区导致flash_sector_erase(sector*FLASH_SECTOR_SIZE);

(iv)我这边读写都提供了两种读写方式,一种是每次读256个字节,分16次读完一个4K扇区,第二种是一次读完4K扇区,从本质上讲,两种方式没有区别:

【文件系统】FatFs文件系统在嵌入式芯片LPC18XX上的移植_第2张图片

程序运行结果如下:

【文件系统】FatFs文件系统在嵌入式芯片LPC18XX上的移植_第3张图片

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(嵌入式,嵌入式,单片机,lpc,c语言)