【ZYNQ开发】使用xilffs库实现对于SD卡的读写

1 Xiliffs库

​       Xilinx 的 Xilffs 库是一个用于嵌入式系统的FAT文件系统库。它支持FAT12、FAT16和FAT32乃至exFAT系统文件系统,适用于SD卡、eMMC等存储设备。Xilffs库与Xilinx的硬件IP核(如SD/SDIO控制器)紧密集成,提供文件读写、目录管理等基本操作。该库具有轻量级、可配置性强等特点,适合资源受限的嵌入式环境,广泛应用于数据记录、固件升级等场景。

2 BSP配置

       与其它常用的库(如 Lwip)一样,要使用 Xiliffs 库,需要先在BSP中添加对其的支持。步骤如下:

       1,打开需要使用 Xiliffs 库的核的工程 BSP。

在这里插入图片描述

       使用多核的需要注意,每个核之间的 BSP 是不互通的,像我上面示例图片中,就是把SD卡的保存放在了第三个核(CPU2)上,这时,其它两个核还是无法使用 Xiliffs 库,如有跨核使用的需求,需要分别进行配置。

       2,在点开的BSP设置中,勾选对于 Xiliffs 库的支持。

在这里插入图片描述

       3,点进左侧的 Xiliffs 库设置,进行相关参数的设置

在这里插入图片描述

       这里面,需要重点关注以下几个参数:

       enable_exfat:是否使能exFAT文件系统?

       enable_multi_partition:是否使用多个分区?

       fs_interface:给SD卡还是RAM使用?

       read_only:是否让文件系统设置为只读模式?(不能写)

       use_lfn:是否使能长文件名?(如果保存的文件字符数比较多(看别人说是6个)如果不开这个保存文件会失败)

3 文件IO操作

       1,挂载文件系统:

FATFS fs;

fresult = f_mount(&fs, "1:/", 0);
if (fresult != FR_OK)
{
    xil_printf("mount file system failed : %d\r\n", fresult);
    return 0;
}

       2,打开文件:

FIL file;

fresult = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);		//FA_READ
if (fresult != FR_OK)
{
    xil_printf("open file failed : %d\r\n", fresult);
    return 0;
}

       这里注意 f_open 的第三个参数,这里用到了两个宏,一个是 FA_CREATE_ALWAYS ,表示不管以前文件系统中是否存在名为 filename 的文件,都是创建一个新的,如果原来有,则会被覆盖掉;第二个宏为 FA_WRITE,表示为以写入模式打开文件,与之对应的,还有 FA_READ 表示以读取模式打开文件。


​ 3,写入文件

UINT bytesWritten, bytesRead;
char data_buffer[10000];      // 数据缓冲区

fresult = f_write(&file, data_buffer, sizeof(data_buffer), &bytesWritten);
if ((fresult != FR_OK) || (bytesWritten != DATA_BUFFER_SIZE))
{
    xil_printf("write file failed : %d, %d\r\n", fresult, bytesWritten);
}

       这里执行 f_write 传入了一个指针 &bytesWritten,最终可以通过读取 bytesWritten 来获取写入的数据字节数。这也引出了另一个问题,即 f_write 如果由于种种原因只是写入了部分数据,没有完全写入,fresult 不会表现出错误,所以在判断错误时,需要加入 bytesWritten 加以判断。


       4,读取文件

fresult = f_read(&file, read_data_buffer, sizeof(read_data_buffer), &bytesRead);
if (fresult != FR_OK)
{
    xil_printf("read file failed : %d\r\n", fresult);
    return 0;
}

​       与上面写入文件同理,执行后,可以通过 bytesRead 获取读到的字节数


       5,关闭文件

fresult = f_close(&file);
if (fresult != FR_OK)
{
    xil_printf("close file failed : %d\r\n", fresult);
}

       特别注意,不要省略了这个步骤,否则文件可能保存不下来!


       6,卸载文件系统

// 卸载文件系统
//fresult = f_mount(NULL, "1:/", 0);		//第一个参数为NULL就是卸载
fresult = f_unmount("1:/");

       有两种写法,但本质上是一样的,因为 f_unmout 是一个宏,它与上面使用 f_mount 是完全等价的:

#define f_unmount(path) f_mount(NULL, path, 0)

       这里需要说明,不是说不卸载文件系统,最后文件就保存不下来,毕竟大部分运用场景,掉电是不确定性的。

4 一些重要的细节

​       1,上面的 f_mount 的第二个参数是 “1:/”,这类似于 Windows 下的盘符的概念,如果不想设置,也可以直接传入 “”。但需要注意的是写入、读取文件与卸载文件系统时,也会涉及它,所有地方必须保持一致,否则一些操作会无效。


       2,如果要对文件进行读写操作,第三个参数可以继续或上,对应的宏,不是说只能读或只能写:

fresult = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE | FA_READ);

       3,有时候,我们并不希望一直在文件的尾部进行内容的追加,也可能会涉及到对于中间部分的操作,这时候,可以使用以下函数来修改表示文件当前所在位置的指针,来改变读写位置:

