跳转至

文件系统使用指南

本文档面向 HiDiTing 社区开发者,说明 3322 平台文件系统的选择、配置、镜像制作、烧录和应用开发方法。应用开发者可通过 Libc/POSIX 文件接口完成文件读写;文件系统集成人员可进一步了解 VFS、VFAT 和 YAFFS 的实现及配置。

阅读说明

适用范围

本文档适用于 HiDiTing 3322 平台 SDK。VFAT/FAT32 用于 eMMC 等块设备;YAFFS 用于 NAND Flash。应用开发者重点阅读“快速开始”和“VFS 文件系统”,文件系统集成人员还应阅读对应文件系统及镜像制作章节。

阅读准备

快速开始

文件系统选型

存储介质 推荐文件系统 典型用途 后续阅读
eMMC / 块设备 VFAT(FAT32) 需要 FAT32 兼容性的文件存储 VFAT 文件系统
NAND Flash YAFFS 原始 NAND Flash 的文件存储 YAFFS 文件系统

最小使用流程

  1. 确认存储介质并按上表选择文件系统。
  2. 按“镜像制作与烧录”完成镜像配置、编译和烧录。
  3. 应用通过 VFS 提供的 open()read()write()close() 等 POSIX 接口读写文件。
  4. 若读写失败,先检查路径和挂载点、镜像配置及烧录结果,再查阅“常见问题与注意事项”。

快速跑通文件系统 Demo

Demo 功能与归档路径

文件系统 Demo 归档在 /samples/native_samples/filesystem/README.md。执行一条 AT 指令即可完成以下闭环:

  1. /user 挂载点下创建专用测试目录 /user/fs_demo
  2. 创建 source.txt,写入固定字符串并关闭文件。
  3. 将文件重命名为 result.txt,重新打开后读回并逐字节校验。
  4. 通过 stat() 检查文件长度。
  5. 删除本次测试创建的文件和目录,不影响 /user 下的其他数据。
samples/native_samples/filesystem/
├── CMakeLists.txt          # 定义 filesystem_sample 组件和公开编译宏
├── filesystem_demo.c      # VFS 操作与 AT+FSTEST 实现
├── filesystem_demo.h      # AT 注册函数声明
└── README.md              # 工程级构建和运行说明

编译、烧录与串口准备

说明:本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。

开发环境 适用场景 使用指南
一站式 CLI(推荐) 快速完成目标选择、构建、烧录和串口监视 一站式 CLI 开发环境使用指南
HiSpark Studio for VS Code 图形化编辑、编译、烧录和调试 HiSpark Studio for VS Code 开发环境使用指南
WSL 与 Docker 在 Windows 上使用一致的 Linux 容器构建环境 WSL 与 Docker 环境使用指南

社区目标通过 CONFIG_ENABLE_FILESYSTEM_SAMPLEfilesystem_sample 组件接入固件。完成一站式 CLI 环境配置后执行:

fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

运行 Demo

在串口中发送:

AT+FSTEST

预期输出如下:

[FS] read-back=HiDiTing filesystem demo size=24
[FS] PASS
OK

[FS] PASS 表示目录创建、文件写入、重命名、重新打开、数据校验、属性读取和清理均成功。若输出 mkdir failedopen renamed file failed,先确认 /user 挂载点存在且可写,再检查文件系统配置和镜像烧录结果。

核心代码走读

/samples/native_samples/filesystem/filesystem_demo.c 将测试范围限制在 /user/fs_demo,入口和退出路径都会清理这两个固定文件,避免误删业务目录。

创建目录并写入文件:

if (mkdir(FILESYSTEM_DEMO_DIR, FILESYSTEM_DEMO_DIR_MODE) != 0) {
    goto exit;
}

fd = open(FILESYSTEM_DEMO_SOURCE_FILE,
    O_CREAT | O_TRUNC | O_WRONLY, FILESYSTEM_DEMO_FILE_MODE);
ssize_t write_len = write(fd, g_filesystem_demo_payload,
    sizeof(g_filesystem_demo_payload) - 1U);
(void)close(fd);

重命名后重新打开文件,验证的不是内存缓存中的原始字符串,而是 VFS 返回的文件内容:

if (rename(FILESYSTEM_DEMO_SOURCE_FILE, FILESYSTEM_DEMO_TARGET_FILE) != 0) {
    goto exit;
}

fd = open(FILESYSTEM_DEMO_TARGET_FILE, O_RDONLY);
ssize_t read_len = read(fd, buffer, sizeof(buffer) - 1U);
(void)close(fd);

if (read_len != (ssize_t)(sizeof(g_filesystem_demo_payload) - 1U)) {
    return false;
}
if (memcmp(buffer, g_filesystem_demo_payload,
    sizeof(g_filesystem_demo_payload) - 1U) != 0) {
    return false;
}

最后通过 stat() 核对文件长度,并显式调用 unlink()rmdir() 回收测试数据。基于该 Demo 开发自己的文件业务时,应替换为应用独占目录,同时保留每一步返回值检查和失败清理路径。

文件系统背景知识

软件架构

应用程序不直接依赖具体文件系统,而是经由 Libc/POSIX 与 VFS 访问底层存储。VFS 根据路径和挂载信息将请求分发至 VFAT 或 YAFFS。

