使用文件系统

本教程介绍 MicroPython 如何提供设备上的文件系统,允许将标准 Python 文件 I/O 方法与持久存储一起使用。

MicroPython 会自动创建默认配置并自动检测主文件系统,因此如果您想修改分区、文件系统类型或使用自定义块设备,本教程将非常有用。

文件系统通常由设备上的内部闪存支持,但也可以使用外部闪存、RAM 或自定义块设备。

On some ports (e.g. STM32), the filesystem may also be available over USB MSC to a host PC. pyboard.py 工具 also provides a way for the host PC to access to the filesystem on all ports.

注意:这主要用于 STM32 和 ESP32 等裸机端口。 在带有操作系统的端口(例如 Unix 端口)上,文件系统由主机操作系统提供。

VFS

MicroPython 实现了一个类 Unix 虚拟文件系统 (VFS) 层。 所有挂载的文件系统都组合成一个单一的虚拟文件系统,从根 / 开始。 文件系统被挂载到这个结构的目录中,并且在启动时工作目录被更改为主文件系统被挂载的位置。

在 STM32 / Pyboard 上,内部闪存安装在 /flash ,并且可以选择将 SDCard 安装在 /sd 。 在 ESP8266/ESP32 上,主文件系统挂载在 / 。 在 znzpi 上,内部闪存安装在 / 。除非安装了SDCard,SDCard将安装在 /

块设备

块设备是实现 uos.AbstractBlockDev 协议的类的实例。

内置的块设备

端口提供了内置的块设备来访问它们的主闪存。

开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。 如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。 端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。

STM32 / Pyboard /znzpi

The pyb.Flash class provides access to the internal flash. On some boards which have larger external flash (e.g. Pyboard D), it will use that instead. The start kwarg should always be specified, i.e. pyb.Flash(start=0).

注意:为了向后兼容,当构造没有参数(即 pyb.Flash() )时, 它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始包括一个虚拟分区表)。

ESP8266

内部闪存作为块设备对象公开,该对象在启动时在 flashbdev 模块中创建。 默认情况下,此对象作为全局变量添加,因此通常可以简单地作为 bdev 访问它。 这实现了扩展接口。

ESP32

The esp32.Partition class implements a block device for partitions defined for the board. Like ESP8266, there is a global variable bdev which points to the default partition. This implements the extended interface.

自定义的块设备

下面的类实现了一个简单的块设备,它使用 bytearray 将其数据存储在 RAM 中:

class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)

    def readblocks(self, block_num, buf):
        for i in range(len(buf)):
            buf[i] = self.data[block_num * self.block_size + i]

    def writeblocks(self, block_num, buf):
        for i in range(len(buf)):
            self.data[block_num * self.block_size + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4: # get number of blocks
            return len(self.data) // self.block_size
        if op == 5: # get block size
            return self.block_size

它的使用方法如下:

import os

bdev = RAMBlockDev(512, 50)
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/ramdisk')

支持简单接口和扩展接口(即:uos.AbstractBlockDev.readblocks()uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例是:

class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)

    def readblocks(self, block_num, buf, offset=0):
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            buf[i] = self.data[addr + i]

    def writeblocks(self, block_num, buf, offset=None):
        if offset is None:
            # do erase, then write
            for i in range(len(buf) // self.block_size):
                self.ioctl(6, block_num + i)
            offset = 0
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            self.data[addr + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4: # block count
            return len(self.data) // self.block_size
        if op == 5: # block size
            return self.block_size
        if op == 6: # block erase
            return 0

由于它支持扩展接口,因此可以使用 littlefs:

import os

bdev = RAMBlockDev(512, 50)
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/ramdisk')

一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如:

with open('/ramdisk/hello.txt', 'w') as f:
    f.write('Hello world')
print(open('/ramdisk/hello.txt').read())

文件系统

MicroPython 端口可以提供 FATlittlefs v1littlefs v2 的实现。

下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们

Board

FAT

littlefs v1

littlefs v2

pyboard 1.0, 1.1, D

Yes

No

Yes

Other STM32

Yes

No

No

ESP8266 (1M)

No

No

Yes

ESP8266 (2M+)

Yes

No

Yes

ESP32

Yes

No

Yes

FAT

FAT 文件系统的主要优点是它可以通过 USB MSC 在支持的板(例如 STM32)上进行访问,而主机 PC 上不需要任何额外的驱动程序。

但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。 对于不需要 USB MSC 的应用,建议使用 littlefs 代替。

使用FAT来格式化整个flash:

# ESP8266 and ESP32
import os
os.umount('/')
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/')

# STM32
import os, pyb
os.umount('/flash')
os.VfsFat.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')

Littlefs

Littlefs 是为基于闪存的设备设计的文件系统,并且更能抵抗文件系统损坏。

备注

有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅 littlefs issue 347littlefs issue 295

注意:它仍然可以通过 USB MSC使用 littlefs FUSEdriver 驱动程序 访问。 请注意,您必须使用 -b=4096 选项来覆盖块大小。

使用littlefs v2格式化整个flash:

# ESP8266 and ESP32
import os
os.umount('/')
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/')

# STM32
import os, pyb
os.umount('/flash')
os.VfsLfs2.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')

Hybrid (STM32)

通过使用 startlen kwargs 给 pyb.Flash,您可以创建跨越闪存设备子集的块设备。

例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs:

import os, pyb
os.umount('/flash')
p1 = pyb.Flash(start=0, len=256*1024)
p2 = pyb.Flash(start=256*1024)
os.VfsFat.mkfs(p1)
os.VfsLfs2.mkfs(p2)
os.mount(p1, '/flash')
os.mount(p2, '/data')
os.chdir('/flash')

这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上, 从而具有更好的电源故障恢复能力等。

偏移量 0 处的分区将自动挂载(并自动检测文件系统类型),但您可以添加:

import os, pyb
p2 = pyb.Flash(start=256*1024)
os.mount(p2, '/data')

boot.py 来挂载数据分区。

Hybrid (ESP32)

在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv 以定义任意分区布局。

在启动时,名为“vfs”的分区将默认挂载在 / ,但任何其他分区都可以使用:

import esp32, os
p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
os.mount(p, '/foo')