使用文件系统 内容 使用文件系统 * A/ ]& r. G- n: F9 s
虚拟FS 块设备 ( ]+ N1 R3 b0 R
文件系统
5 R) r) @, }2 c x+ H8 ] j$ I/ a( e/ C+ ?' _ D
1 f7 ]+ Y: k2 ?2 r+ @& L
3 `9 S! X& G- N. N8 T G. I本教程介绍 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 上,主文件系统挂载在 /。
6 b( h& l% j* X2 y! D- X6 L块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
, y* }! K6 [$ Q, S0 C; o& UESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
) b, s6 m: F2 w9 `( zESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 # N! i& ~! ]$ t" K7 d
{! W$ l3 F G! T ?
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:7 q4 F) ? U }, W. n
- def __init__(self, block_size, num_blocks):
. x. |9 c. _& ?& G q# h - self.block_size = block_size% b8 R) E* H# X
- self.data = bytearray(block_size * num_blocks)+ t) S- j% m8 d4 k
2 w \. k7 G2 x% ^9 \: }- def readblocks(self, block_num, buf):' ?' }3 k/ I$ D. p$ ?6 s5 }
- for i in range(len(buf)):
2 T7 r* Y) a+ ^1 C4 f - buf[i] = self.data[block_num * self.block_size + i]
/ S9 E( g; @/ d- t; }
; U, _2 q( B1 w4 `+ h+ g- def writeblocks(self, block_num, buf):
' h. }0 i( C9 T1 \ - for i in range(len(buf)):; U- d! a+ H5 J7 X1 a. W
- self.data[block_num * self.block_size + i] = buf[i]
~3 H. H) j, u0 g - / |; k0 s" B( a4 c" f
- def ioctl(self, op, arg):# u0 U( n% [4 u3 R/ L# ~9 k u. I
- if op == 4: # get number of blocks
" g1 @" g0 c T; T - return len(self.data) // self.block_size* ^* o5 x( F( l" ^# k' q
- if op == 5: # get block size2 d% W: f. N3 T5 }3 H# L
- return self.block_size
复制代码
. p# d# s0 X' i8 b& q8 [* \: N7 u" D. B' \7 r5 z
; `: N8 r. D5 a2 Q0 k它可以按如下方式使用: - import os" q6 W2 `/ r' {! p4 c. @5 ?
- " T) S1 D6 ]3 V
- bdev = RAMBlockDev(512, 50)
! M2 E5 R4 n; V: k - os.VfsFat.mkfs(bdev)
; t4 h8 Z4 ~7 a& E* O - os.mount(bdev, '/ramdisk')
复制代码
5 y3 ~1 q. M9 B) D" ]- l( r
6 Y2 q9 ~% V: P V8 t, P4 N
" i) y- e+ U" {# e, Z2 V; L支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:: y8 ^* k8 p$ a$ |9 \5 Z0 e& e6 Y
- def __init__(self, block_size, num_blocks):
H1 B9 _4 k: J - self.block_size = block_size
7 J' v* f/ U- _7 ~. h - self.data = bytearray(block_size * num_blocks)( b5 Q0 S/ m2 @ w
# j! n Y' U) K- def readblocks(self, block_num, buf, offset=0):5 u% q: }2 t; y) f
- addr = block_num * self.block_size + offset
" O! f. c; O- g7 C3 k - for i in range(len(buf)):& z) L9 }7 }8 |
- buf[i] = self.data[addr + i]! [2 {2 y+ o8 i# s8 A
& U! b y: p" Z6 ?- def writeblocks(self, block_num, buf, offset=None):
2 b S$ S7 s a/ p$ d& [4 ]# g - if offset is None:" b6 H+ o1 j- A
- # do erase, then write
& _1 j9 L$ c9 g0 U# c - for i in range(len(buf) // self.block_size):
8 |0 g% j7 R4 X# l1 ^) O - self.ioctl(6, block_num + i)5 X" |' y3 K1 Z
- offset = 0
6 N5 b5 q5 N+ b0 O - addr = block_num * self.block_size + offset5 }' y( h( D4 @8 Y3 x- I; }
- for i in range(len(buf)):. \7 D, w1 k; s% L3 ~! a" u+ n
- self.data[addr + i] = buf[i]2 O, ]2 G9 Z6 Y2 |4 m7 z; o0 q
- & T, T" ~# b) G+ j! a
- def ioctl(self, op, arg):2 k9 a1 n$ H7 r/ k7 u+ F* Q
- if op == 4: # block count
2 o" c* B2 a3 c) B% \& A - return len(self.data) // self.block_size
, @/ Y: |) x( g. y. i - if op == 5: # block size
( o. n" C# F1 y9 l9 w9 N3 [6 y9 @ - return self.block_size( T, Z$ S% z- j5 u5 l
- if op == 6: # block erase
# \' f* [/ V: a0 b8 T" K, n - return 0
复制代码 ( w: @. k& T/ ]" \+ t
$ i9 }5 s4 J% }$ z, |
' D. X. ~! j+ ?" w3 I! ?, [4 B/ I
由于它支持扩展接口,因此可以用于littlefs: - import os5 @% {& O6 E. d. n6 P& i# ?
- & _* Y( n, ^0 ~! f! v- Q! x7 H
- bdev = RAMBlockDev(512, 50)
" s- W7 A x. r - os.VfsLfs2.mkfs(bdev)' L* ?4 ]3 ~, n6 U
- os.mount(bdev, '/ramdisk')
复制代码 * O, ]! n9 v2 V/ m
$ q8 O* `) l0 J, c2 Q8 L- e, ?8 @8 j+ ~4 I7 K5 u, [
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:' z* c9 e$ o" I* v; y! P
- f.write('Hello world')( B! r! S9 R, B# i- p$ [
- print(open('/ramdisk/hello.txt').read())
复制代码 * i1 _. x9 w; v! H9 }! i# @
0 y' G/ b. U* Y! [" y' W, Z/ z
& Y8 m! g; }5 E9 d ]# d
7 J6 H1 N$ [# T3 Q: H7 {
! X! s5 U( R2 [* s" }) e0 T文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 6 g% P3 G/ o3 X8 D
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
2 b# u# B! _# { - import os/ i8 w# b$ Z( E0 Q6 l6 `7 J# @
- os.umount('/')7 [ B& n( G0 e" d" |1 h7 I
- os.VfsFat.mkfs(bdev)
& k. u" g" A% p! C$ c k: F6 ~6 } - os.mount(bdev, '/')
9 R( e' B# v4 ]' b' n. F P - - c" j1 t5 J/ }& l( q4 I* g
- # STM324 e$ p( S3 X1 m# p8 U% x( [
- import os, pyb
. p0 _' {. o& _1 r- I& t' _) X. f - os.umount('/flash'), O) N9 ^7 i, A9 R! z6 c8 y# N
- os.VfsFat.mkfs(pyb.Flash(start=0))
9 z3 [2 o7 {) u: @& Z& X - os.mount(pyb.Flash(start=0), '/flash')
% Z( y3 _7 m. U a2 A0 V - os.chdir('/flash')
复制代码 ' B3 s, x) L3 H( n3 ] U7 \+ ]$ [
, P" B: A" N) J2 w% F
' a% |! B! [5 j+ q1 t8 h) X/ l$ P& p/ S
7 k$ }* U2 l+ b$ F4 `3 X: I
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. 4 g# v! V9 c9 R( S9 h, S% X
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP321 g0 k8 d/ z3 C* `; y3 t3 M) |
- import os6 L; P/ a) \6 n9 Y j
- os.umount('/')
( I; I# Z* H/ o& M- q' j$ E7 ^ - os.VfsLfs2.mkfs(bdev)- g$ Z, `- J3 D1 N
- os.mount(bdev, '/') Q# i/ q3 O6 T3 j: e
6 {( @: \. Q% f3 @$ v7 r# x5 e- # STM32
( C5 h0 ?/ A5 {9 A* b$ [; ?! h - import os, pyb( V! {9 Y' \" p
- os.umount('/flash')( o- r* F" i% J7 G' }
- os.VfsLfs2.mkfs(pyb.Flash(start=0))
3 m& r5 c# _% W - os.mount(pyb.Flash(start=0), '/flash')
) }3 m4 }% o9 m. G3 ?4 B% \ - os.chdir('/flash')
复制代码 ) m" j$ c# u) N6 R# ]- z
" P% f: C+ O! e
& j/ _9 s. v, _! y1 s, a* I: _4 G# s- D+ }! M# R, I! r6 k B
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
7 e, T# X) E. W* K6 ~/ K5 n - os.umount('/flash')+ U5 R, S. h' x' n. M
- p1 = pyb.Flash(start=0, len=256*1024), J- l1 D0 T& p4 y& e
- p2 = pyb.Flash(start=256*1024)
% j: p- G a( V) g/ o( | - os.VfsFat.mkfs(p1)/ g/ d7 W% K d$ P* F' ?, f3 r" \, ]
- os.VfsLfs2.mkfs(p2). E9 L9 m4 O0 H" V5 D
- os.mount(p1, '/flash')1 H, G) {, l' y# g
- os.mount(p2, '/data')' { i9 k* C! s% f- v# U! K0 z
- os.chdir('/flash')
复制代码 2 g6 f# Z' M) w( T! @0 Z
* Q% D& w1 H9 l; o5 r
& Y. `6 V3 h3 E3 e! c这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb0 p1 w5 J! C# C/ P! X
- p2 = pyb.Flash(start=256*1024)2 Q5 Z! p7 [( C: U
- os.mount(p2, '/data')
复制代码
$ b. N/ T5 c+ q; k$ P( X1 i* y
% w0 d' j: \/ G- l9 V9 _) L8 m3 T0 }9 J
来 boot.py挂载数据分区。 ; y2 l7 w) f! {7 u7 z: g: N4 a$ V
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os0 }3 D2 S$ e" m' {
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')0 r o5 s& T* L6 S1 O" N* W
- os.mount(p, '/foo')
复制代码 5 K$ a: Z( Q+ z6 X9 }
0 \6 m% ~/ b0 B1 N1 Q6 _/ E: ?: }
) D9 _4 K- W/ E
0 T s, Z3 O& R
0 J7 ~! ]) D; b! @# `3 ?6 `3 T" I/ J' A4 r0 S! Z y. M
|