使用文件系统 内容 使用文件系统 7 _3 ?- [- o% g9 B
虚拟FS 块设备
6 G. }3 d4 ~, U* _内置块设备 : k \& x' y/ q- x7 w) Z [
自定义块设备
7 Z! [' n% D" U- \. u& R5 b4 E4 V
文件系统 / {8 l% X/ |9 ^
4 R/ D& c1 y" [7 T7 y, K! @+ R 6 t! n6 k9 o8 [" @: j1 \# \
6 W/ `0 L& K7 [, y: W) F本教程介绍 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 上,主文件系统挂载在 /。
( O1 D+ m: |- e/ y+ r6 W. R块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 * ?+ V7 G9 I$ O1 P# C, y
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
0 o0 I" X) p% h( d1 |ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 , p+ s* Q. d f/ v
4 w* n% A! G# }% K8 T- ~5 ^
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev: a2 Q% \) J. i9 @5 k4 ?
- def __init__(self, block_size, num_blocks):
0 u# w( J6 o1 a- \ - self.block_size = block_size$ a2 e7 J& a7 G" ~/ f9 M+ l
- self.data = bytearray(block_size * num_blocks) v# @5 E' Z# Q$ _1 P9 I: H2 F
3 q# o2 U; P1 p0 H; P- def readblocks(self, block_num, buf):/ V, \. `* U: @; \
- for i in range(len(buf)):
" V. X$ n/ s1 D5 F - buf[i] = self.data[block_num * self.block_size + i]
! c' F4 {5 }3 X/ Y5 u - : f; `! H; }% i$ y8 @; w D
- def writeblocks(self, block_num, buf):
. H: A' M4 |+ X- w1 N5 S - for i in range(len(buf)):
4 P- D% [; [* l4 C - self.data[block_num * self.block_size + i] = buf[i]
( [/ u7 }" p. F* D/ s. b- c q - ) `5 K* H5 l: e
- def ioctl(self, op, arg): R! J k, p% N4 J& j/ [- Y7 Z
- if op == 4: # get number of blocks
- u- T3 \: l& P- b% C& b - return len(self.data) // self.block_size' y2 \( p2 q& A9 v( l i; S
- if op == 5: # get block size/ ^3 X) P! P5 w, f; |. t
- return self.block_size
复制代码 # @" }4 S( Q$ j( Z, A
# B1 f, ~2 _- I! L- H& p( K0 O7 h1 W6 g# w, @5 |" n/ v5 |6 |
它可以按如下方式使用: - import os2 Z- S- A. m! f8 z8 {/ W
2 E- {! A: @5 U- bdev = RAMBlockDev(512, 50)" z3 W6 c8 @; j" p& u7 d
- os.VfsFat.mkfs(bdev): G1 l" U% U7 p/ P
- os.mount(bdev, '/ramdisk')
复制代码
) m$ A# y ?$ n, j$ d8 Q* `" j) Z8 @
2 ?+ N8 F3 c$ V% G3 N- I1 e& e1 c5 r$ W: l
支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:) e: e! c" w) v' t {, u
- def __init__(self, block_size, num_blocks):
# a7 ~+ q" _9 ?; J - self.block_size = block_size# O8 d/ b+ y2 \$ r9 d: r
- self.data = bytearray(block_size * num_blocks)7 s) e8 j1 o( O4 ?4 K, B
- * j u. i. J8 ]
- def readblocks(self, block_num, buf, offset=0):
) d6 j% Y0 V' y8 K% ? - addr = block_num * self.block_size + offset, s% e, s" L7 S1 j( B& }6 _
- for i in range(len(buf)):
0 B- ~* I" @' n - buf[i] = self.data[addr + i]
/ L/ e, J. e& b t/ k) X - * x7 W k# @) q) L2 t
- def writeblocks(self, block_num, buf, offset=None):. L# S( v Y% i F, k6 S# f
- if offset is None:) G- I2 j3 ?' k% _
- # do erase, then write
7 P8 c. c/ V# U5 S4 k3 v - for i in range(len(buf) // self.block_size):
+ b) @, P- E7 r! Z6 w2 R - self.ioctl(6, block_num + i)
6 B! C. l/ D2 u2 H5 M/ B - offset = 0" ]; q* \- L* B
- addr = block_num * self.block_size + offset; E1 s E4 w e# F4 A
- for i in range(len(buf)):
i$ X3 a* j) o5 t - self.data[addr + i] = buf[i]5 B1 L* P0 e# k8 `6 _
+ ?4 v [/ J, {. j8 j" h- def ioctl(self, op, arg):
$ A8 U, x4 m( Q6 a5 p I - if op == 4: # block count2 K9 \# e0 }% |
- return len(self.data) // self.block_size
3 ^; U8 m- e9 B - if op == 5: # block size3 y8 W$ V, O- u& i) p: V
- return self.block_size
3 n: b. \* R5 G - if op == 6: # block erase
) t/ |7 w0 V/ U3 D# v! @ - return 0
复制代码
: i* I/ B0 Y1 V6 j) ^- s- [
) T' ?0 n& M3 H* o( P- \1 [( ^' n* v5 `, u8 ?" D
由于它支持扩展接口,因此可以用于littlefs: - import os# W4 H) ] t6 B! t
5 m, M* I' A- e" J% g5 ~" W- bdev = RAMBlockDev(512, 50)6 y2 R6 ?. L5 x
- os.VfsLfs2.mkfs(bdev)
' u6 k' P9 M7 X% L9 L8 @! J - os.mount(bdev, '/ramdisk')
复制代码 9 K" t: j7 b+ M# ?
/ A2 s9 }- @. M/ f, M% s+ k% ~- r/ o$ G6 x! b
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:" a; B2 ~* B" d* f' q
- f.write('Hello world')
7 l( e, w/ Q' j, U - print(open('/ramdisk/hello.txt').read())
复制代码 8 p# I* D) L$ {# T5 s: H
2 K' i# x4 V5 ~+ Q
. }+ Z5 q6 }5 o2 ]- x6 Y+ ]3 y7 M- A8 {9 N7 b( E
- Y; g9 \1 F0 _1 `文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
$ t( ?8 x, m# YFATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
2 y( ^% V" C1 ?; T. J, B - import os
. f2 R, @' D/ a, z. |# s( J( B6 \ - os.umount('/')% T0 @% N0 T# S" v
- os.VfsFat.mkfs(bdev)" p- T" {) D7 M
- os.mount(bdev, '/')
! N: H' M5 _/ Q& z. E - ; r' p1 ^# N* s. L
- # STM329 t" S0 }5 v2 b8 \
- import os, pyb6 u0 `1 i3 ~2 s! I& v, W$ N, _0 z
- os.umount('/flash')
" r+ p) c2 y# I& L/ k' ? - os.VfsFat.mkfs(pyb.Flash(start=0))
8 }) P4 c5 z8 M" J8 z4 s - os.mount(pyb.Flash(start=0), '/flash')
9 e1 I0 n/ Y i+ ^ - os.chdir('/flash')
复制代码 8 [2 C( V7 _! T9 u4 @; M
3 v1 }8 r u8 s; l0 \0 J1 \
" r! v, ]/ Q. h, v9 `) R! _. f. a `* E4 b9 b8 q
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
" }, Q4 r$ s# i3 S& p注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
, U& e& ^" S. t3 Y% c% L - import os5 g ]3 I& ^$ Y* T4 g5 B, G! a" a
- os.umount('/')+ I0 T% B6 q2 ? o E( B
- os.VfsLfs2.mkfs(bdev)
( B; L8 a0 c! A/ Z. }' L - os.mount(bdev, '/')
5 {% f' O. y- X7 H6 y( l: q8 U$ _/ j - : g3 y# d3 `+ @* n8 x4 w
- # STM32
( [, C6 \& v' n. m - import os, pyb1 m+ C3 g0 f4 |- P1 x
- os.umount('/flash')7 n. f$ d6 M) W
- os.VfsLfs2.mkfs(pyb.Flash(start=0))4 u( W9 B( S3 m- G4 t
- os.mount(pyb.Flash(start=0), '/flash')
& r" R* z* g% m( H - os.chdir('/flash')
复制代码
' S* K1 n) Y# b: {1 f* P& C# b4 G
+ ^! Y! g* n0 W4 [& E5 a
, }* z% s! J% B8 |2 d6 A S& N1 Q# a# E" B1 E
混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
+ @- r. E# K& n+ _7 \5 V - os.umount('/flash') I9 [# O% @/ j
- p1 = pyb.Flash(start=0, len=256*1024)2 _8 V% K3 U" f$ G- Q
- p2 = pyb.Flash(start=256*1024)
& f: e( j# S2 N2 l- | - os.VfsFat.mkfs(p1)
" \/ _; x: H1 a5 c9 ~, z - os.VfsLfs2.mkfs(p2)
2 i {; ?5 x$ b9 l& n - os.mount(p1, '/flash')% W1 D0 j8 K7 @9 C$ _# ?( w
- os.mount(p2, '/data')
9 G! X1 i4 ?7 y) E8 S9 v - os.chdir('/flash')
复制代码
; i+ o/ Q& j4 G0 e3 D# i' U' } {2 _9 z; R
E& M7 Y% G4 V2 q! I! G这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
% U/ U" O9 @ P# q' t0 i( a - p2 = pyb.Flash(start=256*1024)$ _ h3 H7 D" R7 P f9 S2 N, h
- os.mount(p2, '/data')
复制代码
0 r7 b) }9 D% `8 d
! r* g* O' _9 S9 A2 z3 ]1 T B3 A4 ]% J9 f6 O, m$ c; _; z4 K
来 boot.py挂载数据分区。 4 e2 t1 R5 D( d9 c& q$ L6 E0 Y
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os- _0 Y* {) L1 k+ [" U
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo') r" \7 O H3 t) ~- K, l- D: m
- os.mount(p, '/foo')
复制代码 1 t, i6 g& L! J
, ^% A1 y/ p, S# x$ N, I" w
; v! W/ _+ `# \$ @
0 q5 O% W, `8 _1 {3 D
1 B4 \! l+ m4 g- U' T! g
6 F- s. l* \" R# O! V |