使用文件系统 内容 使用文件系统 . e4 J) h9 G2 L ]8 {3 @
虚拟FS 块设备 # j6 } W0 T d3 S
文件系统 + B. X1 k/ ^1 B, g' B; K
2 M* [) B5 B# a) z; `
* p3 E E/ a! Y2 R & g1 J# u& A- l, ^( a' K
本教程介绍 MicroPython 如何提供设备上的文件系统,允许将标准 Python 文件 I/O 方法与持久存储一起使用。 MicroPython 会自动创建默认配置并自动检测主文件系统,因此如果您想修改分区、文件系统类型或使用自定义块设备,本教程将非常有用。 文件系统通常由设备上的内部闪存支持,但也可以使用外部闪存、RAM 或自定义块设备。 在某些端口(例如 STM32)上,文件系统也可以通过 USB MSC 连接到主机 PC。pyboard.py 工具还为主机 PC 提供了一种访问所有端口上的文件系统的方法。 注意:这主要用于 STM32 和 ESP32 等裸机端口。在带有操作系统的端口(例如 Unix 端口)上,文件系统由主机操作系统提供。 虚拟FSMicroPython 实现了一个类 Unix 虚拟文件系统 (VFS) 层。所有挂载的文件系统都组合成一个单一的虚拟文件系统,从 root 开始 /。文件系统被挂载到这个结构的目录中,并且在启动时工作目录被更改为主文件系统被挂载的位置。 在 STM32/Pyboard 上,内部闪存安装在 /flash,可选的 SDCard安装在/sd。在 ESP8266/ESP32 上,主文件系统挂载在 /。
! \ l, J0 }! H# j! [, u# B块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 ' {5 r; M2 y5 [; c0 f* Z
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。 ; f1 W! z# R7 K) h
ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 # U7 i4 H: `* D# }* f
4 I8 I4 B: ]! u. h) W$ I
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
4 n! H$ F% x& n$ |' k - def __init__(self, block_size, num_blocks):
1 `5 b1 c: p9 b+ J3 r - self.block_size = block_size
( C% f6 ^$ [5 I+ [, b: J& R* A/ K - self.data = bytearray(block_size * num_blocks)9 u1 @3 s. a( C3 v3 g1 S, v1 F/ B
6 V, @) `9 [9 h+ h- def readblocks(self, block_num, buf):
) `, q4 \. F O+ j2 H0 q5 F+ G, T/ ] - for i in range(len(buf)):; ?! o/ z; J! b7 x
- buf[i] = self.data[block_num * self.block_size + i]
Q2 u6 A2 e* B' a& P/ L2 y& b" V
8 R9 p5 U: Y1 N( R8 A3 H- def writeblocks(self, block_num, buf):
4 k* g; d I1 C9 M V6 r( j' a - for i in range(len(buf)):
) C0 Y, k0 Y5 Z- } - self.data[block_num * self.block_size + i] = buf[i]% C8 h+ H2 g, f z5 d
- # Z, ^: u2 F; k
- def ioctl(self, op, arg):% W0 o. ^1 A1 g6 X: ?* H
- if op == 4: # get number of blocks
1 n3 \8 E( ~7 K - return len(self.data) // self.block_size
& X! W$ l/ w2 d5 e2 g2 a _: J: W - if op == 5: # get block size
" R6 I+ S" |- S" t - return self.block_size
复制代码
7 c8 x E! [( \$ A* ?! d
/ n' k1 U8 l+ l
* T5 m( s2 |- j1 ~它可以按如下方式使用: - import os
/ F; ^1 h3 k8 @+ D8 b - * J5 X8 o( C; ` C
- bdev = RAMBlockDev(512, 50)
! }( ]9 q5 f% @6 J - os.VfsFat.mkfs(bdev); d8 y/ K2 o* R. \0 n/ a4 M' r
- os.mount(bdev, '/ramdisk')
复制代码 5 n v$ H( \: z m( l) z+ D% z" Q2 f- c- E
" v$ s0 a8 U- U' g6 _/ }
# S* ^0 R* g! y! p1 r0 I) G支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:( y7 a8 j! S6 p" J# M9 e4 z+ B$ X) f
- def __init__(self, block_size, num_blocks):+ J8 i. S& M; \4 V5 T) e% R3 d6 e
- self.block_size = block_size5 J- M2 l4 |* y. w
- self.data = bytearray(block_size * num_blocks)
* ?' a5 g. W6 m& }
) p1 \7 V- }; n! J' P. b6 o7 F- def readblocks(self, block_num, buf, offset=0):! P1 d( J3 I2 Z! ? D
- addr = block_num * self.block_size + offset
5 q M; D4 C6 l4 F2 m - for i in range(len(buf)):8 c) \$ n& q4 t0 R9 ?
- buf[i] = self.data[addr + i]4 ?1 H5 W. v# K- c
- a8 ~8 n" f- F" d* U. {% X
- def writeblocks(self, block_num, buf, offset=None):4 r B* C J! y# F4 f" ?& L0 G) M
- if offset is None:
5 V0 b3 q: Z2 j a* ~% ?* b) B - # do erase, then write
, d) v) d* y' F+ G3 C* o - for i in range(len(buf) // self.block_size):2 C/ p8 C* y1 v4 w
- self.ioctl(6, block_num + i)
3 y5 }( r/ z" U( m3 r - offset = 04 C3 p# o6 `: h% t) w+ h
- addr = block_num * self.block_size + offset0 E, K! X3 C! ]/ F) M
- for i in range(len(buf)):5 A2 f0 o- q! Q
- self.data[addr + i] = buf[i]
" @- w' r$ }7 I. ~! X' c - " ^2 ]* e% u8 C( L; u
- def ioctl(self, op, arg):7 M- S! o/ v7 x0 R
- if op == 4: # block count9 e" u m( R: p" I
- return len(self.data) // self.block_size
3 v2 ?( f9 y8 R" c5 L7 Y% L: l - if op == 5: # block size, A4 M4 t' l2 o1 U/ X
- return self.block_size/ b! l( K2 {3 }, V1 s
- if op == 6: # block erase, ?$ S+ _ @/ `+ L4 @% p5 ~" b
- return 0
复制代码 2 B- T# S4 T3 F
/ P2 X( M9 N, K$ l) E) o
) I% C) z$ m( e8 o" h由于它支持扩展接口,因此可以用于littlefs: - import os
6 B, w' z K; D4 t9 u& E9 T - 5 ]/ }+ z3 @6 Y! L4 c
- bdev = RAMBlockDev(512, 50)9 @0 F: |# T6 n6 ]
- os.VfsLfs2.mkfs(bdev)7 J% F+ |+ I7 I, ]* N/ s% X
- os.mount(bdev, '/ramdisk')
复制代码
% s$ z% R$ m, J' r" G
, P" J) q) [4 E- i! e% p/ m
; V! l: D* V5 `+ u) v一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:
! Q! D# t* v7 { - f.write('Hello world')
- X% f- C& n u3 K7 p2 s: b - print(open('/ramdisk/hello.txt').read())
复制代码
# `1 O' h0 U1 P0 A! i. X9 \. e+ F$ t1 P) t; ~
0 [1 |: {7 @; Y7 M! p7 @( z( K$ g2 E2 }# t. K
2 ?1 Q7 {* w; g/ @4 g1 ~文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
! d5 j9 l8 b1 @$ X& lFATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32" S5 i& s/ T% L5 l% T
- import os: U' \( F- F& b1 D# t
- os.umount('/')
1 P7 a* {% ~* ^' W) `9 q - os.VfsFat.mkfs(bdev)
' X( B8 L, F/ ^# |0 ^2 R - os.mount(bdev, '/')
( K8 x( `! t8 s1 j& S" q( h6 C+ l
_7 l. t4 ]: B; O- # STM32! J% l8 l+ b$ m# q% `: |
- import os, pyb. V, i0 ~0 G! \. c
- os.umount('/flash')# f* M# R3 {1 ~
- os.VfsFat.mkfs(pyb.Flash(start=0))
# J4 {1 z' O3 u6 E( G8 f9 Y - os.mount(pyb.Flash(start=0), '/flash')
8 i& b- X/ A u5 Y6 T - os.chdir('/flash')
复制代码 7 I) q7 M9 y8 y8 ?* m
& Y& R9 Q, z& P. f4 B$ k
: K* w' q8 {% E* J4 R
9 z U/ F' y M" @# ?. S* Z) t4 tLittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. / b, p5 b# P! V' u3 Y5 \. g. m+ b
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
2 g* |2 {; J% z7 F4 Q2 X( ~ - import os
3 R+ v& ]1 S6 E9 \1 D+ } - os.umount('/')& k6 o& a) O- f9 n" K
- os.VfsLfs2.mkfs(bdev)2 [2 m& b+ W' H4 q- M( W* w9 {
- os.mount(bdev, '/')6 q: q% |# v$ g' ~8 l
- + ^* o \% S( Y, N6 x3 a- W: C, J
- # STM32) N8 F8 H! Z+ ^
- import os, pyb
! a& b7 J8 p- o" @0 ~3 k - os.umount('/flash')
6 ^, \ y$ Y& E* y9 P - os.VfsLfs2.mkfs(pyb.Flash(start=0))
$ v4 A+ ^% g: V" h - os.mount(pyb.Flash(start=0), '/flash')
& G4 W- `0 o4 {2 @6 u# q* s/ { - os.chdir('/flash')
复制代码 # J+ b& E: H# D! Y, ^1 Q/ a' L
; ~" g/ k" R; g1 Q' e
0 Y6 \4 ]7 M2 W" f# Y# E1 |$ G. }
( Y, r( r! z9 b& I2 L4 m混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb. w+ g$ P- x8 k4 J/ m: W. f2 k* P
- os.umount('/flash')$ L, p7 u b$ G9 N9 b( n
- p1 = pyb.Flash(start=0, len=256*1024)
/ b8 ]# B% f# i+ f* s - p2 = pyb.Flash(start=256*1024)
@& M8 o) q% O, E2 d - os.VfsFat.mkfs(p1)
, o2 U- W7 F7 d/ I( O4 p" j - os.VfsLfs2.mkfs(p2)
. ?. p0 g5 D# p b& v! S - os.mount(p1, '/flash')
! ?3 Z3 n" b- I7 t$ X# N4 Q - os.mount(p2, '/data')' K- D' U2 Z/ W+ ~/ i4 J$ v
- os.chdir('/flash')
复制代码
" _/ C% _! G& H6 o" c2 I& f# c9 K' F/ M. }) }9 l
. l! b5 u5 b1 a3 E' P5 F
这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb. K, _0 ]. L$ F. J: L9 l
- p2 = pyb.Flash(start=256*1024)* v4 C7 H; l4 r# l
- os.mount(p2, '/data')
复制代码
J) d7 n# s" |$ P+ u( ?. H0 k W' o4 z3 @7 E8 }1 }: {
7 t% b0 d' m. V- f" e+ v- X来 boot.py挂载数据分区。 , A Q% a2 ?4 F6 {
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os
4 z9 T, R6 H1 H9 y: G% b7 D - p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
" |) w* f( f, [, M' w - os.mount(p, '/foo')
复制代码 " D% V- @7 g* f* Q% b& n
; V# q; G: i6 R5 d) i
* O! f' y' ]# E4 w! M
% C$ r/ f4 }$ Y3 t
# U! w4 |" g$ |5 i" h i# J; `3 p) }9 E& F
|