FatFs移植到STM32(内部flash)

本文详细介绍了如何将FatFs文件系统移植到STM32F103VET6芯片上,包括源码下载、移植条件、源码结构分析、配置参数、diskio.c接口适配、flash驱动的实现以及应用示例,提供了挂载设备、文件操作的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、FatFs简介

二、源码下载

三、移植条件

1、芯片参数

2、flash规划

四、源码结构分析

五、配置文件重要参数说明

1、配置扇区大小

2、配置逻辑设备个数

3、配置格式化文件系统接口

六、diskio.c代码适配

1、disk_status接口

2、disk_initialize接口

3、disk_read接口

4、disk_write接口

5、disk_ioctl接口

七、flash驱动实现

八、应用demo

1、挂载设备

2、文件操作

3、运行截图


一、FatFs简介

FatFs是免费,开源的轻量级 文件系统,纯c实现,移植简单

二、源码下载

GitHub - abbrev/fatfs: FatFs - Generic FAT File System Module

三、移植条件

1、芯片参数

note:如遇挂载的失败,裸机请记得调整栈空间。

芯片类型:STM32F103VET6

flash大小为512KB,RAM大小 64KB

2、flash规划

flash 前62KB存放代码,剩下的450KB由文件系统管理

四、源码结构分析

 用户移植适配的工作全部在diskio.c中完成。

五、配置文件重要参数说明

1、配置扇区大小

#define FF_MIN_SS        2048
#define FF_MAX_SS        2048

如果只需要挂载一个文件系统,以上两个值设置一样即可,本例中所用STM32 扇区大小为2048,所以均设置为2048。

如果需要挂载一个以上文件系统且两个设备扇区大小不同,以上两值设置范围要涵盖多个设备扇区范围。disk_ioctl接口需要提供GET_SECTOR_SIZE接口。

2、配置逻辑设备个数

#define FF_VOLUMES        1

3、配置格式化文件系统接口

#define FF_USE_MKFS        1

该字段配置为1,可以使用f_mkfs接口来格式化空白flash(在本例中为必配参数)

六、diskio.c代码适配

note:留意下各个接口返回值,小心返回错误。

1、disk_status接口

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = RES_OK;

	switch (pdrv) {
	case DEV_RAM :
		stat = STA_NOINIT;
		stat &= ~STA_NOINIT;
		// translate the reslut code here

		return stat;
	}
	return STA_NOINIT;
}

2、disk_initialize接口

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

	switch (pdrv) {
	case DEV_RAM :

		// translate the reslut code here
		return stat;
	}
	return STA_NOINIT;
}

3、disk_read接口

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 = RES_OK;
	int ret;

	switch (pdrv) {
	case DEV_RAM :
		// translate the arguments here
		ret = b_flash_read_sector(sector, count, buff, 0XFF);
		if(ret != 0)
		{
			res = RES_ERROR;
		}

		return res;
	}

	return RES_PARERR;
}

b_flash_read_sector,为底层flash读接口,下文会给出。

4、disk_write接口

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 = RES_OK;
	int ret;
	switch (pdrv) {
	case DEV_RAM :
		// translate the arguments here
		ret = b_flash_write_sector(sector, count, (u8*)buff, 0XFF);
		if(ret != 0)
		{
			res = RES_ERROR;
		}
		// translate the reslut code here

		return res;
	}

	return RES_PARERR;
}

b_flash_write_sector,为底层flash写接口,下文会给出。

5、disk_ioctl接口

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res = RES_OK;

	//LOG_INFO("disk_ioctl dev:%d\r\n", pdrv);

	switch (pdrv) {
	case DEV_RAM :

		// Process of the command for the RAM drive
		switch(cmd)
		{	
			case CTRL_SYNC :
				res = RES_OK;
			break;	
			case CTRL_TRIM:
				res = RES_OK;
			break;
			case GET_SECTOR_COUNT:
			{
				*(int*)buff = b_flash_get_sector_number();
			}break;
			case GET_SECTOR_SIZE:
			{
				*(int*)buff = b_flash_get_sector_size();
			}break;
			case GET_BLOCK_SIZE:
			{
				*(int*)buff = 1;
			}break;
		}
		return res;
	}


	return RES_PARERR;
}

b_flash_get_sector_number,为底层flash获取扇区个数接口,下文会给出。

b_flash_get_sector_size,为底层flash获取扇区大小接口,下文会给出。

七、flash驱动实现

note:s32\u32\u16\u8等为自定义实现,如报错,请按需修改。

//文件系统管理的flash大小, 只能设置为块大小的整数倍
#define ENTER_FLASH_SIZE_FOR_FS     (400)

//1KB的字节数
#define ENTER_FLASH_KB_SIZE         (1024)

//文件系统管理的flash起始地址
#define ENTER_FLASH_FS_START_ADDR   (FLASH_BANK1_END - (ENTER_FLASH_SIZE_FOR_FS * ENTER_FLASH_KB_SIZE) + 1)

//文件系统管理的flash结束
#define ENTER_FLASH_FS_STOP_ADDR    (FLASH_BANK1_END)

//flash的块大小
#define ENTER_FLASH_BLOCK_SIZE      (ENTER_FLASH_KB_SIZE * 2)

//flash的扇区大小
#define ENTER_FLASH_SECTOR_SIZE     (ENTER_FLASH_BLOCK_SIZE * 1)

#define FLASH_ADDR_SECTOR(n) (ENTER_FLASH_FS_START_ADDR + ((u32)n * ENTER_FLASH_SECTOR_SIZE))

