使用文件系统 内容 使用文件系统
1 R/ ~, _% p( X2 X. t# @虚拟FS 块设备
2 X) o( |8 W2 S: @2 H7 I文件系统
8 T$ z3 {3 L7 u1 T7 |- Q- |; I4 l1 a L2 l# P9 L5 p4 c
. g5 o7 [ h: M2 t! B. ~4 x1 W ! g% Z9 g8 v, l+ B: E
本教程介绍 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 u6 f" j! S, Z" e/ N: b) d
块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
. V2 F i- _2 N# {& V. GESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
% R7 _0 o2 ]: m; @ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。
Z/ x2 j! k7 y* I- v, `3 K* Y1 \% K$ v+ I
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
( N% z- E. r ^! v - def __init__(self, block_size, num_blocks):
. A) G7 w9 W- O6 C, j# E$ R - self.block_size = block_size
7 n' ~+ z' r3 k - self.data = bytearray(block_size * num_blocks)
: D) v% o* `* f/ \
7 L' b6 }7 T; N, r2 ?- def readblocks(self, block_num, buf):5 d8 k( w8 V2 f8 t2 ]3 g6 C0 h- ^! ?
- for i in range(len(buf)):
( i, ^# T+ X" t2 K" h - buf[i] = self.data[block_num * self.block_size + i]' `! D. V! |/ M& u
2 Q/ @6 u, A9 U( s: ^7 j$ _' m- def writeblocks(self, block_num, buf):' m% v! }, D, J0 H: |
- for i in range(len(buf)):
9 B! S$ Q: o6 Q" E+ n Z" k - self.data[block_num * self.block_size + i] = buf[i]
1 Z: R' q9 R, @ - % x$ e `$ X. T+ Y7 O$ z4 ^8 u
- def ioctl(self, op, arg):
8 q5 ^7 V0 c9 ]' d1 P - if op == 4: # get number of blocks$ M1 e9 ?# Q/ K+ c
- return len(self.data) // self.block_size
1 D: W+ P2 |$ ` v& U: G5 [4 _ - if op == 5: # get block size& m* }2 }8 n: X/ B) q, ~* o
- return self.block_size
复制代码
8 {- i" R+ w' Q- P/ U e
) { W0 I M* D3 h$ e9 E1 J5 ]. O% s
! ?& P z. Q8 i, q: \3 F它可以按如下方式使用: - import os
5 g4 N% u, ^2 S/ b( a$ X
$ d* j* R' {* Z7 d# f- bdev = RAMBlockDev(512, 50); b7 ~$ ]' [7 V) \2 ?
- os.VfsFat.mkfs(bdev)
! ~3 g2 x9 d9 n - os.mount(bdev, '/ramdisk')
复制代码
" }* F9 G5 P* z) q4 R# T3 R [# Y2 Q; w/ r% ?8 X& M& J5 x
6 \- {+ \2 n7 L. _支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:! \4 C% i8 j5 t, I; s; J' X
- def __init__(self, block_size, num_blocks):
! |/ J9 k2 m7 H$ v - self.block_size = block_size1 D8 d" L* Z- Y, _7 R
- self.data = bytearray(block_size * num_blocks)' p' k+ Z. ?% M2 z( |6 {' p- y# m. b
- - r- V3 M" X: l# f
- def readblocks(self, block_num, buf, offset=0):$ H9 E9 z6 r( r9 C W. [. S- O) b1 e( \
- addr = block_num * self.block_size + offset, X4 K# Y. b9 f+ \6 {
- for i in range(len(buf)):
- t. t- b8 S0 @8 B( ~ - buf[i] = self.data[addr + i]
) O4 D8 N0 m0 t" A3 P5 @ k- u$ U7 B - 7 q$ Z& Y7 a3 U3 _
- def writeblocks(self, block_num, buf, offset=None):
5 \1 X, @/ T, }% ~, ?; ]6 I7 \ - if offset is None:5 Y: ~; e" A# {# ?5 I2 D- ]9 w4 Q: J
- # do erase, then write+ U: ^4 t# e6 I, o' B2 g& o7 h
- for i in range(len(buf) // self.block_size):1 w7 J: i2 {5 W; g* w% k
- self.ioctl(6, block_num + i)
% y" o3 x& `- e h( c - offset = 09 ?( ^8 @& l$ ~3 y% k2 D( c3 K" J
- addr = block_num * self.block_size + offset3 e$ s* |/ }3 X' |- ^4 S
- for i in range(len(buf)):
9 i2 l y: C, d8 l: F - self.data[addr + i] = buf[i]
/ W- Y8 B3 e3 N0 \' Q$ [ - 1 \$ a7 l. ^# I- V) T" }8 i
- def ioctl(self, op, arg):
`0 N, H' H2 G" z - if op == 4: # block count
0 z- ?9 z9 U6 h% n6 \ - return len(self.data) // self.block_size3 \" m. f7 F; y) F( R4 ^3 `7 s
- if op == 5: # block size; O9 i; v( G" @" }
- return self.block_size7 z, w6 }: d4 q+ r! ?: q& i
- if op == 6: # block erase
* w5 u* t4 [5 P4 X: h+ P - return 0
复制代码
' D q( ^0 P* D% _/ Q* v; a4 k) G& A4 M: d% g2 }/ ]% A* \/ K! Z6 Z
/ f! X# L" V1 W
由于它支持扩展接口,因此可以用于littlefs: - import os5 Z. l# i8 ^% Z& T- e
v/ H% J# R: P& p- bdev = RAMBlockDev(512, 50)9 q+ j4 H5 c4 J+ n
- os.VfsLfs2.mkfs(bdev)
# U9 @! r, h9 v - os.mount(bdev, '/ramdisk')
复制代码
. Q/ Z/ B2 k8 R' Q: K/ f4 A+ j9 t/ I7 S3 {
/ c5 P1 y8 J/ N8 \& H' B6 ]# ^一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:/ y4 f$ C0 v% e1 {
- f.write('Hello world')
' k Z Z* o$ n3 j, x6 T+ c! k - print(open('/ramdisk/hello.txt').read())
复制代码
! l, R( t# W! ]/ d) p# \. J% e
; t% N; m! l3 g; G3 F: v6 m9 \- j; J
" a8 o# I8 h" M2 L) v9 _$ \7 `: D q6 C. U
文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
; \% V: l/ x5 }FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP321 F7 [ |4 F b+ Q: ?" ` w h3 c
- import os( b4 D* h2 ]- [# [9 _# z5 Y4 G
- os.umount('/')
& j: S* ?3 f% {6 P2 q" I - os.VfsFat.mkfs(bdev)/ f8 w' I# t$ k5 |. Z0 B2 Z
- os.mount(bdev, '/')' J3 `- r/ Q- }+ U
- : L! n7 t1 {' r' C" M: Z; M; l6 g
- # STM32
6 D' g8 P6 S _6 g! W - import os, pyb& I M5 [3 L* [9 I, q0 z" x* F$ D
- os.umount('/flash')6 o& h5 _+ y* [/ w# y1 F0 m
- os.VfsFat.mkfs(pyb.Flash(start=0))
5 j" [9 g+ J& y h7 y - os.mount(pyb.Flash(start=0), '/flash')
! X0 ?& Z' Y; v" m/ r" ? - os.chdir('/flash')
复制代码 " Y0 G. H; V; g; o1 ?
8 W8 x- C# G" N9 T8 a' U' c# o, f$ ^& }% s+ s8 y
+ g; f* H9 [7 q( o8 Z
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. % V+ X- C' v' l
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
% Z8 X" q; x! K' D4 k5 z4 } - import os
8 U6 C1 e. p( J: f; ]2 K - os.umount('/')
& m9 @+ E5 J; A. h! }% E - os.VfsLfs2.mkfs(bdev)
6 t$ U6 I: y5 Z7 l/ r - os.mount(bdev, '/')
U8 A( [% a4 r, Q& u - 1 d! X& L; ?% X) E) V
- # STM32" O) n4 b5 g6 M, F
- import os, pyb% G% A+ b" |0 s7 j* o; s
- os.umount('/flash')6 S8 z' u% \# F1 E
- os.VfsLfs2.mkfs(pyb.Flash(start=0))5 A, F$ n# R2 P1 k8 i
- os.mount(pyb.Flash(start=0), '/flash')
V P' b& J6 n' b! g2 _ - os.chdir('/flash')
复制代码
1 ^7 [+ d6 f1 Q& H7 S4 t0 r; {" ^( `2 ?5 z0 |! D W$ D# k. S
" K& m. x4 k* |3 [
. I+ b. w* C' M p
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
2 i- Q' M( P# _' \( U - os.umount('/flash')
0 `1 F+ s4 Q; t8 [8 K - p1 = pyb.Flash(start=0, len=256*1024)0 X* W& n/ _. m! n& a
- p2 = pyb.Flash(start=256*1024)2 z9 t2 j* u/ m* b4 t& j) c. s. u
- os.VfsFat.mkfs(p1)2 U) b" q& q' J% o
- os.VfsLfs2.mkfs(p2)
* [% s3 @! }5 \* g7 k& _- q - os.mount(p1, '/flash')
* z0 I5 m6 e" s( ~( W - os.mount(p2, '/data'): G6 H: ]+ Y) r, u: a- v; H
- os.chdir('/flash')
复制代码 7 \9 l% X- R R' h5 q
+ \7 E) b& Q$ |: h! s2 \# h4 |& e$ |; x
这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
9 J. v1 ~9 E+ E4 B/ h1 J - p2 = pyb.Flash(start=256*1024)' P# m, `# ?! x0 {
- os.mount(p2, '/data')
复制代码 9 P- _* f2 J+ x
9 s, f5 C9 R* U* r0 \
& g# g5 h' |0 J' `! ~, C
来 boot.py挂载数据分区。
! r# c# g7 S6 H& _混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os
2 H- `% S$ |& K( {' V2 [ - p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')6 k: U0 V8 g+ ~2 y S
- os.mount(p, '/foo')
复制代码
4 N" B" w" j; [: x. z. x* L& q) `$ M/ @
4 O. B3 k/ Z6 i5 \9 H. l
- T, W4 |9 a; I& _4 W
/ {$ w$ k1 ~+ v' q
! k) V8 ?: ?* [- F0 H; _ |