[GD32开发日记 1] Fatfs 文件系统移植

开发环境基本介绍

本次开发建立在GD32F470开发板上,利用的语言是C语言开发编译平台为keil4平台,利用的程序模板框架为GD32官网可下载的示例代码:🥸🥸🥸

GD32F4xx_Demo_Suites_V2.6.1\GD32470I_EVAL_Demo_Suites\
Projects\17_SDIO_SDCardTest

目标是完成利用Fatfs 文件系统,实现SD卡的文件的读取,写入等操作。

FatFs基本介绍😵‍💫😵‍💫

FatFs是一个轻量级的嵌入式文件系统,适用于嵌入式设备的闪存、磁盘和SD卡等存储介质。它可以支持FAT12、FAT16和FAT32格式的文件系统,并且具有非常高的移植性和可扩展性。

FatFs的主要特点包括:

  1. 代码精简:FatFs的代码量非常小,可以很容易地嵌入到嵌入式设备中。

  2. 高度可配置:FatFs支持FAT12、FAT16和FAT32格式的文件系统,可以根据设备的需要进行配置。

  3. 高度可移植:FatFs的核心代码与底层驱动分离,因此可以很容易地移植到不同的平台上。

  4. 支持长文件名:FatFs支持长文件名,可以在设备中存储具有更有描述性的文件名。

  5. 多任务支持:FatFs可以在多个任务之间共享,支持多个文件同时打开和读写操作。

  6. 支持目录:FatFs支持目录和子目录的创建和操作。

  7. 具有高效性能:FatFs具有很高的文件读写性能和小的存储占用,适合嵌入式系统的应用。

总之,FatFs是一个轻量级、可配置、可移植和高效的嵌入式文件系统,非常适合嵌入式设备的应用。

具体流程

下载FatFs官方代码

官网链接:链接
下载好的文件解压后,应该是如下图所示:
fatfs
其中document告诉我们具体的详细信息,source是源代码,是我们重点需要关注的对象。😼😼

将source文件夹整个复制到工程文件夹下,在keil中添加项目文件,最后会得到如下图所示的工程文件结构:
文件结构
其中各个文件的作用此处不再赘述。

补全函数

官网描述
根据官网描述,想要移植文件系统,总共需要重写5个函数,关闭一个功能。

关闭get_fattime函数

修改:找到ffconf.h中的FF_FS_NORTC,将其改为1,如下:

#define FF_FS_NORTC		1

补全其余5个函数:

其余5个函数全在diskio.c文件中。
由于我们只需要读取SD卡,因此程序可以将其他的设备统一默认为SD卡读写,我个人写的函数如下所示:

  1. disk_status :
DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS status = STA_NOINIT;
	uint32_t *psdstatus;
	sd_error_enum card_state;

    if (pdrv == 0)
    {
        card_state = sd_sdstatus_get(psdstatus); 
		if (card_state == SD_OK)	return RES_OK;
		else return RES_ERROR;        
    }

    return status;
}

此处有坑! 注意好,因为GD32的sd检查返回值和Fatfs函数期望的返回值之间有区别,不能直接返回sd_sdstatus_get的检测结果,要用一个if判断!!! 后面代码同理😿😿😿😿


  1. disk_initialize
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	
		return 0;

}

这里解释一下为什么只要return 0就可以了,在fatfs文件系统中,每一个步骤没有问题返回的都是宏定义RES_OK,而这个宏定义对应的数字为0
另外,而在该程序中,我们用的是GD32的官方用例,用例中的main函数部分就已经把初始化给完成了,因此只要在此返回0就可以了。


  1. 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;
	int result;

	switch (pdrv) {
	default:
			result = sd_block_read((uint32_t*)buff, (uint32_t)(sector * SD_BLOCKSIZE), SD_BLOCKSIZE);
			// //printf("flag22221 , result is %d\n",result); // result is 29 == SD_OK 

			if ( result == SD_OK )
				return 	RES_OK;
	}

	return RES_PARERR;
}

  1. 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;
	int result;

	switch (pdrv) {
		default:
			result = sd_block_write((uint32_t*)buff, (uint32_t)(sector * SD_BLOCKSIZE), SD_BLOCKSIZE);
		    // res = sd_block_write((uint32_t *)(&buff[0]),sector << 9 ,SECTOR_SIZE); // todo:   
			if ( result == SD_OK )
				res = RES_OK; 
          	return res;
	}

	return RES_PARERR;
}

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

	return RES_OK;
}

值得说明的是:如果你的文件系统出现了
f_mount无法挂载,f_read无法读写等情况,且返回值为

