使用文件系统 内容 使用文件系统
0 k% b# x1 o: l9 J' Y6 p, x/ w虚拟FS 块设备
, L, D& R; L5 B文件系统
" }1 _: C, C: u9 l; Z& m8 J" O' A1 ]8 }, W" S+ W
: [# R3 S0 V h
& R# V9 j6 ^$ a- y本教程介绍 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 上,主文件系统挂载在 /。
) `! x0 @2 [$ i& h块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
d9 o: I! D8 gESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
4 F% J! e, D+ S y! f9 JESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。
. b( s& }. n" Q. k5 E( W# V9 y4 m% l9 M8 x; S: V& [$ q3 g* x' [6 t! g
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:; ]; g5 _) C% w
- def __init__(self, block_size, num_blocks):1 n' {* J% Z$ ^) o' \
- self.block_size = block_size* e0 W! u ?$ n1 [4 f2 i7 G
- self.data = bytearray(block_size * num_blocks)0 ^- _( n7 o! c/ |/ I1 t
: S( a( K5 T; f2 d: t* k0 \1 _. L- def readblocks(self, block_num, buf):6 @7 ~2 I u" }% D
- for i in range(len(buf)):' o/ t( K8 {: ?2 h) [+ t
- buf[i] = self.data[block_num * self.block_size + i]
! V5 R3 s8 m$ I& z6 q" o - 5 b3 f3 z `4 ?
- def writeblocks(self, block_num, buf):
% {+ ~% Z+ D- w - for i in range(len(buf)):
6 y' f& z& R1 i+ } - self.data[block_num * self.block_size + i] = buf[i]
% G" k2 ^; x; j) _' N, v - 5 h; |) u. t7 f0 V. `1 {
- def ioctl(self, op, arg):% S3 Z7 Z! }! m) x
- if op == 4: # get number of blocks
/ W" f6 O( p2 t: w - return len(self.data) // self.block_size
& C; Z8 U# r. T% H* y# W) L# ^ _2 G - if op == 5: # get block size
# i$ q. J% H+ Z" a# M - return self.block_size
复制代码 & A! s/ c+ i2 H, R
& S1 j0 w; Y& a
" B9 j/ j) e8 x
它可以按如下方式使用: - import os9 Q4 v& B5 G! z2 c9 P
- 6 U5 x' P, b) D) W! W
- bdev = RAMBlockDev(512, 50)/ t4 z" C4 D; ~; x H/ U
- os.VfsFat.mkfs(bdev)+ i9 N+ n4 K9 k; Z" I/ A
- os.mount(bdev, '/ramdisk')
复制代码 2 z/ e/ ?5 ^' q
4 O) l4 y; |3 o. s* U4 @4 }" h% Q, I# K. W. v% v" P! u- Y8 \
支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
( a& z; I2 r x; J: a8 e0 ], t9 t - def __init__(self, block_size, num_blocks):
/ w q7 t/ u+ y1 w, j8 ` - self.block_size = block_size
# _( |( {9 V" a9 w - self.data = bytearray(block_size * num_blocks)$ F- [5 D' ?2 `7 j7 s% ^' G. M
* Q! T1 ?1 W% n! v* I, E3 W. L8 _- def readblocks(self, block_num, buf, offset=0):
6 c8 Y5 |2 h# w2 j# E" y s) [5 X/ n - addr = block_num * self.block_size + offset
. f2 @! z. `2 d - for i in range(len(buf)):
% L% H( v4 @: Z/ d2 V; b- W7 l& Y - buf[i] = self.data[addr + i]
2 p/ r" K- {+ [0 c- m5 R+ Q
9 I6 _4 f( y8 T p' R+ E$ @- def writeblocks(self, block_num, buf, offset=None):
' I! y7 T' @' U/ O @ - if offset is None:4 z9 l; p0 A1 w- {! r0 Y
- # do erase, then write
: ]- k" Z$ c) G6 a3 y - for i in range(len(buf) // self.block_size):
4 u A3 ]0 n9 i* {' }7 l - self.ioctl(6, block_num + i)
8 P0 H3 q8 R% q( L/ u - offset = 0- Q! x0 S$ o* E1 i
- addr = block_num * self.block_size + offset
r$ x8 k& l, v, ]8 _ - for i in range(len(buf)):
3 }6 @+ a8 f, ?* x: v+ ?/ Y( \ - self.data[addr + i] = buf[i]
5 b h- _4 B! p: a' }5 D' D - : ~' m! E8 x& }% \2 F6 P7 O" @
- def ioctl(self, op, arg):% R& r1 ?7 G, d' I \( J
- if op == 4: # block count( u5 X: |5 k) w$ C- V- r" l4 r9 N
- return len(self.data) // self.block_size
& ?3 c0 X& g5 I8 V2 F, G) I - if op == 5: # block size
) s T6 `% R! r; { - return self.block_size4 L% m: _! l: j' }- H; Q k. m
- if op == 6: # block erase! Z5 @ @1 q, N1 x# w& d& r
- return 0
复制代码
7 t* H7 V3 r) ~3 q' P# d
! m, N; d$ W' k/ ^: Z, p8 h. S6 h$ M$ M9 Q F. l2 P/ s. p
由于它支持扩展接口,因此可以用于littlefs: - import os G5 B8 }- M# J5 `0 ~& Y6 O
- 3 X R8 z& p1 I8 f) K$ M$ [- U* Z
- bdev = RAMBlockDev(512, 50); V9 l1 J$ V2 s! s$ k; k# }
- os.VfsLfs2.mkfs(bdev)$ c8 X+ h1 S) l9 P. F9 w
- os.mount(bdev, '/ramdisk')
复制代码 ( I' F( m0 N- `; { w
/ L0 S: O: r9 q/ \% G
) O% |9 d# c0 Q! @' a3 l
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:( W7 U. w: I2 Z+ L6 f
- f.write('Hello world'), e, z. x. X; a! M1 e
- print(open('/ramdisk/hello.txt').read())
复制代码 , m( L! Z! u# k3 Y b
" T2 n7 f; ]' e! D) C0 [* U
6 I {% g a( p1 K P" E6 ?6 U; Y m$ M- |1 A- `5 N) P7 f
* x$ Z% l% F0 R/ Y2 b5 a7 C文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 J* d( a5 H" g- y
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
. M. z$ I; O" w) w5 H1 U8 [" c6 ` - import os
1 r8 l- d! \5 V& ~# c8 g - os.umount('/')
, j, Z0 Y& J) X" E6 P0 b" H0 r - os.VfsFat.mkfs(bdev)
8 V( V8 Q. P# B' Y$ i% ?/ a* R - os.mount(bdev, '/') _0 A. C2 O" s2 v& ?- t, B- ^
* g+ C3 R; A9 W2 h9 g7 q6 y' G- # STM32
/ h4 q9 ~, k9 V2 Y8 P: v- C% i - import os, pyb
. D) z# u7 U; Q( B9 x - os.umount('/flash') F' @* p; s1 c
- os.VfsFat.mkfs(pyb.Flash(start=0))
: |8 b) y% n% ^2 Y; ~% n - os.mount(pyb.Flash(start=0), '/flash')
! r9 s( r# p/ Y$ l3 a' v! p* h - os.chdir('/flash')
复制代码
d1 i( p9 c. N1 f5 q% s$ k% z, I" X- g# F; y9 g$ t% L* ^
/ K9 G8 B, C8 F- ^
: E& C$ A7 l$ p0 z y8 |LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. 2 P3 Y! _2 w. g+ H
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
" c, @+ H2 `2 d6 H: p, }1 C, M) O& n C - import os
2 b: t; z8 m" U8 @ - os.umount('/')
+ X, E; g3 F' L/ j$ ? - os.VfsLfs2.mkfs(bdev). }2 Y: e! \# f1 v& v) F; p
- os.mount(bdev, '/')
7 i( M5 h, |) ~: w% q. q7 d
* c; @0 ^' T3 `8 ^4 a- # STM32
$ o8 v: ]% g% c/ ]' W3 H4 Y( ^ - import os, pyb
- `8 |+ q% ?7 h w! x$ x) w& h/ P - os.umount('/flash')* P) B+ u; M( e1 w3 k0 G: X8 X
- os.VfsLfs2.mkfs(pyb.Flash(start=0))' n4 F% o* E. y* Q- i* F# R
- os.mount(pyb.Flash(start=0), '/flash')) n; f: f$ [5 N/ f6 B% M; D
- os.chdir('/flash')
复制代码 ( x1 `# D/ l8 W9 t) W
! G0 q" ~! u6 F8 J5 I% |- Y
, e8 O; J7 U; I4 c% r9 ?) L
) O- L' B- d. B, Q5 S混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
9 u7 O' K# ^2 R1 M2 V - os.umount('/flash'), c9 d! p7 ~: C6 S
- p1 = pyb.Flash(start=0, len=256*1024)1 U6 F3 e6 \8 u3 r# D: e( S E5 P$ Q
- p2 = pyb.Flash(start=256*1024)
1 \. p; Y2 K' Y# X6 r - os.VfsFat.mkfs(p1): p2 W9 P. M9 f, v- Y& Y. H
- os.VfsLfs2.mkfs(p2)" m: I0 n( z+ _+ Z4 X
- os.mount(p1, '/flash')
1 H- t7 ~) H) P$ |0 l - os.mount(p2, '/data')7 y' M4 {$ d! o0 @0 I% a$ o
- os.chdir('/flash')
复制代码 6 g0 j6 E8 M5 D: h
& j) Y5 r! Z; ^# M. j3 D+ B) @* f' z! m3 U# a
这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb5 z- D1 I) U% }' L5 d5 _
- p2 = pyb.Flash(start=256*1024)" t% S* |9 \: y
- os.mount(p2, '/data')
复制代码 . h! p' J7 m6 P8 U. O
# G2 m/ Z& I4 R2 p2 D
! o0 t, K. d2 r4 J- {/ H来 boot.py挂载数据分区。
, ?. @8 P! R! L2 X% s$ N- Q- A混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os3 k$ Y' C: C! Z/ M" Y
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')) V& m; o+ N- N* i
- os.mount(p, '/foo')
复制代码
1 f! V4 x- {! G, j, h7 _# y" g: J8 {+ r. N1 w
% L! F& Q; `0 n0 H f' X
- W1 C8 ?7 i6 R
* M$ Z: R; X, [( d! y
* k: G7 ]$ V9 s |