图 1 文件系统软件架构

文件系统软件架构

关键配置

src/kernel/liteos/LiteOS/self_src/tools/build/config/3322.config 中,以下配置决定文件系统功能是否参与构建:

配置项 作用
LOSCFG_FS_VFS_BLOCK_DEVICE 启用 VFS 的块设备支持,供块设备文件系统使用。
LOSCFG_FS_YAFFS 启用 YAFFS 文件系统及相关适配代码。
CONFIG_VFS_BIND_WITH_PATH 启用按路径进行 VFS 绑定/挂载的支持。
LOSCFG_FS_YAFFS_INDEPENDENT_MEMORY_POOL 为 YAFFS 启用独立内存池;对应 3322 的 OS 适配初始化逻辑。

文件系统架构说明

文件系统架构如图 2 所示。

图 2 文件系统架构

文件系统架构

文件系统包括Libc、VFS、VFAT,用户应用可以通过Libc层文件接口使用文件系统。

虚拟文件系统VFS(Virtual File System)采用标准的Unix系统调用读写位于不同物理介质上的不同文件系统,为各类文件系统提供统一的操作界面和应用编程接口。VFS向下兼容各底层存储介质和文件系统类型,实现对open()、read()、write()等系统调用的直接调度而不用关心底层物理存储介质的差异。

虚拟文件分配表VFAT(Virtual File Allocation Table)采用32位的文件分配表,支持最大分区容量128GB,最大文件4GB,该文件系统提供了文件打开、关闭、读取、写入、删除等接口,用户可通过使用这些接口,对eMMC数据进行各类操作。

YAFFS(Yet Another Flash File System)是一种专门针对闪存的文件系统,它是专为NAND类型的闪存存储器设计的。该文件系统提供了文件打开、关闭、读取、写入、删除等接口,用户可通过使用这些接口,对NAND Flash数据进行各类操作。

VFS 文件系统

VFS 为应用开发者提供统一的文件访问入口。无论底层选择 VFAT 还是 YAFFS,常规业务代码均通过本节的 POSIX 接口完成文件操作;底层文件系统差异由 VFS 屏蔽。

VFS 概述

虚拟文件系统VFS也可称为虚拟文件转换系统,是一个内核软件层。

VFS有如下两个作用:

  • 处理与Unix标准文件系统相关的所有系统调用。
  • 为各种文件系统提供一个通用的接口。

VFS 开发流程

VFS 结构

虚拟文件系统VFS中的目录和文件按照目录树的方式组织。

图 3 VFS文件目录结构

VFS文件目录结构

VFS 介绍

VFS主要的四个对象如下:

  • 超级块对象:代表一个特定的已挂接的文件系统。
  • 索引节点对象:代表一个特定的文件项。
  • 目录对象:代表一个特定的目录项。
  • 文件对象:代表一个与进程相关的打开的文件。

图 4 VFS对象之间的关系

VFS对象之间的关系

超级块对象

超级块对象(super_block)存储了描述特定文件系统的信息。通常超级块对象对应位于磁盘上特定扇区的文件系统超级块或文件系统控制块。

超级块对象由许多数据项组成,如下所示:

  • 该文件系统所挂接的设备。
  • 文件系统的基本块大小。
  • 脏标志,表示超级块已经修改过,但还没有写回到磁盘。
  • 文件系统类型。
  • 标志,如只读标志。
  • 指向文件系统根目录的指针。
  • 打开文件的列表。
  • 控制访问该文件系统的信号量。
  • 操作超级块的函数指针数组的指针。

索引节点对象

一个索引节点对象(inode)与一个文件相关联。索引节点对象包含一个命名文件除该文件的文件名和该文件的实际数据内容外的所有信息,例如所有者、组、权限、文件的访问时间、数据长度和链接数等信息。

目录对象

目录对象(directory entry)是一个路径上的一个特定的组成。该组成是一个目录名或者是文件名,目录项对象为访问文件和目录提供了方便。

目录对象包括一个指向索引节点的指针和超级块,还包括一个指向父目录的指针和指向子目录的指针。

文件对象

文件对象(file)代表由进程打开的文件,存放打开文件与进程之间进行交互的有关信息,这些信息仅当进程访问文件期间存放在内核中。

文件对象由相应的open()系统调用创建,由close()系统调用撤销。

VFS 功能

标准 Posix 接口函数如表 1 所示。

表 1 Posix接口函数列表

接口名称

接口描述

open

打开文件。

read

读取文件。

write

写入文件。

seek

移动文件读取位置。

stat

获取文件属性。

close

关闭打开的文件。

unlink

删除文件。

说明: close接口说明: 目前vfs的close接口还可以同时用于网络句柄(socket句柄)的关闭,举例如下: 在文件“/src/kernel/liteos/LiteOS/self_src/tools/build/config/3322.config”中,添加代码“LOSCFG_NET_LWIP_SACK=y”即可。这样就打开了“close接口支持文件系统句柄和网络句柄”的功能。 功能说明: 目前系统中,文件句柄的取值范围为0~128(不包含128),而网络句柄的取值范围为128~(128+配置的可连接网络句柄数量),LOSCFG_NET_LWIP_SACK=y时,close接口将根据句柄的取值选择调用文件系统的接口或者网络系统的接口。