#define FR_WRITE_PROTECTED,		/* (10) The physical drive is write protected */

请务必检查disk_read函数,因为fs系统想要挂载磁盘,必须先从磁盘中读取出分区信息,根据函数追踪如下所示,f_mount调用的函数栈如下:

static FRESULT mount_volume ()
|
find_volume(fs, LD2PT(vol))
||
||
fmt = check_fs(fs, 0);
|||
|||
if (move_window(fs, sect) != FR_OK) return 4;  /* Load the boot sector */
||||
||||
if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) 
}
|||||
|||||
// result = sd_block_read((uint32_t *)(&buff[0]),sector << 9 ,SECTOR_SIZE); // todo:

result = sd_block_read((uint32_t*)buff, (uint32_t)(sector * SD_BLOCKSIZE), SD_BLOCKSIZE);

//printf("flag22221 , result is %d\n",result); // result is 29 == SD_OK
return result;
// 问题出在,gd32返回的是SD_OK,
//而如果直接返回result,则返回值是29,
//而ff系统的resok返回的是RES_OK,对应的值为0

最后就是disk_read函数出错导致的问题。


文件系统实操

在完成上述操作后,就可以进行文件系统实操环节了,最后输出如下图:
(不要在意flag,懒得删了, )😼😼😼
结果输出

最后贴出测试的代码:

void function(){
    // disk_initialize(3);

    res_sd=f_mount(&fs,"0:",1);
    if (res_sd != FR_OK){
        printf("mount fail , error code : %d " , res_sd);
        while(1);
    }else{          
        printf("mount success");
                // while(1)
    }

        printf("\r\n****** file writing test... ******\r\n");     


        res_sd = f_open(&fnew, "0:new.txt", FA_CREATE_ALWAYS | FA_WRITE ); 

        if ( res_sd == FR_OK )
        {
            
            /* 将指定存储区内容写入到文件内 */

            res_sd=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);

            if(res_sd==FR_OK)

            {
                gd_eval_led_off(LED1);
                gd_eval_led_off(LED3);
                printf("writing success\n");
        
            }

            else

            {

                printf("writing fail\n");
                gd_eval_led_off(LED1);

            }    

                /* 不再读写,关闭文件 */
            
            // f_sync(&fnew);
            // f_close(&fnew);
            // res_sd_write = f_sync(&fnew);

        }

        else

        {        
                
                printf("failed to open file,error code:%d\r\n" , res_sd);

        }

        

/*------------------- 文件系统测试:读测试 ------------------------------------*/

        printf("****** start reading test... ******\r\n");

        res_sd = f_open(&ftest, "0:test.txt", FA_OPEN_EXISTING | FA_READ);    

        if(res_sd == FR_OK)

        {

                printf("open success\r\n");

                res_sd = f_read(&ftest, ReadBuffer, sizeof(ReadBuffer), &fnum); 

            if(res_sd==FR_OK)

            {

            printf("manage to read the data: %d\r\n",fnum);

            printf("the data is:\r\n%s \r\n", ReadBuffer);        

            }

            else

            {

            printf("fail to read file(%d)\n",res_sd);

            }                

        }

        else

        {

                printf("fail to open file, error code : %d\r\n" , res_sd); 

        }

        /* 不再读写,关闭文件 */

        f_close(&ftest);

        printf("end test\n");

        f_close(&fnew);        

        printf("end file\n");

        /* 不再使用文件系统,取消挂载文件系统 */

        f_mount ( NULL,"0:",1);
}

写在最后,还有一个f_close的坑😭😭😭

f_close() 函数会调用数据同步函数,此时会向sd卡真正写入数据,之前的f_write()函数并不会真正向sd卡写入数据,只是把数据放在缓存中;
我在完成前面所有操作后,猛然发现程序运行到 f_close()后,就会卡死。研究以后发现,问题出在:
由于开发板的传输速率和SD卡的写入速率不匹配,导致写入失败(或者写入时会占用所有CPU?具体原理我也不太懂)

此时需要调整一下SD卡程序的时钟频率,具体调整位置如下:
在GD32470I_EVAL_Demo_Suites\Projects\17_SDIO_SDCardTest\Soft_Drive\ sdcard.c 中:

#define SD_CLK_DIV_TRANS                    ((uint16_t)0x0008)        
/* SD clock division in transmission phase */

调整上述宏定义的数值,把数值调大即可(上述代码是调过以后的,从2变为8)。


至此文件系统移植结束

表情包

感谢各位观看,有帮助的话点个赞吧❤️❤️❤️

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值