使用文件系统 内容 使用文件系统 G/ A7 z! R9 [" M3 y) T1 H
虚拟FS 块设备 . ?* n1 B% n/ a3 M
内置块设备
* D7 T+ J+ t4 h) d- d自定义块设备
# V$ I' {* Q. p; s4 F; f. ~
文件系统
1 Z# @. F4 ~# l# X A% ~# P9 { V" A7 S* [1 Q
2 F! [: k$ U U2 p& {5 u, y
8 a& F4 n- r' x% O2 m5 D本教程介绍 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 上,主文件系统挂载在 /。 % j4 {: a+ a# h- u: N5 p. ]4 N
块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 7 V" _% D7 k, ]9 Y
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。 * W R$ \9 T G0 p0 [
ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。
" v5 E; _0 C8 M [3 K2 N0 O
7 |- O4 `9 g: u2 e自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
% C- X, L% W% n0 G - def __init__(self, block_size, num_blocks):
' e- G/ C' H' R! V) s - self.block_size = block_size, y$ H. X) z6 U8 v1 L% z( h
- self.data = bytearray(block_size * num_blocks)
! C# B5 d. q5 e7 I
7 \1 N0 I' l$ u) F! @* d- def readblocks(self, block_num, buf): L5 E7 `3 g5 c, g! Q) q
- for i in range(len(buf)):
& M7 J8 k/ w$ y: r" E - buf[i] = self.data[block_num * self.block_size + i]5 X9 B7 a3 _* b, \2 O l
- * O" Q$ ~, p! _, |0 X4 n
- def writeblocks(self, block_num, buf):
% i8 P, r1 P4 b$ e0 D/ N - for i in range(len(buf)):
! J5 [2 |) {2 c' e8 d - self.data[block_num * self.block_size + i] = buf[i]
6 G2 Y1 c( J4 P/ S2 |: r# t, e - . `) J! z9 L! t( T+ c
- def ioctl(self, op, arg):1 ]2 x: p8 }1 N) W
- if op == 4: # get number of blocks
8 D$ O2 T( L+ X' x0 }- O$ K u, Q - return len(self.data) // self.block_size
1 @0 R: [5 z0 x7 X2 k - if op == 5: # get block size
2 ~) o/ Y8 ^* d5 R3 i0 Z3 g4 Q - return self.block_size
复制代码 x5 V% Y) R. H( h/ j
/ R4 q @( c7 n' x! S' L+ C
$ ` z" u* l" Q B: A4 F) h它可以按如下方式使用: - import os
) s; t ]- g8 S$ j8 c - 6 X6 u' V1 z9 V- ^( n
- bdev = RAMBlockDev(512, 50)
* w z, ], X- M+ K - os.VfsFat.mkfs(bdev)- F$ o- F3 R( }7 t& W0 ?. Q8 Z
- os.mount(bdev, '/ramdisk')
复制代码
! H7 ^" K4 d! x( f8 W
, `) V5 i& R/ ~! X+ |
) f7 U, A# V% Z8 @9 W# n支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
% _& C# C& p; k* G! v - def __init__(self, block_size, num_blocks):) Q: p% @+ g1 x; o
- self.block_size = block_size# W' c( I- A5 M
- self.data = bytearray(block_size * num_blocks)1 T4 [' D4 F- T4 g
: U" ?5 G0 M) j1 |9 Q% X0 G3 H% J- def readblocks(self, block_num, buf, offset=0):) O* F6 o7 y S0 Z/ w
- addr = block_num * self.block_size + offset- u" ?8 j' a: q( @* `
- for i in range(len(buf)):
1 ~ U" l4 N! R. R - buf[i] = self.data[addr + i]( G/ Y- G! t5 R/ q
) J" V7 `3 }! \ \0 R5 t- def writeblocks(self, block_num, buf, offset=None):
$ t; `: d+ F1 {/ Q- j( n+ ? - if offset is None:
! T. m6 i! r" ?" V) F9 ?9 U. g - # do erase, then write& p, D& U$ G! X
- for i in range(len(buf) // self.block_size):! C& q; S( _4 e) c2 v$ C: _
- self.ioctl(6, block_num + i)7 n3 \) u. ~ o1 A
- offset = 0& p3 U# c) s5 t% B. G/ O% K2 P
- addr = block_num * self.block_size + offset
7 d8 E0 {; y5 B+ a. _) e3 v" U9 G - for i in range(len(buf)):( ?3 p- J" s/ ]/ A, }! D/ l
- self.data[addr + i] = buf[i]# C; {& h; r4 U
8 x4 P; j! a! t+ O- def ioctl(self, op, arg):' v8 s9 Y8 @1 y6 Y. `# f
- if op == 4: # block count6 P) Y8 T5 Z7 W$ s
- return len(self.data) // self.block_size
' V: G0 l+ v x3 U- m# ^ - if op == 5: # block size
$ f1 P* k8 M" `3 t0 F. N6 _ - return self.block_size
9 x& G0 l; L B) n, v0 Y& f3 @0 A& t: x - if op == 6: # block erase
]5 W0 X- i, ]( t( o - return 0
复制代码 . g$ k9 u. d' ?2 H8 e1 |
( x) ~0 `* J, a; @) E
+ t/ L" m5 D7 W2 n# U由于它支持扩展接口,因此可以用于littlefs: - import os
4 O& K/ Q# i4 g1 h; L$ D - : O; W% A' u# P( j4 h* Y: \
- bdev = RAMBlockDev(512, 50)
2 v5 K0 |5 D% L' @7 p$ S - os.VfsLfs2.mkfs(bdev), l4 X2 c% M! H3 c5 @
- os.mount(bdev, '/ramdisk')
复制代码
9 `8 K! s" T7 W4 h" W3 P
5 h+ M* e( G' z. ]4 ^7 h/ u
/ S8 v; s, ^ ^: W一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:* z4 D3 H& _( I4 o2 ]$ C
- f.write('Hello world')5 @4 s7 `: C! j/ D; ~& [
- print(open('/ramdisk/hello.txt').read())
复制代码 ! J1 T( o- [2 F) t/ S5 J3 s; P. b
1 G' N! M* |: n9 u5 Y2 N2 i3 N& }, T
( M3 P5 F& [) ?
- h- H7 m) v& O& a+ w j7 F文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 + k4 }& |7 n7 u8 K( y
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32& P' D( o6 W l' y+ t F
- import os
o# \: M' H e% m8 y% X - os.umount('/')" B' @7 L. @) e! C* {
- os.VfsFat.mkfs(bdev); B- E) y" Q7 `2 g6 C
- os.mount(bdev, '/')
" W& _. i! e; a4 r; y
/ x1 \/ u4 _* D# C5 |- # STM32) L1 c6 w9 l0 L& a, X& j" [. `$ E
- import os, pyb
o6 Z# w1 g( [, J; y4 j - os.umount('/flash'): [9 K" F/ f' A& M9 ~/ k, Y
- os.VfsFat.mkfs(pyb.Flash(start=0))
. S- H% h1 C, i' W; F5 m - os.mount(pyb.Flash(start=0), '/flash')
& M" J( ]8 ?7 S ~& u - os.chdir('/flash')
复制代码
8 S" [( Z4 m/ _
& x+ O) W/ n* ?; p" n& h* B9 E5 W1 V1 w
4 B9 {0 M( M1 R3 R9 f% ]" p6 I/ iLittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
: p7 m5 I# G- u7 p7 M$ [5 s* ~" q" ~注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
+ s4 |9 G0 n- p0 z - import os
& e) q S" Z6 i7 c. a$ d - os.umount('/')
3 [2 P- T/ t5 ~% _: J& A - os.VfsLfs2.mkfs(bdev)- P" c5 ~$ v. l9 v4 e6 [& i6 n, n
- os.mount(bdev, '/')' k! `/ v9 i! T: O' ?: k6 C
6 q2 X* u; l- r3 O: p- # STM32
( k4 n0 D! s( |" g% Y - import os, pyb
& o9 f8 x- B! p9 `. H - os.umount('/flash')" {4 t$ O* O( ]7 E' m* ]0 C& u% t" j
- os.VfsLfs2.mkfs(pyb.Flash(start=0)): }3 M5 p! T2 A+ _# T2 |5 Z Y
- os.mount(pyb.Flash(start=0), '/flash')
) i! l5 U- {( z- e5 H a3 C( g! K - os.chdir('/flash')
复制代码 + v* t; e# f* p, ~) E* G, l
( X) ^& f; ~" D' Y6 [$ R; o5 v0 E" [0 \# R: N( u/ P* G; a- Z
/ O% I @0 M& |. {; w; i混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
% ` \. [( T9 e/ W( x2 N& [; L - os.umount('/flash')2 Z V2 g$ P! e8 }+ Z9 B* a
- p1 = pyb.Flash(start=0, len=256*1024)
4 u. k5 Z+ ^. s1 Z; h6 y; Z) y - p2 = pyb.Flash(start=256*1024)
' Y) s1 |+ U$ J0 n4 o - os.VfsFat.mkfs(p1)1 q9 Y% C, E. h- |. G5 P
- os.VfsLfs2.mkfs(p2)
! N5 D I6 F- Q/ U - os.mount(p1, '/flash'); Z/ h4 C3 O5 I- ~1 L0 k
- os.mount(p2, '/data')8 r: r+ E& ^" j) M j: Z
- os.chdir('/flash')
复制代码 1 f$ y0 M) F9 R
2 V# a4 @/ k+ ]6 B- k
8 c+ M9 x& M: i: ^) w8 ?9 g+ Z这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb% h2 T; E! [3 {1 ^; H5 l
- p2 = pyb.Flash(start=256*1024) F' O; k+ |0 W. I! R
- os.mount(p2, '/data')
复制代码
' ^! c9 K! ]) b# e. Z' C3 Q. \) H- H- @1 ]/ E U! ?
% q: {/ ]* m% i I) d
来 boot.py挂载数据分区。 " ]( W2 g3 m+ j# v; }
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os+ y8 U' [- R; Q9 I+ d
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')$ }. u5 h4 s$ ?0 U( ?6 b2 G
- os.mount(p, '/foo')
复制代码 ) P. M4 }9 G$ \7 E; f
& Q+ e+ K6 N8 O
. u, T$ w3 k! P5 l8 o* L; i
5 g& X" L( r7 ^/ a6 ]# \: E4 h$ c T8 A
* a! r9 D9 X3 a m- K y
|