VFAT 文件系统

VFAT 适用于 eMMC 等块设备。应用开发者通过 VFS 接口读写文件;本节的接口说明和开发流程同时面向需要完成分区、格式化及底层集成的系统开发人员。

VFAT 概述

VFAT是文件系统的一种,用于嵌入式目标上的eMMC设备。具有很好的稳定性及兼容性,可充分兼容Windows、Linux、LiteOS系统,并且维护方便。

说明: VFAT是FAT(File Allocation Table)文件系统的扩展,最早是在Windows 95中引入的。VFAT保持与FAT的向后兼容性,但放宽了一些限制规则。例如,VFAT文件名最多可包含255个字符,VFAT在FAT的基础上保留了文件名的大小写,但使用时并不区分文件名大小写。

VFAT 文件系统结构

扇区是eMMC中最基本的数据存储单元。每个扇区通常包含512Byte的数据。当操作系统或应用程序需要从eMMC读取或写入数据时,它会以一个或多个连续的扇区为单位进行操作。

簇是一组连续的扇区,它是文件分配表(FAT)中分配存储空间的基本单位。

VFAT 文件系统由 DBR(DOS Boot Report)及其保留扇区、FAT 和 DATA 三个部分组成,其结构如图 5 所示。

图 5 VFAT文件系统结构

VFAT文件系统结构

此结构是在分区被格式化时所创建的,各部分含义如下:

  • DBR及其保留扇区:DBR的含义是DOS(Disk Operating System)引导记录,也称为操作系统引导记录,在DBR之后往往会有一些保留扇区。
  • FAT:FAT表是用于管理文件存储的关键数据结构。它记录了文件在存储设备上的簇(cluster)分配情况,通过链式结构将文件的各个数据块连接起来,从而实现对文件的组织和访问。
  • DATA:数据区,是FAT32文件系统的主要区域,其中包含目录区域。

VFAT 文件系统介绍

  • VFAT的主要作用是标明分区的存储介质以及簇的使用情况。
  • VFAT表由VFAT表项构成,VFAT表项简称VFAT项,每个VFAT项占用4Byte,每个VFAT项都有一个固定的编号,编号从0开始。
  • VFAT表项的前两个VFAT项为文件系统保留使用,0号簇VFAT为介质类型,1号簇VFAT为文件系统错误标志。
  • 分区格式化后,用户文件以簇为单位存放在数据区中,一个文件至少占用一个簇。
  • 分区的数据区中的每个簇都会映射到VFAT表中的唯一一个VFAT项,0号VFAT和1号VFAT被系统占用,用户的数据则从2号VFAT开始记录。
  • 如果某个文件占用多个簇,则第一个VFAT项记录下一个VFAT项的编号(即簇号),如果这个文件结束,则用“0x0F FF FF FF”表示簇结束。
  • VFAT1跟在DBR之后,其具体地址由DBR的BPB(BIOS Parameter Block)参数中指定,VFAT2跟在VFAT1之后。

VFAT 开发指导

FAT32文件系统的读写接口对上层业务开发人员是不可见的。在使用文件系统接口时,上层业务开发人员无需关注FAT32文件系统的具体实现,只需要调用VFS层文件系统接口进行文件读写操作即可。具体的文件系统读写接口可在“VFS 功能”中查看。

VFAT 集成接口说明

以下表 2表 3 所示接口上层业务开发人员无需关注,仅供底层FAT32文件系统集成人员参考使用。

表 2 添加分区接口说明

头文件

接口名

描述

disk.h

add_mmc_partition

添加eMMC分区,用户可以设置新分区的起始扇区和大小。

  • add_mmc_partition函数有三个参数:
    • 第一个参数:描述分区信息的结构体。
    • 第二个参数:分区的起始扇区,必须要在磁盘的有效范围内,且不与现有的分区重叠。
    • 第三个参数:表示分区大小,以扇区为单位,分区大小应小于等于剩余的eMMC存储空间。

表 3 挂载与卸载分区接口说明

头文件

接口名

描述

mount.h

mount

挂载分区

umount

卸载分区

  • mount()函数有五个参数:

    • 第一个参数:设备节点,该参数需要与add_mmc_partition ()函数对应起来,设备节点“/dev/mmcblk0p”为固定前缀,后面拼接对应的分区号。
    • 第二个参数:挂载点,挂载点名称可自定义,类似于“/user/”或“/system/”这类根目录都可以。
    • 第三个参数:填写“vfat”。
    • 第四个参数:分区的读写特性,填0可读可写。
    • 第五个参数:最后一个参数填写NULL。
  • umount()函数有1个参数:

    参数对应mount()挂载分区名称。

VFAT 开发流程

