使用文件系统 内容 使用文件系统 ) K* g. [1 M- Y8 U) x8 R
虚拟FS 块设备 " u' M4 W9 F, k) ]4 @
内置块设备 3 k. V" K& D9 x9 \
自定义块设备 : U7 Z& O2 {& Y! T* `0 x( x
文件系统
O4 u" O% P4 f4 L! Y, U( G1 R3 |4 W
7 x) G. ~5 c) V$ Y& u
! k( J. N8 z& ?' e3 h) s2 p: h本教程介绍 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 上,主文件系统挂载在 /。 : |' l$ l0 P3 Z1 H
块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 " i9 V L7 q5 G3 [0 p; t" S" O. S
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
! w# A N! f/ [4 j: iESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 & Y* A6 g" x; i. u0 k
: t( {- k: o c% G4 S自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
0 G& ^0 c) T/ |0 ?# p+ H* i4 a - def __init__(self, block_size, num_blocks): ^6 e0 ?3 \0 [1 Y% i" S
- self.block_size = block_size0 {" H( B4 B' [
- self.data = bytearray(block_size * num_blocks)
# g5 K k x. @+ a( D - 8 x# D$ f: f$ F' | m1 x7 u, y
- def readblocks(self, block_num, buf):" ^' {# G3 M2 P) j. Q$ ]' w
- for i in range(len(buf)):/ N) z6 y# \+ } q. {2 j9 E
- buf[i] = self.data[block_num * self.block_size + i]2 Q5 z' A" L! C) g' y3 z* f
* i( q! N4 P3 k& X* @' [* l) l; u; m- def writeblocks(self, block_num, buf):( [$ l R4 ~6 Q5 g: X$ w; _. @
- for i in range(len(buf)):
) K X- C' w: K+ F. ` - self.data[block_num * self.block_size + i] = buf[i]" L' W3 K- u+ Q5 a: ?
- 3 g8 u N, g, K# A2 |7 ]
- def ioctl(self, op, arg):
+ K" s) q5 k# \& f. |( T8 n - if op == 4: # get number of blocks, v2 i- A/ X4 j# y$ R7 R. i% k1 N
- return len(self.data) // self.block_size4 G) q! K- @0 f; ~0 p
- if op == 5: # get block size' j+ c4 u# x. S* P8 g) Q: W9 ^$ c
- return self.block_size
复制代码 ! r7 G7 Q8 @3 A2 m& {* d
9 |: [1 \8 o; A& M5 ~) \
* n8 D0 M* Q5 [/ G. A% I它可以按如下方式使用: - import os7 e) B/ Q: D3 w. F. W- D* o
- , l2 a$ y1 \) Y: ?3 W1 A2 z; B
- bdev = RAMBlockDev(512, 50)
* k$ Z7 x) [4 n# Q/ B" R, j - os.VfsFat.mkfs(bdev)
; f* q7 E1 C' n! X5 l9 d( O - os.mount(bdev, '/ramdisk')
复制代码
/ z! T+ \- r! k1 o0 e# X/ X/ E: B: H& w5 i6 P8 A
" r4 {* u6 h& h# r5 H1 W* N$ O支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:* c6 {2 e3 R) X t% r. q0 I
- def __init__(self, block_size, num_blocks):
: h+ O- C# f O* P* |5 r - self.block_size = block_size) R. X3 Q( k+ g. u1 m
- self.data = bytearray(block_size * num_blocks)) Z4 y4 W8 g1 s' |" z3 N
- 6 F0 N" r6 Q) S+ Y
- def readblocks(self, block_num, buf, offset=0):+ c8 f8 y/ c" T0 J' }; `
- addr = block_num * self.block_size + offset
8 `- h- @" z% n' d1 \* y7 ~ - for i in range(len(buf)):( |$ Z9 P" h4 `; S! b6 O7 {
- buf[i] = self.data[addr + i]/ j6 O5 G% v- l
- ! g& d/ p& j o7 r2 ]
- def writeblocks(self, block_num, buf, offset=None):
; \% s) e |9 z; w* x! u1 q1 E Z - if offset is None:: d" p3 @8 _4 @" E% L+ D& F4 d7 B
- # do erase, then write$ F3 H9 m9 r: H& q% I9 J% T# N
- for i in range(len(buf) // self.block_size):
; r: s3 X! _$ g% m8 l$ a- Y' } - self.ioctl(6, block_num + i)
# V. [. S5 r& \ H9 ` - offset = 0# N! H5 i3 o1 W2 d1 z5 c
- addr = block_num * self.block_size + offset+ w6 p$ x2 q* y8 U% ^" u4 t0 {( ^7 p
- for i in range(len(buf)): o; e3 T% i3 j# y
- self.data[addr + i] = buf[i]
9 ]0 q1 K E+ b8 e. S; | - + Y9 p+ C% ? D- h, l! q3 l! ]
- def ioctl(self, op, arg):
' w5 p" r6 v: g. O/ m) Q - if op == 4: # block count
8 R% ^. K6 C" a7 Y - return len(self.data) // self.block_size( e- k% ]; y/ p2 \$ [9 |* B# [
- if op == 5: # block size
/ z6 H; ~6 n+ \8 q% E - return self.block_size
4 P1 N! S7 R+ @4 q" r; P' W. F - if op == 6: # block erase+ c) I2 b; K0 |. {! m
- return 0
复制代码
- f; Z1 @: c0 z( L% g9 Q, _1 M2 z1 _6 w8 B
5 j: v# ?/ a$ h" z( N G- H# S
由于它支持扩展接口,因此可以用于littlefs: - import os
- g, O, D4 o% X- N+ I
0 u0 Z# p2 n& N- bdev = RAMBlockDev(512, 50)
! Q1 K1 R- q1 {. |9 E( I& P - os.VfsLfs2.mkfs(bdev)
4 ^$ Q" h9 Q) ] K ` - os.mount(bdev, '/ramdisk')
复制代码 + i4 F& S5 A) f7 q
6 T' s' {# A) q3 c. {( [
! u6 P7 q2 r2 T6 [5 l# X3 N# l一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:
' _7 @, T# o& e4 ~: ]8 P - f.write('Hello world')# L! `) n Y8 m" K/ W) |5 D( b
- print(open('/ramdisk/hello.txt').read())
复制代码 , Y( }6 K: T8 E" Y2 a) Y
0 l" W: t' K& a/ H' P% C" f
' [9 z# m& }6 X+ Q6 k! P
. }3 M5 f j2 ]7 U9 }
3 V4 G4 T) `4 v文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 / G0 V5 |3 z7 o3 D
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
: _% }+ k2 S( D/ L9 M2 l T4 d, c - import os. x- i' }" y ^: p3 h/ o
- os.umount('/')4 n9 \1 G# e3 F
- os.VfsFat.mkfs(bdev)$ T; F' C' A8 d! u
- os.mount(bdev, '/')" U' H( w4 a, o7 K+ e; v
- , w5 V; U7 h) J6 e$ {* o! H
- # STM32$ B/ \- T+ D1 |7 Y3 y2 ^5 N4 b
- import os, pyb
9 {( n, q% ^9 X6 z, ?( }0 _1 h - os.umount('/flash')' X( K. Q" J2 ~4 m9 R: {
- os.VfsFat.mkfs(pyb.Flash(start=0))% ]. c: a7 C( D" s+ O& a/ R
- os.mount(pyb.Flash(start=0), '/flash')
( r+ O+ C- J3 K6 ?- I+ O! V' D9 N4 ?# D - os.chdir('/flash')
复制代码 ( _% E) J: k1 p3 H4 X
7 d9 A6 @3 M1 M4 u& O5 ?. w, D
: U! H4 x x4 r4 d& R' x8 V7 l6 y. m
7 i: _ z" i* }5 |# Q5 I7 oLittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
" e) O; X0 }) y+ J6 f% `& I% s$ e* f注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
$ ^, D' R6 s' ?" u - import os
# A& a) M: [6 M - os.umount('/')
2 u) B! T" A& O# g) h - os.VfsLfs2.mkfs(bdev)1 X9 _" Z' x' S7 W( m
- os.mount(bdev, '/')/ l% l+ _+ U* C2 O" _
* E+ S/ X, |9 a* j- # STM32
% Q) V' V0 O6 w - import os, pyb. n! E% s4 M, j0 @0 O- k8 ^+ t# k* R
- os.umount('/flash')
2 r: [. H. j$ u1 v% R" { - os.VfsLfs2.mkfs(pyb.Flash(start=0))9 N7 W2 a) S# p; ~4 D, s" B% v
- os.mount(pyb.Flash(start=0), '/flash')
5 s! }6 r; c7 b - os.chdir('/flash')
复制代码
/ R1 B* x5 d P0 U1 O) }
5 d* N: O1 ~/ d- Z# ~+ a
: g& ^; x3 H# }$ W# E& u+ d# i* B; @# I* S
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
4 w3 J" T! X$ f c - os.umount('/flash')
" \* I5 o6 z9 x; ~9 T3 n6 n* o' e4 H - p1 = pyb.Flash(start=0, len=256*1024)
% f3 b7 W& z5 X* ]) \9 L! | P - p2 = pyb.Flash(start=256*1024)2 N0 {4 y" c! |1 m- |( T l; J
- os.VfsFat.mkfs(p1)
' Z2 v1 N5 O1 G0 A7 p7 F ^ - os.VfsLfs2.mkfs(p2)) R5 o: f6 e% J5 d9 b9 o( |8 s
- os.mount(p1, '/flash'); D, t8 v: W& a9 e0 J: e
- os.mount(p2, '/data')! f3 ~ u! E4 p6 Q7 ~7 c- N y& P5 ]
- os.chdir('/flash')
复制代码 ) F; N1 z% Z8 j! J3 }8 a
5 m9 s z: w& v1 Y; y, m: ?$ e+ P ?" D2 t7 b! o
这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
' J# N3 t) O. l3 w! o, g( N* y& H- O - p2 = pyb.Flash(start=256*1024)0 V# t# O- y5 V5 e
- os.mount(p2, '/data')
复制代码
7 x+ I3 h( Z4 Y" e# b+ ]0 P8 ^' |3 z" ?
2 t% S6 m' m& d来 boot.py挂载数据分区。
. s! F" N$ ~; o混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os
- R! }# [6 P i' | - p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')4 P9 E" c5 c/ Y/ h4 k1 i. @6 V
- os.mount(p, '/foo')
复制代码 4 Y$ k1 o8 S" I+ Y
0 C- P( g# Z6 b8 D! [2 q/ _. V! n/ y# ~% b5 x9 e# e0 M: v& W
' a: Q; _+ v8 F. t. G4 o. c
( [2 I' ~) w `
& _7 E9 B% i, w. H' S6 c |