fresult = f_lseek(&file, 0);
if (fresult != FR_OK)
{
	xil_printf("f_lseek 0 failed : %d\r\n", fresult);
	return 0;
}

​       其中第二个参数就表示文件指针的目标位置(偏移量),从文件开头开始计算,单位为字节


​       4,要想实现文件名的递增操作,可以使用 snprintf 函数进行辅助:

snprintf(filename, sizeof(filename), "1:/f_%d.bin", file_cnt); 
file_cnt++; // 递增文件名计数器

fresult = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
if (fresult != FR_OK)
{
    xil_printf("open file failed : %d\r\n", fresult);
    return 0;
}

​       效果如下:

在这里插入图片描述


       5,如果对文件的一条龙操作(打开,写入,关闭)比较频繁,需要注意一下保存速度能否满足数据的带宽,我自己测试的SD卡,使用一个核单纯用来保存文件,最终的写入速度只有 10MB/s +,如果对速度有更高要求,需要换更高速的SD卡,甚至换硬盘。并且,经过测试,随着保存的文件的增多,这一条龙的操作时间有缓慢增加的趋势,对于实际应用需要考虑进去。


       6,可以操作的文件格式很多,比如 txt,bin,甚至是 csv等,但需要根据不同的文件格式进行对应的写入策略修改,如果求简单就用 txt 或者 bin 即可。

5 总体测试代码

       需要注意,这里的测试代码试图打开在SD卡中存在的一个名为 1.bmp 图片,读取其数据,大家可以根据自己情况灵活更改。

#include "string.h"
#include "ff.h"
#include <stdio.h>
#include "xil_printf.h"

#define SD_SAVE_BAG_NUM_ONCE 256			//256*(11*72)=202752Byte=198KB 0x31800
#define DATA_BUFFER_SIZE (256 * 11 * 72)

FATFS fs;
FIL file;
UINT bytesWritten, bytesRead;
FRESULT fresult;

char filename[20];


// 数据缓冲区(200KB)
char data_buffer[DATA_BUFFER_SIZE];      // 数据缓冲区
char read_data_buffer[2000000000];      // 数据缓冲区

// 全局变量,用于文件名递增
static int file_cnt = 0;

int sd_init()
{
	// 初始化数据
    for (int i = 0; i < DATA_BUFFER_SIZE; i++) {
        data_buffer[i] = (char)(i % 256); // 填充0-255的循环数据
    }

    // 挂载文件系统
    fresult = f_mount(&fs, "1:/", 0);
    if (fresult != FR_OK)
    {
    	xil_printf("mount file system failed : %d\r\n", fresult);
    	return 0;
    }

    xil_printf("sd init is successful\r\n");

    return 0;
}

void sd_open_write_close()
{
    snprintf(filename, sizeof(filename), "1:/f_%d.bin", file_cnt); // 格式化文件名,开始时,是file_0.bin
    file_cnt++; // 递增文件名计数器

    fresult = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
    if (fresult != FR_OK)
    {
    	xil_printf("open file failed : %d\r\n", fresult);
    	return 0;
    }
    
    // 写入数据
    fresult = f_write(&file, data_buffer, sizeof(data_buffer), &bytesWritten);
    if ((fresult != FR_OK) || (bytesWritten != DATA_BUFFER_SIZE))
    {
    	xil_printf("write file failed : %d, %d\r\n", fresult, bytesWritten);
    }
    
    // 关闭文件
    fresult = f_close(&file);
    if (fresult != FR_OK)
    {
    	xil_printf("close file failed : %d\r\n", fresult);
    }
}

void sd_ffs_end()
{
    // 卸载文件系统
    //fresult = f_mount(NULL, "1:/", 0);		//第一个参数为NULL就是卸载
    fresult = f_unmount("1:/");
    if (fresult != FR_OK)
    {
    	xil_printf("unmount file failed : %d\r\n", fresult);
    }
}

void  sd_ffs_read()
{
	fresult = f_open(&file, "1:/1.bmp", FA_OPEN_EXISTING | FA_READ);
    if (fresult != FR_OK)
    {
    	xil_printf("open 1.bmp failed : %d\r\n", fresult);
    }

	fresult = f_read(&file, read_data_buffer, sizeof(read_data_buffer), &bytesRead);

	xil_printf("read file num : %d\r\n", bytesRead);

    if (fresult != FR_OK)
    {
    	xil_printf("read file failed : %d\r\n", fresult);
    }
}

int main()
{
	int Status;

    xil_printf("CPU2 start!\r\n");

    Status = sd_init();

    sd_open_write_close();
    sd_open_write_close();
    sd_open_write_close();
    sd_open_write_close();
    sd_open_write_close();

	sd_ffs_read();

	sd_ffs_end();


	xil_printf("CPU2 end!\r\n");

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辣个蓝人QEX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值