MicroPython的文件系统操作

本文介绍了MicroPython如何提供并使用设备上的文件系统,以及如何使用Python标准的I/O方法进行持久存储。MicroPython会自动创建默认配置并侦测主文件系统,同时支持修改分区、文件系统类型或自定义块设备。

文件系统通常由设备上的内部闪存支持,但也可以使用外部闪存、RAM 或自定义块设备。在某些端口(如 STM32)上,文件系统可以通过 USB MSC 连接到PC主机。pyboard.py 工具还为主机访问所有端口上的文件系统提供了一种途径。

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

虚拟文件系统 VFS(Virtual File System)

MicroPython实现了类似Unix的虚拟文件系统层,所有挂载的文件系统都被整合到一个根路径为/的虚拟文件系统。文件系统被挂载到该目录结构,并且在启动时工作目录会被更改到主文件系统的挂载目录。

STM32/Pyboard上,内置闪存被挂载在/flash,可选的SD卡被挂载在/sd.而ESP8266/ESP32的主文件系统则被挂载在/

块设备

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

内置块设备

端口提供内置块设备以访问其主闪存。

开机时,MicroPython 会尝试侦测默认闪存上的文件系统,并自动进行配置并加载,如果找不到文件系统,MicroPython 会尝试为整个闪存创建一个FAT文件系统。端口还可以提供"出厂重置"主闪存的机制,通常是在开机时通过按键组合来实现。

STM32/Pyboard

pyb.Flash类可以访问内部闪存,对于某些有大容量外部闪存的板子(如:Pyboard D)则会使用外部闪存,必须要指定start参数,如:pyb.Flash(start=0)

**注意:**为实现向后兼容性,不带参数的情况下(即 pyb.Flash()),仅实现简单的块接口,并反映 USB MSC 所显示的虚拟设备(即在开始时包含一个虚拟分区表)。

ESP8266

内部闪存以块设备对象的形式呈现,启动时在 flashbdev 模块中创建。默认情况下,该块设备对象被添加为全局变量,通常只需使用 bdev 即可访问。它实现了扩展接口。

ESP32

esp32.Partition 类为电路板定义的分区实现了一个块设备。与 ESP8266 类似,有一个指向默认分区的全局变量 bdev。它实现了扩展接口。

自定义块设备

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

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: # 获取块数
            return len(self.data) // self.block_size
        if op == 5: # 获取块大小
            return self.block_size

使用举例:

import os

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

下面举例说明同时支持简单接口和扩展接口(即同时支持 os.AbstractBlockDev.readblocks()os.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:
            # 先擦除,再写
            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: # 块数量
            return len(self.data) // self.block_size
        if op == 5: # 块大小
            return self.block_size
        if op == 6: # 块擦除
            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 移植可以提供 FAT、littlefs v1 和 littlefs v2 的实现。

下表显示了特定端口/板卡组合的固件默认包含哪些文件系统,但在定制固件构建中可以选择启用这些文件系统。

板子 FAT littlefs v1 littlefs v2
pyboard 1.0, 1.1, D
其它STM32
ESP8266(1M)
ESP8266(2M+)
ESP32

FAT

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

不过,FAT 不能容忍写入过程中的断电,这会导致文件系统损坏。对于不需要 USB MSC 的应用程序,建议使用 littlefs 代替。

使用 FAT 格式化整个闪存:

# ESP8266 和 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 第 347 期和 littlefs 第 295 期。

使用 littlefs v2 格式化整个闪存:

# 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')

使用 littlefs FUSE 驱动程序,还可以在电脑上通过 USB MSC 访问 littlefs 文件系统。请注意,必须同时指定 --block_size 和 --block_count 选项才能覆盖默认值。例如(在构建 littlefs-fuse 可执行文件后):

$ ./lfs --block_size=4096 --block_count=512 -o allow_other /dev/sdb1 mnt

这将允许在 mnt 目录下访问主板的 littlefs 文件系统。要获取 block_sizeblock_count 的正确值,请使用:

import pyb
f = pyb.Flash(start=0)
f.ioctl(1, 1)  # 在 littlefs raw-block 模式下,
block_count = f.ioctl(4, 0)
block_size = f.ioctl(5, 0)

Hybrid (STM32)

通过使用 pyb.Flashstartlen 参数,可以创建跨闪存设备子集的块设备。

例如,将前 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')

这对于通过 USB MSC 提供 Python 文件、配置和其他很少修改的内容可能很有用,但也允许将频繁更改的应用程序数据驻留在 littlefs 上,以更好地应对断电等情况。

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

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

到 boot.py 以挂载数据分区。

Hybrid (ESP32)

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

启动时,名为 "vfs "的分区默认挂载在/位置,但也可以使用 boot.py 挂载其他分区:

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

你可能感兴趣的:(micropython,硬件,python,单片机,物联网)