使用文件系统 内容 使用文件系统 z1 ]% I3 R8 P
虚拟FS 块设备
) g$ z" x6 L6 u( ]% _内置块设备
9 d7 B+ k$ K5 N8 P- m4 |自定义块设备
& G- _( O8 W9 p5 |7 y- \ l( |9 M7 M
文件系统
# o: n# g' d" v; g1 l5 K& x
V0 \ C: g& x2 ~, O% ~
9 l2 h/ ^# d( `1 {- h
1 s# L. e! C, f# p本教程介绍 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 上,主文件系统挂载在 /。 ! \+ j$ P( I$ K' o, e' E7 p
块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 * p8 P {$ n& v) A! ^4 R+ d
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。
* L0 b4 F( A1 _5 n. w4 |ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。
; a2 i! b% j m# @
0 S! P7 L5 b) ~ I( @自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
9 T1 K0 K) w# l: Z9 i - def __init__(self, block_size, num_blocks):
0 ^" ?1 Q2 y) B; G; Y - self.block_size = block_size
9 G; h! Q1 c- @6 B' G: h - self.data = bytearray(block_size * num_blocks)
$ l6 g9 Q9 j6 b; ]. n - 0 w# b$ |% X p3 I& o; e) B
- def readblocks(self, block_num, buf):
8 |! q: C3 E6 g/ q( N - for i in range(len(buf)):% i) ~7 x" g( e
- buf[i] = self.data[block_num * self.block_size + i]
) a% Q. y/ V5 F8 Z, r7 t
; f7 L4 E( ^% @1 H- def writeblocks(self, block_num, buf):2 Z3 t/ ^6 N( n
- for i in range(len(buf)):
7 d9 ~. e3 k& B - self.data[block_num * self.block_size + i] = buf[i]/ W; |) c* s1 w" f
3 }8 y6 Z' S$ p$ w& P9 ^; u; k- def ioctl(self, op, arg):* |9 X# S# F# `8 Q6 V
- if op == 4: # get number of blocks' y: f3 ^) V" J( g) A; e# d2 a
- return len(self.data) // self.block_size
- y' y s0 }& {) S - if op == 5: # get block size' {3 a4 {7 f2 ^5 O
- return self.block_size
复制代码 . F- d2 R1 C6 q$ y
; |8 Z. {1 O+ n4 \, N+ S8 Y. R$ _/ ?
它可以按如下方式使用: - import os5 e% s. e* L2 K( _7 h6 ^
2 _ B/ g; I' j8 U1 c( t3 c" V9 m$ K( r- bdev = RAMBlockDev(512, 50)
# n6 K' }) |: F3 V+ B/ g0 i - os.VfsFat.mkfs(bdev)/ I# H- O8 c/ S
- os.mount(bdev, '/ramdisk')
复制代码 / x$ S/ k2 h# }+ {
* s; p7 ]3 W* t. u5 @8 G- j9 r7 H
. V" z, X& Y4 _支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
4 I9 `! \8 g& U& Q, c* e A - def __init__(self, block_size, num_blocks):
0 {$ i. {, |2 \ - self.block_size = block_size# u+ U( A& w( @( {# a, G
- self.data = bytearray(block_size * num_blocks)
, @4 @* b D& m6 M
# d$ g" A* W z# y- def readblocks(self, block_num, buf, offset=0):0 ?) ^$ P9 e: W8 z" W) }, W. U% s d+ w
- addr = block_num * self.block_size + offset ]1 f1 b. p1 J O' z" K
- for i in range(len(buf)):
( N% r/ K8 h9 N/ t. K1 b1 E- H6 A - buf[i] = self.data[addr + i]$ w$ i/ T7 G1 U7 Z' t3 S
- 9 s; {0 I% k% n1 d' Y6 G( t+ X
- def writeblocks(self, block_num, buf, offset=None):+ e* Z4 H# Q! D! l) E7 [# O
- if offset is None:7 e. [. s* Y$ n7 E; v1 v% a# x- w# W5 C
- # do erase, then write
, x4 S n7 v) c; w) Z9 K - for i in range(len(buf) // self.block_size):
+ f1 ], [" l( N- U! m. S6 l2 c - self.ioctl(6, block_num + i)
# `, s2 S6 m/ V, Q - offset = 00 `( C( ]) f2 P Q
- addr = block_num * self.block_size + offset/ @, X+ ^! S$ N+ n5 c5 _; m! ?
- for i in range(len(buf)):2 g0 g8 z2 E S/ ^- D
- self.data[addr + i] = buf[i]! T: J5 ~; r$ g2 d |" }
' s( x8 T; S/ W+ {! I+ S- def ioctl(self, op, arg):
; q: e$ |# D6 M- ?7 f: g7 K+ O5 }9 ~ - if op == 4: # block count/ C: N" x8 I7 o& Q/ A/ c
- return len(self.data) // self.block_size
+ i* q/ G5 i* r! A+ P - if op == 5: # block size$ H+ u# E) b' ?0 h
- return self.block_size
! P& c$ o- A- q& L0 s. ? - if op == 6: # block erase
1 J* ?' }7 I7 J+ G9 R - return 0
复制代码 9 H$ j1 s( L0 i3 Z! N
# P1 P/ b1 t: T. t
* h& F) \( ]0 e. f! a, y1 {0 W$ _由于它支持扩展接口,因此可以用于littlefs: - import os
) S3 O- \/ I: U% m( k - 5 x2 S: Q- x4 l t2 g- o% l4 s
- bdev = RAMBlockDev(512, 50)
- } W: ~$ i1 [3 A. K# A - os.VfsLfs2.mkfs(bdev)* B( u) g, b( h6 Q# n
- os.mount(bdev, '/ramdisk')
复制代码
h: w$ K! ]8 c! a5 f
! a7 w+ \+ R2 g1 e/ `6 Y: {3 u1 r$ Z, g% G% f
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:
* j9 U3 L; {/ X3 N/ T% } - f.write('Hello world')
0 m5 n, W' m% I- A4 w6 r0 T, [ - print(open('/ramdisk/hello.txt').read())
复制代码 . \3 s/ \% {) o* `" n7 @0 k) ^
: N* V# k* D+ \* B7 s0 u; |2 F0 |8 r8 T
5 V- Q% ?7 L3 z+ _' P% Z2 T$ [. {3 J7 a/ {* P) d! P8 c
文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 1 @6 ]* t5 j. L
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32! Z$ C% M# C% x2 ^& b! j
- import os
6 g# d3 @- w* | - os.umount('/')/ m9 n# V2 N+ @, C } ]
- os.VfsFat.mkfs(bdev)
0 L% \0 d m& ~1 X. T: I0 ` - os.mount(bdev, '/')5 n# z" E: r7 R! D8 k
( a) T' `" Q% r2 Q- # STM32: ~( ^; c+ P7 B3 i$ l1 c' T
- import os, pyb
1 [; a7 _2 J$ Y% k [; N2 r - os.umount('/flash')! W) W! M; k/ U6 x
- os.VfsFat.mkfs(pyb.Flash(start=0))0 p" \' Y) s4 a7 M( y* b
- os.mount(pyb.Flash(start=0), '/flash'): c$ I7 M7 {3 i
- os.chdir('/flash')
复制代码 : `/ w: p) K/ }2 D8 T8 f3 e3 O6 _9 Y
; z* X! z& G: `1 H
* O2 y0 j* y. Q; E; g( D. ~& v& Q) n$ A3 G
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. % R% t. H5 h3 x
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32: m$ j" V8 D1 n) S
- import os
6 y% L5 T/ ]7 K - os.umount('/')1 T- I! b" ^# s) v4 M$ y! {
- os.VfsLfs2.mkfs(bdev)/ r! K1 x# ]9 J9 w8 s
- os.mount(bdev, '/')$ J8 R2 P* j" o7 M7 J! |$ U2 k
M( i$ w$ ?& m- y1 {0 H% z- # STM32
6 o, [6 A! @/ Q* W# V - import os, pyb
0 R( v8 B3 E) Q& \6 n - os.umount('/flash')# y7 H: ^! ~6 W ]* F' K S5 U5 }
- os.VfsLfs2.mkfs(pyb.Flash(start=0)) E4 d+ K H s/ k7 N O% u2 S
- os.mount(pyb.Flash(start=0), '/flash')2 y* B* ^6 c+ {. }$ p. P8 @
- os.chdir('/flash')
复制代码 + G* `0 M- ` Q# F( }
6 n+ Y- G3 o, X# n
4 p4 j @$ s' m' ~3 C
/ h9 i1 Q. O7 i( N混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
/ g& s' s1 d5 e. v$ b( ^- N - os.umount('/flash')9 q! S5 W, U* _! {8 u; ?' q
- p1 = pyb.Flash(start=0, len=256*1024)& b. [2 o* e2 l0 b7 ?" l
- p2 = pyb.Flash(start=256*1024)
9 G. r! I7 p0 B- _8 x - os.VfsFat.mkfs(p1) }( U: N' y' j+ K3 k5 U6 k
- os.VfsLfs2.mkfs(p2)$ q* ~5 q: B2 b
- os.mount(p1, '/flash')% A# v0 R! O/ Y* \" h' @ W
- os.mount(p2, '/data')
% G9 ^9 P2 B6 O - os.chdir('/flash')
复制代码
- K7 x0 t* U4 v9 v
! e7 E# X# Q8 H6 P+ S
0 {' ?7 d: f, L. w% x* s这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
4 ^! {- w! n$ Z/ ]( K( J2 K% z - p2 = pyb.Flash(start=256*1024)9 a0 g' x1 o" r T7 g9 f
- os.mount(p2, '/data')
复制代码
% ]) ]$ ~9 ^+ r i9 z& u% J/ C s& l# y8 p2 I' h
r* b; o- y6 l: b来 boot.py挂载数据分区。
+ D6 p& L9 B2 `混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os& Z3 j( V9 B0 I- ^# w; G8 D% d6 s
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')$ x: R. o+ i. r, j. q$ Z# s. z4 J
- os.mount(p, '/foo')
复制代码
3 \" [2 B1 \- d; ^$ K6 X6 N L \
/ q5 r. P3 d* y) Y6 z1 t
- ]4 O5 g: v5 K) A
8 f: b7 E* w7 Q! b
5 l$ H: i1 Q( P& _. u& H- D4 R# y( i |