使用文件系统 内容 使用文件系统 + p. l @3 @3 D+ l. _, g8 e
虚拟FS 块设备
6 R4 }8 ?5 m& G: e( [, ^内置块设备 2 {& B2 D! l3 Y3 C' t/ f5 Z, _0 d) y
自定义块设备 7 ~; c/ ?/ W, {
文件系统
$ h2 G2 I/ G5 a4 o8 E. ^6 T( ?
- Q; Y9 N$ i: }! i2 x+ J
- n5 l/ H- E0 r' C
9 e# Q9 H: R# N9 _本教程介绍 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 上,主文件系统挂载在 /。
* R9 d( `6 y& \; O% y+ P$ H' h块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
4 K$ j3 y' b* a' r2 \8 O2 ?ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
+ l: q( u: |; ^2 Z: yESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 - r# P: D. N3 Y, N& D& A% j+ \
' g5 \; M' Q6 i" @# F4 l" h自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:* H0 H. x2 W* z, J, s
- def __init__(self, block_size, num_blocks):
; @ R, q5 T& I- L9 p - self.block_size = block_size; d- J# J& b* X b$ F4 X6 E
- self.data = bytearray(block_size * num_blocks)
! e+ t4 H- h+ C7 T: J- W0 k' s! f
* U5 v4 G3 e# u: F% D& h" L( V9 i2 `- def readblocks(self, block_num, buf):
/ A8 k+ l# I. M! C3 j; D& k - for i in range(len(buf)):" I* T# U4 x0 q9 U/ A0 G1 C! ^3 {
- buf[i] = self.data[block_num * self.block_size + i]
4 S/ u; s. g9 f5 T6 i5 ?! p3 U1 ~ - K4 H: S4 _; q1 r1 j
- def writeblocks(self, block_num, buf):+ B& o \% R; Q
- for i in range(len(buf)):# I& U7 K, K3 a, T; ]5 ~3 K
- self.data[block_num * self.block_size + i] = buf[i]. p5 }% G$ e B" G0 s
+ h3 s9 Z$ Q H5 b9 }2 G- def ioctl(self, op, arg):: z8 l* e& ^; R
- if op == 4: # get number of blocks
1 X9 V: P* Q2 e1 k! } - return len(self.data) // self.block_size
% d4 X( P; q4 k4 ` - if op == 5: # get block size
3 `& m: R" h, T) } - return self.block_size
复制代码 $ E: e. |* t) [( ~7 Y7 ^2 P. z
9 M1 \% k0 j- G5 z
# I/ h$ I# c4 i# I" t) `) ]) m它可以按如下方式使用: - import os' l" \4 q- g7 U* G, A% O8 j; L
- # t9 W0 f+ Q, w v
- bdev = RAMBlockDev(512, 50)
' \( r# C" G1 G. j, A7 K N - os.VfsFat.mkfs(bdev)
$ k# @: u7 ?/ y* b) I - os.mount(bdev, '/ramdisk')
复制代码 v* d. u( p1 F
! O" t$ e) a# {. m, I8 T6 @: C2 ^" W& \4 s) G f1 R( L# K
支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
* T5 M: u; x# o$ z: h6 u& m - def __init__(self, block_size, num_blocks):5 a7 h: c0 W' p, i9 Q
- self.block_size = block_size
' P$ e6 S9 x7 }! c4 }; J0 S - self.data = bytearray(block_size * num_blocks)
: c4 m6 c/ E' ~6 C - : A& l0 s, H) u5 l, I# D B+ k
- def readblocks(self, block_num, buf, offset=0):
% E1 l( C7 Y! W/ Q | - addr = block_num * self.block_size + offset
" A- u5 W5 w5 m: f' s& i; J& a6 q - for i in range(len(buf)):
7 q) `4 n1 R9 s9 A' k( `8 ^ - buf[i] = self.data[addr + i]
0 H Y& j' w; O, i4 o( p - 3 O- }; b( f/ j7 j9 r. k
- def writeblocks(self, block_num, buf, offset=None):
5 Q3 z& A6 Z. z - if offset is None:" L# C" o) r, J8 @1 g2 T1 Y
- # do erase, then write& b+ S( r1 n# }5 f
- for i in range(len(buf) // self.block_size):
8 o' Q) f1 W" C" m - self.ioctl(6, block_num + i)
! W$ D: }' ~+ E! x" J) \ - offset = 0' i! Q( d% L" `0 g5 p: l0 s9 W' d
- addr = block_num * self.block_size + offset
6 e1 n& n; m: `* `2 y - for i in range(len(buf)):5 t. x! H, K' z" M4 `! G/ Y" j
- self.data[addr + i] = buf[i]
! a9 Y4 W! p: D6 K! @ - 6 m: t# m- {, Z* X. Q; ]" W
- def ioctl(self, op, arg):
, i) Z& u! g3 J2 x3 ^* j9 V; F - if op == 4: # block count
4 a& ~0 c6 X4 B! S3 R4 I - return len(self.data) // self.block_size
7 n) g. B% l5 @9 R6 p8 B+ r - if op == 5: # block size4 z7 N' M, ^% n3 n4 ?3 i) e) Z- W
- return self.block_size
4 e) J$ T) R3 j5 M" @ - if op == 6: # block erase# d& `+ ]) F9 L+ m! u
- return 0
复制代码
0 ?4 O( H4 i9 n/ E ]4 l, n5 C6 y3 N& y9 O/ q! X3 _ h
0 D% c7 g0 [5 n' b0 ~; [由于它支持扩展接口,因此可以用于littlefs: - import os
: c' V+ z4 M# S, Y - 1 i+ q. R) O( B
- bdev = RAMBlockDev(512, 50)
* f* c/ U- N) G% v/ v - os.VfsLfs2.mkfs(bdev)7 n4 z, y0 S+ x
- os.mount(bdev, '/ramdisk')
复制代码 + E# z0 | I/ R* i" K$ }& E
h. J+ I/ N8 M1 F9 _2 U7 P4 Z
5 Y5 A! w4 U+ y6 R; F1 h
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:
2 u/ l# u3 q2 a" s# `7 s( O - f.write('Hello world')
7 f9 V {8 h1 X! v - print(open('/ramdisk/hello.txt').read())
复制代码 ; f0 l% N7 g W5 V" p# @
2 N; b/ g7 d+ [* H/ g9 \; }
r4 L( t9 W, h! \4 y" z, n
( E# E4 m' j3 ^# X4 P$ t1 X. w) F7 c9 f1 Q! i
文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
$ _, I s" X- u4 XFATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32& o7 H; _0 v6 q
- import os) }9 G2 B% I- A
- os.umount('/')3 W. o4 R4 D1 I% u; g9 Y( K, I
- os.VfsFat.mkfs(bdev)
8 k+ y% p/ y. i" r; y& h - os.mount(bdev, '/')( p* i3 D& {: K
- ! o* {) k( b/ s
- # STM32( K( M$ r4 c" i) p0 m
- import os, pyb9 \4 c- x5 E& K$ i
- os.umount('/flash')$ N. ?. `" ]& N" V
- os.VfsFat.mkfs(pyb.Flash(start=0))
$ f* [6 c \ ^0 S - os.mount(pyb.Flash(start=0), '/flash')! S8 P0 Q/ `/ E/ B+ m" D
- os.chdir('/flash')
复制代码
4 x V, r$ @/ Y( h$ P9 s2 ]: Q1 e5 Z) B
" y: d G. ?* P# j. v9 y- d( g; {+ a) l& V, n; s& _- B! `
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. - \4 K% {7 M- e. _5 |
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP325 e- A5 y2 I. p% `' R5 U! W' _# \
- import os
0 L* S K) N2 n6 B: | - os.umount('/')
+ K3 ]1 d2 V. t8 ?/ i - os.VfsLfs2.mkfs(bdev)
9 ^! ` u! X5 d& V. e& L - os.mount(bdev, '/')
! O3 J! T; g v$ s. ?5 W! t6 e5 @
. {/ h; d3 T& K& `- # STM32
j9 R1 q( F r/ u0 ] - import os, pyb( o# y0 R+ O; _) j* ]" Q3 l
- os.umount('/flash')3 C5 E3 C6 S; Y. ]" ^1 R% {
- os.VfsLfs2.mkfs(pyb.Flash(start=0))
8 _( O6 W* Q; K3 K9 H! G* r - os.mount(pyb.Flash(start=0), '/flash')$ V0 t0 q9 |1 p O+ l3 Y9 |
- os.chdir('/flash')
复制代码 9 Q2 T" j1 H, x; q! X- |
0 C8 N( r- t5 b$ R3 A
! x6 U* l6 {4 c* }
3 Z1 F3 H, x& A5 i) N" y i混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
3 o f& r; @1 g7 S. q6 }1 v& y - os.umount('/flash')3 @+ \; g1 h; G" }0 j% v) ~) L
- p1 = pyb.Flash(start=0, len=256*1024)
# a& x5 s% ]( X9 U' I- i4 I5 b- J - p2 = pyb.Flash(start=256*1024)" L" }, S2 a0 U! d! P1 ?$ J
- os.VfsFat.mkfs(p1)' S! L) W6 E8 A
- os.VfsLfs2.mkfs(p2)
* h! F9 V: v1 s1 n, R - os.mount(p1, '/flash') M" E2 K) z. l: ]! L j
- os.mount(p2, '/data')
3 P* \2 \( Y9 `5 M# f% X - os.chdir('/flash')
复制代码 7 a- c& A$ \1 ~ q5 A. {% u& W
* |* C" y- g# g" y& a0 T& \
& v, G9 H" H2 M这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb$ _; |+ D) i" Q
- p2 = pyb.Flash(start=256*1024)
t9 [9 T5 z1 l2 S( S - os.mount(p2, '/data')
复制代码
; A+ H5 h) q4 X& U
% }" G' n, ?1 B% x: [0 s6 |1 c) s: p
$ R' g m3 Q) Y/ X2 p' |来 boot.py挂载数据分区。 4 I3 f. q% @2 i; { l! |* G3 T( K
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os/ m; m/ ]0 c9 R8 v: k! s- F) Q
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')& b* V5 u% u2 B! ]/ z: t' Z1 D
- os.mount(p, '/foo')
复制代码 % ~2 ~. [$ W l
8 P$ [ t2 a5 _
3 k% e9 }) G- W1 d- A* e' M
2 \; ]+ Z. V: M5 G+ e" e- H% A- O/ j/ f0 e
( h2 i* T) d) O1 _
|