使用文件系统 内容 使用文件系统
; w" Y0 F3 t( C9 H D1 _+ r虚拟FS 块设备
3 T* _5 t& Q6 v0 @0 R0 a内置块设备
. j1 L1 K3 H* |: ?4 z" T+ M自定义块设备
5 B! d3 s9 ?4 o: u3 w* c$ r9 Q
文件系统 / c2 p; K& b" n# Z7 I
+ A, h5 R3 @# X& d
' N. c7 i8 j6 J' x: \+ F
. N, |: f. a. r _9 r本教程介绍 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 上,主文件系统挂载在 /。
# o. q2 A( n) h Z, ^% S% h* M0 v/ K块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
( v( ~0 {4 c3 L8 m4 I) ^ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。 3 u" E7 V& {! t0 [1 L; r- J O2 D1 W
ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 7 n( q( A: _5 f7 K
, f- B5 r: W0 l7 c自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:8 a F% P# \2 _0 F
- def __init__(self, block_size, num_blocks):+ A8 A6 F/ H! u9 S6 X; a* q
- self.block_size = block_size
, M4 ^0 X0 u9 S6 {' D4 y! l - self.data = bytearray(block_size * num_blocks)
8 {+ f" C4 I2 Q# ~) ?4 C1 c2 i& E
7 Y2 [- t* j6 ?3 e" [- def readblocks(self, block_num, buf):
. n% O0 i/ `* I0 }0 Z - for i in range(len(buf)):
: @ P- M* v. } - buf[i] = self.data[block_num * self.block_size + i]& _% J3 F$ Y5 m6 ~ M2 a% G
- $ J( I4 K6 O# ^4 j; F2 R* j0 t
- def writeblocks(self, block_num, buf):
1 U$ v8 S8 j" ? \6 w" q - for i in range(len(buf)):
0 n x: C- W' ~ - self.data[block_num * self.block_size + i] = buf[i]; l, A' \( z) e" U% @
- . g. H0 V$ V# [3 u" ]( s% Z
- def ioctl(self, op, arg):
8 E1 k. F7 k2 V: r* G9 { - if op == 4: # get number of blocks
: B" s# ?5 e7 e - return len(self.data) // self.block_size& ?) ]$ B2 c- {0 A& |; k& V
- if op == 5: # get block size: L% I t1 p9 f& i. b/ R: c
- return self.block_size
复制代码
% G2 S P( ^" J7 s) u, c! @6 a
* _: [$ e, L* \& I1 q A
它可以按如下方式使用: - import os
1 I( ~% Z- ]' x! E7 ^) K7 m
0 `6 W* a7 g1 z& S2 c& A# @- bdev = RAMBlockDev(512, 50)2 u# U3 _1 q, C5 N) u! N
- os.VfsFat.mkfs(bdev)4 l' C; O8 u7 ]+ d( l5 g' m
- os.mount(bdev, '/ramdisk')
复制代码
( e! A# p; r# ]7 m1 S
& T, ? S" ~% H
) f J* }0 e/ b3 v. d5 a8 X1 S支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
$ h7 ?+ V; X, @& j - def __init__(self, block_size, num_blocks):
2 ~& e7 z n1 W% B" j* p+ I1 G - self.block_size = block_size
# W: K7 m) c# q% X1 j" P - self.data = bytearray(block_size * num_blocks); H# ]9 z8 F: z' w
+ s4 j! C1 S5 t4 t6 M- def readblocks(self, block_num, buf, offset=0):- n ^, c( U. S+ ~- a+ s$ F' D
- addr = block_num * self.block_size + offset7 [9 a) N2 ?( _$ _
- for i in range(len(buf)):# C1 I9 R) ~& Y. K& I2 W
- buf[i] = self.data[addr + i], S7 I/ p9 `/ a/ A$ U5 U
9 G7 A$ p0 r2 ?6 x$ Z0 E4 h- def writeblocks(self, block_num, buf, offset=None):
% c5 q; s% w: A) Q7 O& h - if offset is None:: i- b+ m0 A. ]2 \7 K) H
- # do erase, then write
* P! r* K1 W! R2 n0 k - for i in range(len(buf) // self.block_size):$ C: k9 W. `" R; r' }. y- |
- self.ioctl(6, block_num + i)2 z- f: n2 ]& i$ K/ T- P# Z
- offset = 0
2 S2 t$ e" h- |' u - addr = block_num * self.block_size + offset) @9 ]% H" [- d* f
- for i in range(len(buf)):0 ^# D( l& N- Y7 N
- self.data[addr + i] = buf[i]7 W/ G( o' C, ]2 r! h
8 _* d' X2 L6 |" m- def ioctl(self, op, arg):
* p6 c# X; l' E* M - if op == 4: # block count9 f! ?1 T! v9 x9 O" f2 f
- return len(self.data) // self.block_size
4 n) {% v5 {) B - if op == 5: # block size4 L) U C7 Q/ H2 a& d
- return self.block_size
$ n8 c' Q- x( a/ u+ T! D) r - if op == 6: # block erase
( I8 R7 X1 x+ e9 v - return 0
复制代码 / i+ K6 q, _9 C9 X7 S: }
' b) c3 `/ w! ~5 R# S6 D; ]
# z) D% i5 O; k% w) f$ x1 i由于它支持扩展接口,因此可以用于littlefs: - import os, v( P* F( W; A9 ]/ f( t' n
- ! W& h! P4 V) N9 k$ O0 w0 K
- bdev = RAMBlockDev(512, 50)
: m" Y9 L) j/ i2 K- W0 I - os.VfsLfs2.mkfs(bdev)+ E* v; t7 d% l" N2 U6 f. F! g1 ?7 [( J
- os.mount(bdev, '/ramdisk')
复制代码
4 {: N& A0 y* e* t7 p$ T( N5 ?- n( U N1 e* I% g3 F- j
" g6 Z# `: t: q) \6 {+ H" S, m4 A一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:# v, d8 e% I- f; U- r6 W+ A: P
- f.write('Hello world')- m8 K$ i6 B9 t( _9 g9 _: G
- print(open('/ramdisk/hello.txt').read())
复制代码
7 f: c' R; ]5 i: G/ r; {1 G2 d
, `! [- D" C3 i/ f1 [( d! p# f
8 x3 L' W) o& i8 [; D J2 B
* o' a e7 Y8 H/ I( [) @5 X) i" j. ?5 ^# s
文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
' P7 P1 r& \5 \& ]0 w5 y2 {* YFATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
5 I9 k0 F9 I+ z% [2 x - import os; v3 V" p5 E& [% I6 D3 c
- os.umount('/')
+ ~ \+ S$ u' N - os.VfsFat.mkfs(bdev): h9 f6 i0 J3 s" ~# N' F/ A
- os.mount(bdev, '/')
z( {4 R2 c) J
5 O; }5 A A# S. A- # STM321 G% K+ D$ t9 J, l( g4 I. D
- import os, pyb
5 ^+ C# U6 q3 g$ i2 r* s& ? - os.umount('/flash')( t. D( n/ D1 S! L' J: p
- os.VfsFat.mkfs(pyb.Flash(start=0))
3 x6 N5 ~5 J9 W1 T# e1 d; W$ ^ - os.mount(pyb.Flash(start=0), '/flash')& G- f, _' t. X* o7 _, Y
- os.chdir('/flash')
复制代码 $ J' ?) [$ C, u7 v) K4 n6 j
" ?1 J" P' u# p' V2 |! f S
! Y7 E1 L5 D, r3 |& r! @
M6 H0 F4 X* @+ \LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
7 N; K" }2 }+ Z注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
! U; {4 Q" N6 _: ?/ t3 D4 w - import os
: w7 @! U! }3 ~' s- ` - os.umount('/')8 V L1 t: V) g. ]
- os.VfsLfs2.mkfs(bdev)
; q j6 ~, p% _# \; a - os.mount(bdev, '/')6 f$ Y& U4 i1 s6 o
1 P7 A. S T" Z2 g- # STM32" J) X: G, k' Y- N5 g7 x
- import os, pyb* O' B' v) |7 y3 r! C' }
- os.umount('/flash')
# k6 t# v1 G& J. Y2 ]5 S - os.VfsLfs2.mkfs(pyb.Flash(start=0))
) `' n+ {3 I; M" q3 @ - os.mount(pyb.Flash(start=0), '/flash'), ^/ s( z V! N
- os.chdir('/flash')
复制代码
# N. ^/ Z- p; c! i& F9 S( i& G& z' r( x( V4 G. k8 E
8 \6 [, u c: U g! w3 p. @4 U2 b$ w" ^4 r& u8 \# J
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
( e m* t# I+ ^4 r2 p4 ^ - os.umount('/flash')
6 b* ?" q/ U8 x9 n - p1 = pyb.Flash(start=0, len=256*1024)
# Q4 z( W, S( C% ] - p2 = pyb.Flash(start=256*1024)/ i$ ]8 m/ C2 k
- os.VfsFat.mkfs(p1)! o) ~; T" n" z; v3 p+ L0 A% c
- os.VfsLfs2.mkfs(p2)
# X5 D' R% F! R$ L: }. `# j; \ - os.mount(p1, '/flash')
1 H, o6 o% C: n9 T6 s - os.mount(p2, '/data')
# t5 ^( k! W; ~9 R$ u - os.chdir('/flash')
复制代码
- g/ x; v; O8 q4 ` E8 s9 d" [& j) {+ k8 ~0 V, G! l' g0 B
, K6 d0 j6 p, o$ V( ]* G* Y; x
这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb$ E Q4 X) r, _( W7 n, [7 ~5 f
- p2 = pyb.Flash(start=256*1024)& o9 T5 R6 A. w1 _3 I! n3 \$ O
- os.mount(p2, '/data')
复制代码
/ U% M% |, @) e1 X! G) S& Z- U8 ~' q" h& y1 q8 C4 \
# R9 K$ D: n. C( c- }. L
来 boot.py挂载数据分区。
u) ~' U4 B5 @, G: A( E7 Q混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os
7 ]4 @6 L$ f& X: s1 f# N5 p - p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')! n: s8 @4 ?2 X; `
- os.mount(p, '/foo')
复制代码 3 ?6 ?* M% d% p5 e
' {( h) f' p5 N b4 i4 K0 l0 v$ l2 D. ~ ~; R: w9 W
/ b2 q/ e0 ]9 w$ {$ `+ \4 D
" w, r- L3 y, H) d! g$ a. `/ J& Z( @: s* e
|