使用文件系统 内容 使用文件系统 - X( j4 k8 w2 }* B2 q+ b
虚拟FS 块设备
' n7 i! N6 g; B2 @内置块设备
* h3 a/ E' @+ J7 `- B8 M( g自定义块设备
0 L/ \* r9 ^3 w
文件系统 ' w, W8 {$ m8 R! k
7 |" i( O1 u" }. a/ z: o
& Q, V$ ~) c G9 |+ q# e6 [) o' T
) a% \6 I* {, W1 j2 V本教程介绍 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 上,主文件系统挂载在 /。
7 O1 R2 @0 k& M% X块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
' U! Z l1 }; j7 k5 c; QESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
5 z5 y$ g- y) U2 NESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 0 Q, l- q! B3 R8 o1 W* `" C
* u% j/ m a/ S) M6 F
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:* u! l; J) h- S2 \5 {
- def __init__(self, block_size, num_blocks):3 x. `7 [3 |* |: d) t1 }8 I4 T
- self.block_size = block_size3 Q7 U& Z& J0 B2 H `. }' }/ ~
- self.data = bytearray(block_size * num_blocks)
6 T: j8 ~! b; T( c; r - ( M& H! ~, `- T% A6 o: X
- def readblocks(self, block_num, buf):
6 M% g- X; T9 f0 y* K+ R( g - for i in range(len(buf)):' |2 H- u* B( u, g
- buf[i] = self.data[block_num * self.block_size + i]
" J, f9 M4 d9 t1 o: }
9 v/ r0 k U1 y% m- def writeblocks(self, block_num, buf):3 G- p% n- w1 n, p+ ~ r' s
- for i in range(len(buf)):
, d3 O5 i& f; s( p. i( {; [3 c! ~! V - self.data[block_num * self.block_size + i] = buf[i]
% J4 T: G* {9 p, C6 [% q - 8 @* O/ B6 S2 D0 r' ]- k4 H. I
- def ioctl(self, op, arg):1 V/ n1 W0 E4 F2 @4 ~1 b
- if op == 4: # get number of blocks. s* S- d& p1 G' |8 Z1 C
- return len(self.data) // self.block_size
- N, h7 ~$ z8 F - if op == 5: # get block size* m! H. \. r1 Z4 I
- return self.block_size
复制代码
1 E$ E% d( f+ @; h" A
7 V9 N: Q/ v% ]
) g% k' a0 W+ l; F它可以按如下方式使用: - import os) F- [: k$ p' L8 x
- ! Y1 W }- @5 w2 Y; ^5 w
- bdev = RAMBlockDev(512, 50)
2 X' Q. Z6 ?0 K1 r& i* b" D2 o - os.VfsFat.mkfs(bdev)8 |! w0 E6 A: D# R% X x
- os.mount(bdev, '/ramdisk')
复制代码 6 ?7 b: ~2 D% ~7 [0 k
/ I" }, H t1 {: `( p
% Y+ |6 f$ W# i. @8 {6 I
支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
+ o! b9 ~9 \" p% M/ Y ?" m( | - def __init__(self, block_size, num_blocks):2 Y+ ]1 c1 |3 l. A
- self.block_size = block_size
. h0 f3 h1 h( B, H# o, X - self.data = bytearray(block_size * num_blocks)
1 Z" d7 ^, P+ F' l- z# G. n7 C
5 b) ~% a1 P6 Y: ~; L2 O8 k0 u- def readblocks(self, block_num, buf, offset=0):
) T# ?+ j2 M/ V- J! p - addr = block_num * self.block_size + offset
% @! L" T" e9 l e9 l k - for i in range(len(buf)):6 v: \( y& \4 C: r/ h) s. N
- buf[i] = self.data[addr + i]
+ ~/ j1 O; r$ S+ i S/ C$ a$ x - * p, }! o, B% N% b
- def writeblocks(self, block_num, buf, offset=None):
7 A& R! ~! M6 \# A0 b2 c2 V5 c$ A$ } - if offset is None:
/ J: e( w8 q) Q! U" e - # do erase, then write) r9 L: [( I7 n
- for i in range(len(buf) // self.block_size):$ B. u7 f- v5 L7 E5 f: x
- self.ioctl(6, block_num + i)( C% ?! g# {8 _4 @, Z
- offset = 0
' E( g+ s0 t2 o0 Q6 ~, q% s - addr = block_num * self.block_size + offset
@ B# ^) U1 s - for i in range(len(buf)):' X" S6 m8 G8 q( x6 ?. y: @
- self.data[addr + i] = buf[i]
L0 [! t: G4 d4 j5 t+ t
$ P, `& y6 m$ z# g- def ioctl(self, op, arg):- ]% V m* s8 j3 G0 k
- if op == 4: # block count& W6 ~, V, D" x
- return len(self.data) // self.block_size& Q2 T6 J# K% i% [
- if op == 5: # block size
6 `6 k" e, B; d - return self.block_size! t# U" r Q' V. ?; @* z7 B
- if op == 6: # block erase" d: ?4 d, [0 o5 p! u0 F
- return 0
复制代码 5 N; Y3 W* s, s+ s6 h
0 s c3 K" b5 u) @5 g2 g
% z0 r4 N8 ~" }由于它支持扩展接口,因此可以用于littlefs: - import os w. u; Q. S( I. P3 x& z* M0 ^
/ A- l8 X- t% C6 }- bdev = RAMBlockDev(512, 50)
2 _: A3 @2 v1 T2 T) [) o2 O# j - os.VfsLfs2.mkfs(bdev)! ?9 p3 c& c9 A }+ o$ ?
- os.mount(bdev, '/ramdisk')
复制代码 ; q( b* v. p% @# w, B
9 n9 @3 C! |2 g% `9 M
( n4 d% A, J5 c; x; J; `) i一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:9 @6 `6 B+ `% t4 c9 j8 N
- f.write('Hello world')
- {+ `' T" p" v. W - print(open('/ramdisk/hello.txt').read())
复制代码 & M# t" w. H4 p: K/ p2 x# K C
0 ^$ S, W) E3 H, m5 `
0 x! U4 k: [8 v8 n
" r! n$ I& b x
1 N3 D5 R) Y! M& z2 v3 f; x, p文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 4 x q0 T5 @' l
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32 r; E) q. P! [5 p
- import os" S6 G' y% k& c: ]% q5 G
- os.umount('/'): A0 f( w8 N- h2 R/ F, s
- os.VfsFat.mkfs(bdev)2 B4 g' U2 S0 ~; p) `& y: _& l
- os.mount(bdev, '/')+ V' Z0 {* f' w2 j1 F3 G; {: u
- ) |3 w$ P/ H r; q9 b8 `
- # STM32
3 d" b1 D1 g V& }( ]" {' c - import os, pyb
" m0 C) x1 z9 `& P) `9 { - os.umount('/flash')
& L# i8 T+ C& ]* [ C# r - os.VfsFat.mkfs(pyb.Flash(start=0)); i4 X; l, s9 L( w* O. B4 }; u
- os.mount(pyb.Flash(start=0), '/flash')# x* z% S- C1 @: E. x3 }6 O
- os.chdir('/flash')
复制代码
% m/ Y _/ L0 c5 p0 e1 e2 W; |
5 v1 u3 E* ?4 f5 Q- ~" A
; I/ ^" p& z- x' w/ ]( C" d& Z
% ~$ D- D9 N3 i' F' `LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
4 m, S& b) H* I5 ]* Y3 }注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
8 |8 v; Y1 X; S- j. Q6 _ - import os( W" m8 C H& I. K4 y/ m# b
- os.umount('/')+ {4 l7 r+ Q3 O% |5 Y
- os.VfsLfs2.mkfs(bdev)
z5 ^( I% ?1 p2 V' ~3 x7 R0 d# T# I - os.mount(bdev, '/')* Q9 o7 R6 J! \! }' w
9 K# K. Y" D \) Y& m+ I- # STM32
' c9 R# u: B3 J* K% b" |: L - import os, pyb
, [9 _! K4 {) { - os.umount('/flash')
* w' Q, K7 \/ A* ?3 Z) D - os.VfsLfs2.mkfs(pyb.Flash(start=0))6 Z! T" Q% ?7 I! H7 X$ K1 e& i
- os.mount(pyb.Flash(start=0), '/flash')) v9 f3 X% H: p; f7 H
- os.chdir('/flash')
复制代码 0 v6 V+ A5 g6 m! G
1 ] y2 D" }+ B! A2 H9 T! U! T S
; @4 i' m6 c Y/ a' \9 K( k8 u5 f- l9 ?8 c. n' H
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb/ B0 G8 V( I% u; c$ c7 t
- os.umount('/flash')
4 h& N: k1 ?! t2 b- I) g - p1 = pyb.Flash(start=0, len=256*1024)% _+ N! x0 }8 B% O* G! g
- p2 = pyb.Flash(start=256*1024)' u. u1 D- o v: \% W
- os.VfsFat.mkfs(p1)
. ]2 H1 n/ }% f. L7 x1 Q - os.VfsLfs2.mkfs(p2)
- `6 c0 N* Z( b! O* H8 B I - os.mount(p1, '/flash')
6 o n$ n3 w, O8 { - os.mount(p2, '/data')
/ _1 ~: ^/ I% P; L - os.chdir('/flash')
复制代码 ( h x0 I5 C+ z( o* p y6 v
9 G: z3 a/ t% |0 o
5 x. w! J# G# a# h6 P% U这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
" L/ t. C9 p4 F3 |% X2 Q, c0 X - p2 = pyb.Flash(start=256*1024)
p5 C' D9 @2 ?$ W% n6 E' z3 T - os.mount(p2, '/data')
复制代码 ! L( x( e9 _2 }0 O
+ `9 j2 U* n# O5 x! \5 M
7 E5 ]" t5 E$ c# {; o! l: @2 H来 boot.py挂载数据分区。 0 _% F7 z1 S" |: m3 ^, @. q
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os. Y$ m9 x$ h& `9 {! P0 g
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
2 T9 F! m7 k% P/ E2 C7 B% A& n - os.mount(p, '/foo')
复制代码 ; W$ e* |; S# e. _
0 k: W5 U6 n: a( H7 u' X+ w) M U' I1 C5 f- O$ U
, T, p: b$ p# r+ W+ S# e T( h) ]
& |' F% F u8 g
|