使用 ROMFS¶
概述¶
ROMFS(只读内存文件系统)是一种为 MicroPython 设备设计的轻量级只读文件系统。它针对微控制器和嵌入式系统进行了优化,在这些系统中,代码和数据需要存储在闪存中,并能够被高效访问而无需复制到 RAM 中。
ROMFS 的主要优势包括:
零拷贝导入:存储在 ROMFS 中的
.mpy字节码文件可以直接从闪存中执行(内存映射),而无需先复制到 RAM 中。这与 冻结模块 的工作方式类似,但不需要重新刷写整个固件。低 RAM 开销:从 ROMFS 加载的
.mpy文件中的常量对象(字符串、字节等)直接从闪存引用,而不会在 RAM 中复制一份。灵活的部署方式:ROMFS 镜像可以在主机 PC 上构建,并使用 mpremote 部署到设备上,无需重新构建固件。
标准文件系统接口:ROMFS 挂载在 VFS 中,并通过普通的 Python 文件操作(
open、os.listdir、import等)进行访问。
ROMFS 与可读写的 FAT/LittleFS 文件系统(位于其他闪存分区中)以及 冻结模块(被编译进固件本身)互为补充。
板卡支持¶
在每一块在其闪存布局中预留了 ROMFS 分区的摄像头板卡上,OpenMV 固件都启用了 ROMFS。在这些板卡上,ROMFS 分区会在启动时自动检测并挂载到 /rom;/rom 和 /rom/lib 都会被添加到 sys.path 中,因此存储在那里的模块可以直接导入。
板卡 | ROMFS 支持 |
|---|---|
OpenMV Cam N6 | 是 |
OpenMV AE3 | 是 |
OpenMV Cam RT1062 | 是 |
OpenMV Cam Pure Thermal | 是 |
OpenMV Cam M4 / M7 / H7 / H7 Plus | 是 |
Arduino Giga | 是 |
Arduino Portenta H7 | 是 |
Arduino Nicla Vision | 是 |
Arduino Nano 33 BLE Sense | 否(无 ROMFS 分区) |
Arduino Nano RP2040 Connect | 否(无 ROMFS 分区) |
请参阅 romfs,这是一个 OpenMV 专用的辅助工具,用于检查挂载在 /rom 的 ROMFS。
工作流程¶
使用 ROMFS 的典型工作流程如下:
在你的 PC 上创建一个目录,存放你想要部署的 Python 文件(或
.mpy文件)。使用
mpremote romfs deploy <directory>来构建 ROMFS 镜像并将其部署到设备上。ROMFS 将在下次启动时挂载到
/rom(或者如果重启设备,可以立即挂载)。随后,设备上的 Python 代码就可以像从任何其他文件系统中那样从 ROMFS 中
import模块。
例如:
# On the host PC, with a directory "myapp/" containing app.py:
$ mpremote romfs deploy myapp/
软复位之后,设备上将有 /rom/app.py(如果安装了 mpy_cross,则为 /rom/app.mpy)可供导入。
有关 mpremote 子命令的完整细节,请参阅下面的 mpremote romfs 子命令 一节。
Python API¶
ROMFS 的 Python API 通过 vfs 模块提供。
- class vfs.VfsRom(buffer)
从 buffer 创建一个 ROMFS 文件系统对象,buffer 必须是一个支持缓冲区协议的对象(例如
bytes、bytearray或memoryview对象),且包含一个有效的 ROMFS 镜像。该构造函数会验证 buffer 是否以 ROMFS 魔数字节(
b"\xd2\xcd\x31")开头。如果缓冲区太小或不是有效的 ROMFS,则会引发OSError(ENODEV)。由该构造函数创建的对象可以使用
vfs.mount()进行挂载。示例:
import vfs # Load a ROMFS image from flash into a memoryview. dev = vfs.rom_ioctl(2, 0) # get partition 0 as a memoryview fs = vfs.VfsRom(dev) vfs.mount(fs, '/rom')
或者,挂载存储在文件中的 ROMFS 镜像:
import vfs with open('/flash/app.romfs', 'rb') as f: romfs_data = f.read() fs = vfs.VfsRom(romfs_data) vfs.mount(fs, '/rom2')
VfsRom对象上提供以下方法:- VfsRom.open(path, mode)
从 ROMFS 中打开一个文件。仅支持只读模式(
''、'r'、'rt'、'rb')。尝试以写入模式打开文件将引发OSError(EROFS)。返回的文件对象支持
read()、seek()、tell()和close()。对于以读取模式打开的二进制文件,返回的对象还支持缓冲区协议,因此可以获得文件数据的memoryview,它直接指向 ROMFS 内存(零拷贝)。
- VfsRom.ilistdir(path)
返回目录 path 中各条目的迭代器。每个条目是一个元组
(name, type, inode, size),其中 type 为文件时是0x8000,为目录时是0x4000。
- VfsRom.stat(path)
为 path 返回一个类似
os.stat的 10 元组。如果路径不存在,则引发OSError(ENOENT)。
- VfsRom.statvfs(path)
返回文件系统统计信息。块大小报告为 1,块数量表示 ROMFS 镜像以字节为单位的总大小。空闲块和空闲文件始终为 0(只读文件系统)。
- VfsRom.chdir(path)
在 ROMFS 内切换目录。仅支持根目录(
'/');切换到任何子目录都会引发OSError(EOPNOTSUPP)。
- VfsRom.getcwd()
返回 ROMFS 内的当前工作目录。始终返回
'/'。
- vfs.rom_ioctl(op, ...)
用于访问设备只读内存(ROM)分区的底层接口。
支持的操作如下:
vfs.rom_ioctl(1)—— 返回可用 ROM 分区的数量。vfs.rom_ioctl(2, id)—— 以memoryview对象的形式返回索引为 id 的 ROM 分区。该内存可以读取,但不能直接写入。vfs.rom_ioctl(3, id, length)—— 准备一个 ROM 分区以供写入。擦除索引为 id 的分区的前 length 个字节。返回以字节为单位的最小写入大小(后续写入所需的对齐量)。vfs.rom_ioctl(4, id, offset, buf)—— 将 buf(一个类字节对象)写入索引为 id 的 ROM 分区,写入位置为字节偏移量 offset。vfs.rom_ioctl(5, id)—— 完成对分区 id 的写入序列(执行写入后所需的任何收尾操作,例如缓存刷新)。
这些操作由
mpremote在内部用于部署 ROMFS 镜像。大多数用户无需直接调用vfs.rom_ioctl()。示例(查询可用分区):
import vfs n = vfs.rom_ioctl(1) print("Number of ROM partitions:", n) for i in range(n): dev = vfs.rom_ioctl(2, i) print(f" Partition {i}: {len(dev)} bytes")
启动时自动挂载¶
当固件中启用了 ROMFS 支持时,MicroPython 会在初始化期间自动尝试将第一个 ROM 分区挂载到 /rom。如果该分区包含有效的 ROMFS 镜像,则会将其挂载,并自动把 /rom 和 /rom/lib 添加到 sys.path 中。
这意味着,在使用 mpremote 部署 ROMFS 镜像之后,只需进行一次软复位即可使新模块可被导入。
如果分区中未找到有效的 ROMFS 镜像(例如在一块刚刚烧录过的板卡上),则会静默跳过挂载。
使用 mpremote 管理 ROMFS¶
mpremote 工具提供了三个子命令,用于管理已连接设备上的 ROMFS 镜像。
romfs query¶
$ mpremote romfs query
列出设备上所有可用的 ROMFS 分区及其大小。同时以十六进制显示每个分区的前 12 个字节,并报告是否存在有效的 ROMFS 镜像。
示例输出:
ROMFS0 partition has size 131072 bytes (32 blocks of 4096 bytes each)
Raw contents: d2:cd:31:XX:XX:XX:XX:XX:XX:XX:XX:XX ...
ROMFS image size: 1234
romfs build¶
$ mpremote romfs [-o <output>] build <source>
从主机 PC 上的目录 source 构建一个 ROMFS 镜像。该镜像会写入 output(默认值:<source>.romfs)。
选项:
-o <output>、--output <output>:指定输出文件路径。-m、--mpy(默认):在将.py文件添加到镜像之前,使用mpy_cross自动将它们编译为.mpy。需要mpy_crossPython 包(pip install mpy_cross)。--no-mpy:禁用对.py文件的自动编译。
示例:
$ mpremote romfs build myapp/
Building romfs filesystem, source directory: myapp/
/
|-- main.py -> .mpy
\-- lib/
\-- helper.py -> .mpy
Writing 2048 bytes to output file myapp.romfs
romfs deploy¶
$ mpremote romfs [-p <partition>] deploy <source>
将 ROMFS 镜像部署到设备。source 可以是以下两者之一:
主机上的一个目录:ROMFS 镜像会在内存中构建并直接部署。
一个
.romfs或.img文件:从磁盘读取镜像并部署。
选项:
-p <partition>、--partition <partition>:指定目标分区索引(默认值:0)。-m、--mpy(默认):当 source 是目录时,将.py编译为.mpy。--no-mpy:禁用对.py文件的自动编译。
部署完成后,必须对设备进行软复位,新的 ROMFS 才会挂载到 /rom。
示例:
$ mpremote romfs deploy myapp/
Building romfs filesystem, source directory: myapp/
/
|-- main.py -> .mpy
\-- lib/
\-- helper.py -> .mpy
Image size is 2048 bytes
ROMFS0 partition has size 131072 bytes (32 blocks of 4096 bytes each)
Preparing ROMFS0 partition for writing
Deploying ROMFS to ROMFS0 partition
ROMFS image deployed
$ mpremote soft-reset
示例¶
部署一个简单的应用程序¶
假设你有一个项目目录 myapp/,其结构如下:
myapp/
main.py
utils.py
lib/
helper.py
要将其部署到设备的 ROMFS:
$ mpremote romfs deploy myapp/
软复位之后,即可从 ROMFS 中导入这些模块:
import main
import utils
from lib import helper
从 Python 列出 ROMFS 内容¶
挂载之后,ROMFS 的内容可以像任何其他文件系统一样被浏览:
import os
for entry in os.ilistdir('/rom'):
print(entry)
# Or simply:
print(os.listdir('/rom'))
OpenMV 还附带了一个小型的 romfs 辅助工具,它会打印一份格式化的列表,其中包括每个文件的内存映射地址和对齐量:
from omv import romfs
romfs.ls_romfs()
在 ROMFS 中嵌套 ROMFS¶
作为文件存储在外层 ROMFS 中的 ROMFS 镜像可以作为嵌套文件系统挂载。例如,如果 /rom/inner.romfs 存在。由于 /rom 是一个 ROMFS,从中打开的文件对象支持缓冲区协议,因此可以直接获得一个零拷贝的 memoryview:
import vfs
with open('/rom/inner.romfs', 'rb') as f:
inner = vfs.VfsRom(memoryview(f))
vfs.mount(inner, '/inner')
print(os.listdir('/inner'))
ROMFS 镜像格式¶
ROMFS 镜像格式是一种紧凑的二进制格式,专为微控制器上的内存映射访问而设计。简要概述如下:
镜像以魔数字节
0xd2 0xcd 0x31开头(编码为"RM1",并将前两个字节的高位置位)。镜像的其余部分由若干 记录 组成,每条记录都带有一个类型标签(varuint)、一个长度(varuint)和一个载荷。
记录类型包括:填充、原样数据、间接数据指针、目录、文件。
目录名和文件名以带长度前缀的字节字符串形式存储。
文件数据可以原样(内联)存储,也可以通过指向镜像中其他位置的间接指针存储,后者可实现内存映射访问所需的对齐。
未知的记录类型会被静默跳过,从而提供向前兼容性。
该格式在 MicroPython 源代码的 extmod/vfs_rom.c 中定义。mpremote 用于构建镜像的 Python 实现位于 tools/mpremote/mpremote/romfs.py。
参见
使用文件系统 —— MicroPython VFS 及可用文件系统类型的概述。
MicroPython 清单(manifest)文件 —— 如何将 Python 模块冻结到固件中。
MicroPython .mpy 文件 —— MicroPython .mpy 二进制文件格式。
MicroPython 远程控制:mpremote —— 完整的 mpremote 命令参考。
romfs —— 用于检查已挂载的 /rom 文件系统的 OpenMV 辅助工具。