按如下步骤集成FAT32文件系统,上层业务开发人员无需关注。

  1. 在“/src/drivers/chips/3322/porting/mmc/mmc_init.c”中,添加以下代码,使能eMMC。

    pre_timer_init();  // kernel调度前使用timer, 保证emmc在kernel初始化前正常使用
    ret = emmc_drv_init();
    if (ret != ENOERR) {
        printf("[%s:%d] ret[%d]\n", __FUNCTION__, __LINE__, ret);
        return ret;
    }
    /* 卡时钟门控配置,器件初始化后才能配置 */
    writel(EMMC_BASE_ADDR + CKG_CTRL_AND_STS, 0x2000000);
    
  2. 调用add_mmc_partition创建分区,最多只能建立16个分区,分区号为0~15。

    创建分区的示例代码如下:

    struct disk_divide_info emmc = {.sector_count = 0xffffffff};
    if (ret = add_mmc_partition(&emmc, 2048, 230000)) {
        dprintf("add mmc partition 0 failed, return %d\n", ret);}
    if (ret = add_mmc_partition(&emmc, 232048, 460000)) {
        dprintf("add mmc partition 1 failed, return %d\n", ret);}
    

    “ret”等于0时创建成功。

  3. 调用mount函数挂载分区。

    // 规定时间内多次访问该分区,访问成功则尝试挂载分区
    do {
        ret = los_part_access("/dev/mmcblk0p0", EMMC_PART_MODE);
        ++try_access_num;
        uapi_tcxo_delay_ms(20);
    } while ((ret != ENOERR) && (try_access_num < 20));
    if (ret != ENOERR) {
        return -EACCES;
    }
    ret = mount("/dev/mmcblk0p0", "/system/", "vfat", 0, NULL);
    if (ret) {dprintf("mount FAT32 err %d\n", ret);}
    

    “ret”等于0时挂载成功。

  4. 调用umount函数卸载分区。

    调用umount("/system/")函数卸载分区。

VFAT 注意事项

  • 文件系统目前最大支持4G文件存储,并在磁盘0扇区开始的位置固定为bootrom预留2048个扇区。
  • 磁盘默认被划分为5个分区,前四个分区大小固定,最后一个分区使用剩余空间。

如果需要调整分区大小,可以在“product_evb_standard.h”文件中修改分区大小。

// Emmc一共五个分区,前四个分区大小固定,最后一个user分区使用剩余空间。
#define EMMC_VOLUME_BOOT_SECTOR_COUNT       12000ULL
#define EMMC_VOLUME_SYS_SECTOR_COUNT       3145666ULL
#define EMMC_VOLUME_INNER_SECTOR_COUNT     262144ULL
#define EMMC_VOLUME_SYS_BK_SECTOR_COUNT    524288ULL

如果需要调整分区名称,可以在“mmc_init.c”文件中修改分区名称。

#define FF_VOLUMES_NUM              5
#define FF_VOLUME_NAME_BOOT          "/boot/"
#define FF_VOLUME_NAME_SYS          "/system/"
#define FF_VOLUME_NAME_INNER        "/inner/"
#define FF_VOLUME_NAME_SYS_BK       "/systembk/"
#define FF_VOLUME_NAME_USER         "/user/"
struct ff_volume_info {
    const char *name;
    uint64_t sector_count;
};
static struct ff_volume_info g_ff_volume_infos [FF_VOLUMES_NUM] = {
    {FF_VOLUME_NAME_BOOT, EMMC_VOLUME_BOOT_SECTOR_COUNT},
    {FF_VOLUME_NAME_SYS, EMMC_VOLUME_SYS_SECTOR_COUNT},
    {FF_VOLUME_NAME_INNER, EMMC_VOLUME_INNER_SECTOR_COUNT},
    {FF_VOLUME_NAME_SYS_BK, EMMC_VOLUME_SYS_BK_SECTOR_COUNT},
    {FF_VOLUME_NAME_USER, 0xffffffff} // 运行时修正为剩余扇区数
};

表 4 eMMC内的文件系统默认分区

设备节点

分区大小(扇区数量)

挂载目录

/dev/mmcblk0p0

12000

/boot

/dev/mmcblk0p1

3145666

/system

/dev/mmcblk0p2

262144

/inner

/dev/mmcblk0p3

524288

/systembk

/dev/mmcblk0p4

剩余空间

/user

说明: 第一次挂载之前需要进行eMMC的格式化操作,可以使用AT命令“AT^FSFORMAT”来实现全盘的格式化。后续系统重启,重新烧写镜像,都不应再次格式化eMMC,否则会导致存储的数据丢失。

YAFFS 文件系统

YAFFS 面向 NAND Flash。应用开发者只需通过 VFS 接口访问文件;需要配置 NAND 分区、内存池或集成 YAFFS 的开发人员请继续阅读本节开发指导和注意事项。

YAFFS 概述

YAFFS(Yet Another Flash File System)是一种适用于嵌入式系统的文件系统,主要用于闪存设备。而NAND Flash是一种闪存类型,也是一种非易失性存储器。YAFFS文件系统用于管理NAND Flash上的数据。它可以在闪存中实现可靠的数据存储和访问,并提高数据存储的效率。YAFFS文件系统提供了文件打开、关闭、读取、写入、删除等接口,用户可通过使用这些接口,对NAND Flash数据进行各类操作。

YAFFS 开发指导

YAFFS文件系统的读写接口对上层业务开发人员是不可见的。在使用文件系统接口时,上层业务开发人员无需关注YAFFS文件系统的具体实现,只需要调用VFS层文件系统接口进行文件读写操作即可。具体的文件系统读写接口可在“VFS 功能”中查看。