s32 b_flash_read_sector(u32 sector, u16 number, u8 *data, u32 timeout)
{
    u32 addr = FLASH_ADDR_SECTOR(sector);
    u32 i = 0;
    u32 block_num = number * (ENTER_FLASH_SECTOR_SIZE / ENTER_FLASH_BLOCK_SIZE);
    for(i = 0; i < block_num; i++)
    {
        u32 offset = (i * ENTER_FLASH_BLOCK_SIZE);
        memcpy((void*)(data + offset), (void*)(addr + offset), ENTER_FLASH_BLOCK_SIZE);
    }
    return HAL_OK;
}

s32 b_flash_write_sector(u32 sector, u16 number, u8 *data, u32 timeout)
{
#define WRITE_FLASH_UNIT_BYTE_NUM (4)
    s32 data_len = 0;
    u32 error = 0;
    u32 ret = HAL_OK;
    FLASH_EraseInitTypeDef pEraseInit = {0};
    u32 block_num = number * (ENTER_FLASH_SECTOR_SIZE / ENTER_FLASH_BLOCK_SIZE);
    u32 addr = FLASH_ADDR_SECTOR(sector);
    pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
    pEraseInit.PageAddress = addr;
    pEraseInit.NbPages = block_num;
    data_len = block_num * ENTER_FLASH_BLOCK_SIZE;
    HAL_FLASH_Unlock();
    ret = HAL_FLASHEx_Erase(&pEraseInit, &error);
    if(ret != HAL_OK)
    {
        HAL_FLASH_Lock();
        return HAL_ERROR;
    }
    while(data_len > 0)
    {
        u32 *p_data32 = (u32*)data;
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *p_data32);
        addr += WRITE_FLASH_UNIT_BYTE_NUM;
        data += WRITE_FLASH_UNIT_BYTE_NUM;
        data_len -= WRITE_FLASH_UNIT_BYTE_NUM;
    }
    HAL_FLASH_Lock();
    return HAL_OK;
}

u32 b_flash_get_sector_size(void)
{
    return ENTER_FLASH_SECTOR_SIZE;
}

u32 b_flash_get_block_size(void)
{
    return ENTER_FLASH_BLOCK_SIZE;
}

u32 b_flash_get_sector_number(void)
{
    return ((ENTER_FLASH_FS_STOP_ADDR - ENTER_FLASH_FS_START_ADDR) / ENTER_FLASH_SECTOR_SIZE);
}

八、应用demo

note: 空白flash挂载文件系统会失败,根据挂载接口的返回值去格式化文件系统,格式化后,用户就可以进行文件系统操作了

1、挂载设备

#define DISK_FLASH    "0:"
static int phase1_flash_fs_init(void)
{
    static FATFS flash_fs;
    u32 ret = 0;
    MKFS_PARM opt = {0};
    opt.fmt = FM_FAT;
    static u8 work_buf[2048] = {0};
    ret = f_mount(&flash_fs, DISK_FLASH, 1);
    if(ret != FR_OK)
    {
        switch(ret)
        {
            case FR_NO_FILESYSTEM:
            {
                ret = f_mkfs(DISK_FLASH, &opt, work_buf, sizeof(work_buf));
            }break;
            case FR_MKFS_ABORTED:
            {
                ret = f_mkfs(DISK_FLASH, &opt, work_buf, sizeof(work_buf));
            }break;
        }
    }
    if(ret != 0)
    {
        LOG_ERROR("flash fs f_mkfs fail(%d)\r\n", ret);
    }
    else
    {
        LOG_INFO("flash fs f_mkfs succ\r\n");
    }
    return ret;
}

2、文件操作

#define USER_FILE_NAME "0:/data.txt"
s32 b_demo_user_fs(void)
{
    FIL fp;
    u32 real_write_num = 0;
    
    FRESULT ret = f_open(&fp, USER_FILE_NAME, FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
    if(ret != FR_OK)
    {
        LOG_ERROR("f_open fail:%s\r\n", USER_FILE_NAME);
        return -1;
    }

    u8 *test_w_b = "fs test write data";
    ret = f_write(&fp, test_w_b, strlen(test_w_b), &real_write_num);
    if(ret != FR_OK)
    {
        LOG_ERROR("f_open fail:%d\r\n", ret );
        return -1;
    }
    LOG_INFO("write len:%d\r\n", real_write_num);
    LOG_INFO("write data:%s\r\n", test_w_b);
    
    ret = f_close(&fp);
    if(ret != FR_OK)
    {
        LOG_ERROR("f_close fail:%d\r\n", ret);
        return -1;
    }

    u32 real_read_num = 0;
    u8 test_r_b[100] = {0};
    ret = f_open(&fp, USER_FILE_NAME, FA_READ);
    if(ret != FR_OK)
    {
        LOG_ERROR("f_open fail:%s\r\n", USER_FILE_NAME);
        return -1;
    }

    ret = f_read(&fp, test_r_b, 100, &real_read_num);
    if(ret != FR_OK)
    {
        LOG_ERROR("f_read fail:%d\r\n", ret);
        return -1;
    }
    LOG_INFO("read len:%d\r\n", real_read_num);
    LOG_INFO("read data:%s\r\n", test_r_b);

    ret = f_close(&fp);
    if(ret != FR_OK)
    {
        LOG_ERROR("f_close fail:%d\r\n", ret);
        return -1;
    }
    return 0;
}

3、运行截图

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值