使用文件系统 内容 使用文件系统
9 k& j- ^9 `4 X. K) i/ O虚拟FS 块设备
9 m1 T! N" q' {/ f y+ y1 o0 L# l) R内置块设备
6 {+ t5 n5 d# y) t" T5 ?4 ^: N自定义块设备 , G) v9 B7 z# c" S/ _* Y7 q
文件系统 - E) n$ r( A/ u# k" s0 V$ u( k
; Y2 m5 J3 H3 V1 x 3 m" ]. X2 `( J
6 z" ^ u& @' `* m3 u" s- u L本教程介绍 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 上,主文件系统挂载在 /。
+ u. L' `, d/ l. S块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
" V6 B4 B O, v6 n |( c V4 H1 UESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
4 L1 R& S: Q/ j, K lESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 ! j, {1 I9 {- o
7 \7 r( g+ K0 ?1 i* w$ B. g自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
; S1 j" R$ W( K: Z, j, | - def __init__(self, block_size, num_blocks):* d! Y2 k; c% v% ]! |
- self.block_size = block_size; D; G' \3 @+ M( L0 j( T" x
- self.data = bytearray(block_size * num_blocks)" {# c& _5 E; p. R0 Q/ \3 f# E2 m+ A
- & ? R+ }4 {( w0 p& _
- def readblocks(self, block_num, buf):
U; }8 ^3 e+ G - for i in range(len(buf)):
& Y) r! |- n+ U3 |) k+ H, Q - buf[i] = self.data[block_num * self.block_size + i]
1 r& B! ~& b$ M( \1 I9 |: i
8 Q3 y8 r3 P/ X9 l* i2 L- def writeblocks(self, block_num, buf):7 Q2 F. F, j3 e/ {
- for i in range(len(buf)):
( Q7 V( q+ b% A6 R8 a0 N. P# ] - self.data[block_num * self.block_size + i] = buf[i]: _, I7 p; |' M+ W
- " Q1 S+ v5 f: u n# ~
- def ioctl(self, op, arg):
6 t9 ?* D2 W$ f - if op == 4: # get number of blocks
: @9 w# O2 |& q% ~- z7 v- j - return len(self.data) // self.block_size
) A# n8 Y0 f M7 D - if op == 5: # get block size# N% t$ t d; d( k7 z+ c) O
- return self.block_size
复制代码
8 o& B9 o& b: a; {. _1 m2 M/ K- O1 f6 o# Q }# C `
( n1 n! {1 V7 P它可以按如下方式使用: - import os
7 ^7 F% v$ [5 \7 n7 S
/ ]! U) w- @, \( p* x& g- bdev = RAMBlockDev(512, 50)
) Z& |9 a' k( [0 N7 w1 V7 T - os.VfsFat.mkfs(bdev)
; ?& R6 Z1 f, m4 F8 J - os.mount(bdev, '/ramdisk')
复制代码
/ S2 K5 Q3 [9 ^# W* n/ ]5 p8 C }3 M2 b
) z( p4 Y1 o9 i0 G4 T; ]支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:+ v1 N! w& ]8 a4 ^9 A% n! a |" S
- def __init__(self, block_size, num_blocks):
' q# ^# K# i2 z# @. z - self.block_size = block_size
2 p s3 k2 g, u4 w - self.data = bytearray(block_size * num_blocks)+ j% o0 E9 H4 |% C
( P& R& ~$ y; ?# Y: Q- def readblocks(self, block_num, buf, offset=0):
& B* b! A6 ^: w% g - addr = block_num * self.block_size + offset
0 s" T2 m; O: P - for i in range(len(buf)):
! _ L' G( X: ]0 q# l0 l- A! Z - buf[i] = self.data[addr + i]. V9 ^. M, k2 J' v! }
- ! o6 U" L- U5 L% l9 l: h$ ^ I
- def writeblocks(self, block_num, buf, offset=None):
/ H3 a! M9 C' `! B. y7 z - if offset is None:# n1 b9 b+ U% [
- # do erase, then write& ~9 N- I5 w6 O. w# p* B/ [" x; g6 b
- for i in range(len(buf) // self.block_size):$ ?' \- a, k7 z$ Y
- self.ioctl(6, block_num + i)# A% b5 K/ l3 E/ y0 X* s
- offset = 0
3 C+ k# B' h( j - addr = block_num * self.block_size + offset
3 b5 R: q* p- H - for i in range(len(buf)):
# ?1 j% F& n" z5 C2 E - self.data[addr + i] = buf[i]
% D% G4 e/ g& h6 a/ F# w6 G
0 X% T( N2 B, f- def ioctl(self, op, arg):. l. ~+ U4 L: a9 p+ Y
- if op == 4: # block count9 W5 w* k+ N, O; _/ n& w
- return len(self.data) // self.block_size8 N3 S9 u2 A* K
- if op == 5: # block size' J% ]2 q9 w, N: G
- return self.block_size8 R* [) ^8 V$ S
- if op == 6: # block erase1 `) \+ m$ @; T1 M0 I" z
- return 0
复制代码
9 _/ K1 B; |) o9 \
/ p' I: K# c. z
m6 [! y2 V9 \6 H: S4 i" O' a由于它支持扩展接口,因此可以用于littlefs: - import os
4 e, M$ S& v4 s! X2 n6 e9 f+ v
! g9 A. E6 y, Z/ O7 z4 N; A- bdev = RAMBlockDev(512, 50)8 Z0 ~ l9 c6 i
- os.VfsLfs2.mkfs(bdev)
# T y: t' ~$ q Q - os.mount(bdev, '/ramdisk')
复制代码 + n! h% _6 \% z n4 v! W
% y9 A; e" u' \- K
, @& y; u9 J5 r( ~3 A/ f一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:5 K% t9 O8 P" a3 u
- f.write('Hello world')- V; I% Q# p# W" ~ Y
- print(open('/ramdisk/hello.txt').read())
复制代码 ( v. p6 S( H8 R6 v' N: f+ g3 W
, u: Q" d, w' |, I+ L) Y( [& X O; J3 {$ h0 ~/ l
- ?$ U% T9 F& o8 G# c# B# l# ]+ |
) G0 ?0 d& t" Q& o8 t3 C
文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 ) N+ \1 t- ^3 w S
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
9 b' R* f& n' {( r) I7 Q4 P: L- U - import os
4 I. {: q# S/ S, _ - os.umount('/'), r/ t+ j" z. U. {. t% e4 Z. i6 M
- os.VfsFat.mkfs(bdev): j) [" c: q8 T3 t- Y
- os.mount(bdev, '/')
4 n# l0 w, B0 C" ^! S
; a) H" l9 W4 N m0 B. K! Z1 u- # STM32
. x" m2 t" P2 o6 t) L - import os, pyb! W& B3 Y% Q, S- V% M9 V2 t/ t
- os.umount('/flash'), ~" O/ f# c" @4 v
- os.VfsFat.mkfs(pyb.Flash(start=0))
8 V$ U, V$ }( y' L8 P2 ?$ {5 j - os.mount(pyb.Flash(start=0), '/flash')% n: H& l! P" I0 n
- os.chdir('/flash')
复制代码 $ C+ \- {4 \" H0 G& |! Q q
6 R! I7 R' Q% [9 w. ]5 G
7 O# \0 i+ J* h* [/ A9 Y6 a. g: ]3 V. @' A5 D% Z
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
9 O( l1 Y& x1 O( \0 C: d注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP321 s g0 q/ t# }& C' h
- import os3 l+ r& e1 T/ B
- os.umount('/')
: v- ]8 t" N% \# y3 Y" | - os.VfsLfs2.mkfs(bdev)& G2 `0 O* {* K* @2 Y
- os.mount(bdev, '/')2 ?: H5 |8 F* l1 P: z; _! m
- 1 f" z8 e5 `4 B; y' g1 c
- # STM32& l4 B, m4 S3 `$ x9 M d
- import os, pyb
$ A: A9 G2 V3 ` - os.umount('/flash')
2 G! R: W" P4 X3 o! F - os.VfsLfs2.mkfs(pyb.Flash(start=0)). Y$ @6 D9 S- S+ c0 K
- os.mount(pyb.Flash(start=0), '/flash')
2 }: X6 u V8 _ - os.chdir('/flash')
复制代码 ' w- ]" Z2 B: o0 T, ]+ J
$ V( o& Y$ }4 G& e5 f
) N1 X7 `1 U( K7 O4 V- e
. u# R' {/ Y* {. Z/ M
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb4 j5 I& M k8 u8 U$ t) \) b
- os.umount('/flash')
# N# Q- _, _+ G/ O, a - p1 = pyb.Flash(start=0, len=256*1024)
6 j# J" A3 D& v - p2 = pyb.Flash(start=256*1024), B m+ W. Q% y
- os.VfsFat.mkfs(p1)
: s, G. M( ]1 w9 K9 C7 } - os.VfsLfs2.mkfs(p2)& ?# G; P, Q7 @, b' {9 x2 \% @
- os.mount(p1, '/flash')
7 d6 V3 A: Q2 o$ |9 O" m0 ]6 ~* h - os.mount(p2, '/data')8 G+ {. F) ?+ u( L. O* ~
- os.chdir('/flash')
复制代码
' g' c' k% p) Y K# x& e' y9 h9 T+ Z. r1 c
* C1 B0 W9 f! P, z# n这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
' Z0 {8 o- h! P3 L - p2 = pyb.Flash(start=256*1024)1 E5 }" Q1 W# r3 j6 ?( R
- os.mount(p2, '/data')
复制代码
; h. j; B4 Z0 I5 r. I6 m/ s9 I: R3 h: D
' I3 K' o$ O* d0 {
来 boot.py挂载数据分区。 9 Z5 [5 C1 ~% O6 Y' j: l
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os
% s O% ^2 }0 {" y) O - p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo'), D5 f: |, m3 |6 }) Q: Y! ?% t. q
- os.mount(p, '/foo')
复制代码
$ _( G6 z* O$ ?! t9 Z, w7 F1 L5 s+ Y7 j" K( `) w! I5 f' D" m
+ [9 _: S$ S* A8 c" P% y, K8 M
" O/ a4 G. c/ D5 Y, n& b- V! S6 a5 r+ t8 x6 b. L; i/ D# G' O1 E
( J2 Z2 l" c5 i |