YAFFS文件系统的默认分区:

  • 文件系统目前最大支持512MB文件存储。并将block0预留给bootrom。
  • 磁盘默认被划分为 3 个分区,分区大小为剩余总容量的 5%、25%、70%。如果需要调整分区比例,可以在 nand_init.c 文件中修改分区名称或分区大小的比例。分区名称与分区大小比例为一一对应关系,如果需要增删需要修改 YF_VOLUMES_NUM 改成对应的数量。

    #define YF_VOLUMES_NUM 3
    #define YF_NAME_STRS "boot", "/system/", "/user/"
    uint32_t yf_volume_percent[YF_VOLUMES_NUM] = {5, 25, 70};
    

表 5 YAFFS文件系统默认分区

设备节点

分区大小

挂载目录

/dev/nandblk0

5%

/boot

/dev/nandblk1

25%

/system

/dev/nandblk2

70%

/user

YAFFS 集成接口说明

以下接口上层业务开发人员无需关注,仅供底层YAFFS文件系统集成人员参考使用。

表 6 接口说明

接口声明 功能说明
add_mtd_partition() 添加 YAFFS 分区。系统自动为设备节点命名,命名规则为“/dev/nandblk + 分区号”。
delete_mtd_partition() 删除已经卸载的分区。
  • add_mtd_partition添加YAFFS分区时,系统会自动对起始地址和分区大小根据设备block大小进行对齐。该函数有四个参数:

    • 第一个参数:介质,固定填写“nand”。
    • 第二个参数:分区的起始地址,以16进制的形式传入。起始地址的取值范围由NAND Flash介质的存储空间大小决定。起始地址为0x20000的整数倍,包括地址0。
    • 第三个参数:表示分区大小,以16进制的形式传入,分区大小应小于等于NAND Flash介质存储空间的大小,大小为0x20000的整数倍,例如NAND Flash的存储大小为512MB,配置起始地址为0则配置的分区大小应小于等于0x20000000。
    • 第四个参数:表示分区号,有效值为0~19。
  • delete_mtd_partition函数有两个参数:

    • 第一个参数:分区号,与add_mtd_partition()函数的第四个参数对应。
    • 第二个参数:介质,固定填写“nand”。

表 7 接口说明

接口声明 功能说明
mount() 挂载分区。
umount() 卸载分区。
  • mount()函数有五个参数:

    • 第一个参数:设备节点,该参数需要与add_mtd_partition()函数对应起来,设备节点“/dev/nandblk”为固定前缀,后面拼接add_mtd_partition的第四个参数分区号。
    • 第二个参数:挂载点,挂载点名称可自定义,类似于“/user/”或“/system/”这种根目录都可以。
    • 第三个参数:填写“yaffs”。
    • 第四个参数:分区的读写特性,填0可读可写。
    • 第五个参数:最后一个参数填写NULL。
  • umount()函数有1个参数:

    参数与mount()的第二个参数相同。

YAFFS 开发流程

按如下步骤集成YAFFS文件系统,上层业务开发人员无需关注。

  1. 在“/src/drivers/chips/3322/fmc/nand/nand_init.c”添加如下代码,使能NAND Flash。

    nand_flash_init();
    
  2. 调用add_mtd_partition创建分区,最多只能建立20个分区,分区号为0~19。

    创建分区的示例代码如下:

    if (ret = add_mtd_partition("nand", 0x0, 0x1400000, 0)) {
        dprintf("add yaffs partition 0 failed, return %d\n", ret);}
    if (ret = add_mtd_partition("nand", 0x1400000, 0x2800000, 1)) {
        dprintf("add yaffs partition 1 failed, return %d\n", ret);}
    

    “ret”等于0时创建成功。

  3. 调用mount函数挂载分区。

    ret = mount("/dev/nandblk0", "/user/", "yaffs", 0, NULL);
    if (ret) {dprintf("mount yaffs err %d\n", ret);}
    

    “ret”等于0时挂载成功。

  4. 调用umount函数卸载分区。

    调用umount("/user/")函数卸载分区。

  5. 调用delete_mtd_partition函数删除分区。

    调用delete_mtd_partition删除分区前要卸载分区。

    ret = delete_mtd_partition(0, "nand");if (ret != 0)
    { printf("delete yaffs error\n");} else { printf("delete yaffs ok\n");}
    

YAFFS 注意事项

须知: 由于NAND Flash器件支持内部ECC(Error Correcting Code)特性,该特性在版本上需要使用宏“ENABLE_ECC”进行开启或者关闭,当版本上该特性发生切换(开启 -> 关闭或者关闭 -> 开启)时,由于NAND Flash内部存储结构发生变化,需要对文件系统进行格式化后再使用。

对于YAFFS的多分区特性,分区起始地址可灵活配置,但分区间地址不能重叠,用户需要根据Flash使用情况合理配置空间,防止与其它文件系统或其它不适合挂载YFFAS文件系统的空间发生冲突。

  • 建议添加单个分区的block数量不少于10个block。
  • 建议分区使用前先进行擦除操作。
  • 建议分区数应该小于等于4个,否则影响系统运行效率和内存占用。

镜像制作与烧录

本章说明将资源生成文件系统镜像并随 loaderboot 烧录的方法。请先按存储介质选择 FAT 或 YAFFS 流程,再严格执行对应的开发、编译与烧录步骤。

镜像制作与烧录概述

eMMC器件对应的文件系统是FAT32,而NAND Flash器件对应的文件系统是YAFFS。根据这两种文件系统的特点,生成file.bin镜像的规则有一些差异:

  • FAT32文件系统,file.bin的镜像大小必须与分区大小一致。这意味着在生成file.bin时,需要确保镜像的大小与分区的大小完全相同。
  • YAFFS文件系统,file.bin的大小无需与分区大小一致,只需要与资源大小一致即可。这意味着在生成file.bin时,只需要保证镜像的大小与需要存储的资源大小相同即可。

因此,根据不同的文件系统类型,生成file.bin的规则会有所不同。对于FAT32,需要保证镜像大小与分区大小一致;而对于YAFFS,只需要保证镜像大小与资源大小一致即可。

  • FAT32的文件系统的镜像大小修改位置为“/src/build/config/target_config/3322/mk_emmc_image/mkfatimgtool.py”。

    需注意:文件系统第一个分区的可用空间大小与这里配置的大小相同

    配置分区信息,请根据实际情况进行配置,添加格式为[分区路径, 分区大小]。

    分区大小需要根据按照百分系统分配好后的sector数进行计算,大小=sector(对应区域扇区数) * cfg_sector_size(单扇区字节数) / 1024(KB单位换算)

    分区大小必须指定,如果分区system大小为1572833KB,并且只需要做system分区的镜像,分区配置如下:

    partition_list = [
        #[os.path.join(cur_dir, 'fs', 'boot'), "6000", "file_boot.bin"],   # boot, 分区大小不能大于6000KB
        [os.path.join(cur_dir, 'fs', 'system'), "1572833", "file_system.bin"],   # system, 分区大小不能大于1572833KB
        #[os.path.join(cur_dir, 'fs', 'inner'), "131072", "file_inner.bin"],     # inner, 分区大小不能大于131072KB
        #[os.path.join(cur_dir, 'fs', 'systembk'), "262144", "file_systembk.bin"],   # systembk, 分区大小不能大于262144KB
        #[os.path.join(cur_dir, 'fs', 'user'), "263000", "file_user.bin"],       # user, 分区大小不能大于emmc剩余大小
    ]
    
  • YAFFS的文件系统的镜像大小修改位置为“/src/build/config/target_config/3322/mk_fs_image/mkyaffs2tool.py”。

    配置分区信息,请根据实际情况进行配置,添加格式为[分区路径, 分区大小]。

    分区大小需要根据按照百分系统分配好后的blocks数进行计算,大小=blocks(文件块个数)*pages_per_block(每个文件块包含的页数)*bytes_per_page(每页包含字节数)。

    置成null代表镜像大小根据资源文件大小自适应。

    partition_list = [
        [os.path.join(cur_dir, 'fs', 'boot'), "0x1980000"],
        [os.path.join(cur_dir, 'fs', 'systerm'), "0x8000000"],
        [os.path.join(cur_dir, 'fs', 'user'), "null"],
    ]
    

图 6 eMMC与NAND Flash器件镜像对比图

eMMC与NAND-Flash器件镜像对比图

FAT 文件系统镜像制作与烧录

FAT 镜像概述

制作FAT文件系统镜像前,需要确认当前所有分区的大小,这个需要通过A核启动初始化日志中查看,这个数值决定了生成img的大小。当前eMMC默认设定大小为4GB,第一分区默认为boot分区。

FAT 镜像开发指导

FAT的文件系统镜像相关工具在SDK的“build\config\target_config\3322\mk_emmc_image”目录下。

制作文件系统镜像前,需要按照实际使用配置修改以下参数,配置参数都位于“mkfatimgtool.py”脚本内头部,如下所示:

  • image_name:生成镜像的文件名,当前默认为“file”,生成文件名为“file.bin”。
  • cfg_sector_size:eMMC器件单扇区字节数,默认为512Byte。
  • partition_list:FAT文件系统的分区配置,具体配置说明如下:

    partition_list = [
        [os.path.join(cur_dir, 'fs', 'boot'), "6000", "file_boot.bin"],   # boot, 分区大小不能大于6000KB
        [os.path.join(cur_dir, 'fs', 'system'), "1572833", "file_system.bin"],   # system, 分区大小不能大于1572833KB
        [os.path.join(cur_dir, 'fs', 'inner'), "131072", "file_inner.bin"],     # inner, 分区大小不能大于131072KB
        [os.path.join(cur_dir, 'fs', 'systembk'), "262144", "file_systembk.bin"],   # systembk, 分区大小不能大于262144KB
        [os.path.join(cur_dir, 'fs', 'user'), "263000", "file_user.bin"],       # user, 分区大小不能大于emmc剩余大小
    ]
    

    以上参数格式为[资源文件路径, 分区大小, 生成的镜像文件名],其中资源文件路径为装有烧录资源文件的路径,默认为脚本当前目录下的fs目录。

    分区大小为要生成镜像的KB大小。

说明:

  • 当前支持生成一个文件系统镜像,默认挂载所有分区。分区大小必须根据实际eMMC初始化容量填写,如果填写比实际容量小,则会造成未填写大小的容量无法使用,导致容量浪费。如果填写比实际容量大,则烧录会覆盖下一个分区的FAT头数据,可能导致下一个分区无法读取和使用。
  • APP核的分区大小配置请参见“VFAT 注意事项”,当前系统默认配置是eMMC按照4GB容量配置,如果资源文件大于分区大小,则需要适配A核分区的大小,提高到适宜值。如果资源文件较少,无需那么大空间,为了降低烧录时间,可以适当降低分区的大小。

FAT 镜像编译指导

Windows环境下制作filebin文件方法

支持烧录FAT文件系统镜像作为loaderboot功能特性,默认不打开。如需打开需要进行编译配置,具体步骤如下:

  1. loaderboot编译配置(当前默认开启),需要在loaderboot target中添加宏:SUPPORT_EMMC_LOADERBOOT、PROVISION_WRITE_WITH_INTERFACE,以及添加组件:3322_loaderboot_emmc,chip_boot_config。

    SDK代码:target_config.py

    图 7 在loaderboot target中添加宏

    FAT镜像编译宏配置

  2. application 编译配置,在对应的 ACore target(SDK 配置:config.py)中将 fs_image 的开关置为 True,编译后生成的文件系统镜像位于 output 目录中(SDK 产物目录为 output/3322),如图 8所示。

    须知: Linux环境下,制作镜像前需要执行指令: sudo apt-get update sudo apt-get install dosfstools

    图 8 将fs_image设置为true

    将fs_image设置为true

    Windows环境下制作镜像:

    如生成镜像的过程中,有报拒绝访问错误,则是FAT文件系统镜像制作工具(mkfatimg.exe)缺失权限导致,需要配置工具权限,方法如下:

    1. 选中mkfatimg.exe程序,右键选中属性,如下属性配置框,选择“安全”标签,在“组或者用户名”选择“everyone”,点击“高级”按钮。

      图 9 属性配置框

      属性配置框

    2. 进入到“高级”按钮对话框后,如下图,双击“权限条目”中的“Everyone”选项,出现配置权限弹窗。

      图 10 高级安全设置

      高级安全设置

    3. 配置权限弹窗,请确保“读取和执行”有被选中。

      图 11 配置权限弹窗

      配置权限弹窗

    4. 配置完成后,点击所有窗口“确定”按钮,完成权限配置。

  3. 执行对应target的pack命令,生成对应版本包。

FAT 镜像烧录指导

说明:

  1. 由于FAT文件系统镜像较大,建议使用USB烧录的方式进行烧录,减少烧录耗时。
  2. eMMC烧录时的地址单位不是字节而是扇区(512Byte),例如烧录地址为0x76000800,代表烧录的起始地址为eMMC的第2048(即0x800)个扇区,其中0x76000000为偏移量。

选择上述loaderboot和application编译后生成的PKG包,即可使用BurnTool工具进行版本整包烧录,其中“file.bin”为制作的文件系统镜像,默认勾选即可烧录。由于文件系统镜像“file.bin”实现在loaderboot阶段烧录进eMMC器件,所以文件系统镜像“file.bin”需要与编译的loaderboot同时烧录。

图 12 烧录示意图

烧录示意图

YAFFS 文件系统镜像制作与烧录

说明: 由于YAFFS文件系统镜像制作工具(mkyaffs2image)只有Linux版本,所以本文下述章节约束说明,YAFFS文件系统镜像制作需要在Linux环境进行。

YAFFS 镜像概述

制作YAFFS文件系统镜像前,需要确认以下两点:

  1. 使用的NAND Flash器件参数,具体参数如下:

    • blocks(文件块个数)
    • pages_per_block(每个文件块包含的页数)
    • bytes_per_page(每页包含的字节数)
    • bytes_per_oob(每个oob包含的字节数)

      例:NAND Flash的容量大小为512MB,该器件有4096个block,每个block有64个页,每页有2048个字节,每个oob区包含64个字节。

  2. 当前版本的文件系统分区大小。

    例:文件系统默认分三个区,boot分区,大小占5%;system分区,大小占25%;user分区,大小占70%。

    其中分区百分比主要用于计算block大小,system分区大小占比25%,即表示它占用的block数为4096×25%=1024,system分区实际占用的字节数=1024×64×2048=134217728yte=0x8000000Byte,其他分区也使用同样方式进行计算,可以得出分区字节大小。

须知: 上述配置参数(blocks、pages_per_block、bytes_per_page和bytes_per_obb)必须和当前使用的NAND Flash器件参数相同,否则制作的文件系统镜像会出现读写异常。 相关参数可查阅使用的NAND Flash器件手册或咨询厂家获取。

YAFFS 镜像开发指导

编译 YAFFS 文件系统镜像的 Python3 脚本为 mkyaffs2tool.py

制作文件系统镜像前,需要按照实际使用配置修改以下参数,配置参数都位于“mkyaffs2tool.py”脚本内头部,如下所示:

  1. image_name:生成镜像的文件名,当前默认为“file”,生成文件名为“file.bin”。
  2. cfg_page_size:NAND Flash器件的bytes_per_page值,以实际器件为准。
  3. cfg_oob_size:NAND Flash器件的bytes_per_oob值,以实际器件为准。
  4. partition_list:YAFFS文件系统的分区配置,具体配置说明如下:

    partition_list = [
        [os.path.join(cur_dir, 'fs', 'boot'), "0x1980000"],
        [os.path.join(cur_dir, 'fs', 'systerm'), "0x8000000"],
        [os.path.join(cur_dir, 'fs', 'user'), "null"],
    ]
    

每个分区都是partition_list中的一个元素,而每个分区元素要包含制作的文件路径和分区大小信息,文件路径为相对于“mkyaffs2tool.py”脚本所在位置的相对路径,分区大小信息为十六进制。

如上示例配置,文件系统镜像包含三个分区目录,分别包含当前目录fs下的boot,system和user三个子目录,大小分别为0x1980000,0x8000000和剩余空间。这样做出来的文件系统镜像是包含boot、system和user分区的。如果只需要单分区的镜像,只保留该分区的配置即可。

需要注意的是文件系统的最后一个分区的大小可不指定,直接置成null即可,默认使用剩余空间。文件系统单分区时,请直接置为null即可,无需设置大小。

最后将需要制作的资源文件放到fs目录下对应的文件夹中即可。

说明: 当前生成的文件系统镜像大小是由YAFFS文件系统的分区配置和资源文件的大小决定的,如果放在除最后一个分区的其他分区内的资源文件不足以填满这个分区,那剩下空间将填充0xFF数据,保证镜像文件内包含分区数据。只有最后一个分区只填充资源文件,不会填充0xFF数据。所以,如果想降低生成镜像大小,首先除最后分区以外的其他分区的大小尽量缩小,其次将资源文件都放在除最后分区的其他分区内。

举例:

  1. 当前使用情况,前面一个分区占30%,最后一个分区占70%。

    资源文件放在前一个分区内,则最终镜像大小=总空间×30%。

    资源文件放在最后分区,则最终镜像大小=总空间×30%+资源文件大小。

  2. 如果将前一个分区的大小修改为20%,最后一个分区大小占80%

    资源文件放在前面分区内,则最终镜像大小=总空间×20%。

    资源文件放在最后分区,则最终镜像大小=总空间×20%+资源文件大小。

  3. 如果将4个分区修改成一个分区

    则最终镜像大小=资源文件大小。

YAFFS 镜像编译指导

支持烧录YAFFS文件系统镜像作为loaderboot功能特性,默认不打开。如需打开需要进行编译配置。

  1. loaderboot编译配置(当前默认开启),需要在loaderboot target中添加宏:SUPPORT_FMC_NAND,PROVISION_WRITE_WITH_INTERFACE;以及添加组件:3322_loaderboot_fmc,chip_boot_config。

    SDK代码:target_config.py

    图 13 在loaderboot target中添加宏

    YAFFS镜像编译宏配置

  2. application 编译配置,在对应的 ACore target(SDK 配置:config.py)中将 fs_image 的开关置成 True,编译后生成的文件系统镜像位于 output 目录中(SDK 产物目录为 output/3322)。

    图 14 fs_image的开关置为True

    fs_image的开关置为True

说明: 编译补充: 由于YAFFS文件系统镜像制作工具(mkyaffs2image)只支持在Linux系统上使用,所以给出在Window系统上编译带有镜像的版本包方法:

  1. 镜像依旧需要在Linux系统上生成,跳转到“/src/build/config/target_config/3322/mk_fs_image/mkyaffs2tool.py”目录下,然后执行“python3 mkyaffs2tool.py”指令。
  2. 将生成在“/src/build/config/target_config/3322/mk_fs_image/mkyaffs2tool.py”目录中的“file.bin”文件复制到Window系统上相同路径的目录中。
  3. 按照上述编译指导配置相关参数,然后进行编译,即可将“file.bin”镜像打包到版本包中。

YAFFS 镜像烧录指导

说明:

  1. 由于YAFFS文件系统镜像较大,建议使用USB烧录的方式进行烧录,减少烧录耗时。
  2. NandFlash烧录的地址单位为字节,例如烧录起始地址为0x36020000,指的是烧录起始位置从NandFlash的0x20000字节开始,其中0x36000000为偏移量。
  1. 选择上述loaderboot和application编译后生成的PKG包,即可使用BurnTool工具进行版本整包烧录,其中“file.bin”为制作的文件系统镜像,默认勾选即可烧录。

    图 15 烧录示意图

    烧录示意图-1

  2. 由于文件系统镜像“file.bin”实现在loaderboot阶段烧录进NandFlash器件,所以文件系统镜像“file.bin”需要与编译的loaderboot同时烧录。

常见问题与注意事项

现象 优先检查项 关联章节
文件无法打开 文件路径、挂载点和文件系统镜像是否已正确烧录 VFS 文件系统镜像制作与烧录
文件读写失败 open()/read()/write() 的返回值、存储介质与文件系统选型 VFS 文件系统VFAT 文件系统YAFFS 文件系统
镜像未生效 fs_image 开关、编译产物以及 loaderboot 烧录步骤 镜像制作与烧录
YAFFS 不可用 LOSCFG_FS_YAFFS、独立内存池配置与 NAND 分区配置 文件系统背景知识YAFFS 文件系统

各文件系统的专项限制和配置要求仍以对应章节中的“注意事项”为准。