使用文件系统 内容 使用文件系统
! h" [1 g( p& a/ M" r9 r0 c虚拟FS 块设备 ! s9 y% g# {4 n% O/ v
文件系统
i! l( b( |5 D. |7 E" s' Y9 [4 s7 \1 E( j. X2 I" T
8 J- v6 K% R6 P: V * D7 C! o4 |, u0 ?
本教程介绍 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 上,主文件系统挂载在 /。
9 X. E0 k& `( R) A) v' g( t b* R块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 / x+ w- f9 g9 r$ U
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。 3 r! N; G1 `* k$ _" _
ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 , ~' I2 K+ Y: ?1 z
, V! p0 z! w9 |2 y4 d; l- n& b6 h+ d: M/ J
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
6 ?! ] }- \) }" W2 h, q - def __init__(self, block_size, num_blocks):
! H* s4 A9 g4 W3 o6 G, o. V# q3 B - self.block_size = block_size
' I, w2 u# n$ d9 H) i" f' U5 w' X; f - self.data = bytearray(block_size * num_blocks)
/ z: O7 f' w; _* s+ K
) y$ r5 n l# C- def readblocks(self, block_num, buf):' M; m4 d/ v4 h6 R
- for i in range(len(buf)):
5 l7 e$ d+ U; |$ x; A3 N5 t1 C - buf[i] = self.data[block_num * self.block_size + i]
3 v/ ?+ `; C, ^, O0 B3 K1 z$ l7 K
1 x0 e- h& v& _1 u- def writeblocks(self, block_num, buf):6 x1 I# \# f9 h: ]& |' c: J( [
- for i in range(len(buf)):7 \0 L" a/ H5 E" j9 W4 J
- self.data[block_num * self.block_size + i] = buf[i]
$ b/ ^# C- d6 V ]1 d4 L - 1 o5 X7 \* j% y. w" _0 y+ S, j
- def ioctl(self, op, arg):, ?$ I' f5 h7 `# M
- if op == 4: # get number of blocks
N! E5 ~8 j! L1 Q9 |2 v( ] - return len(self.data) // self.block_size0 h/ ~/ r" J6 K# n$ V2 m+ B% Y
- if op == 5: # get block size
; p. C2 C9 d* g9 @+ H - return self.block_size
复制代码
' v1 E* V& b" p, A7 o
( I2 f" a# m, k- Q& }* Q! O, R1 ^5 ^, [7 v, ~7 Y
它可以按如下方式使用: - import os3 s4 |0 |5 M4 S7 @. c/ \
- 5 v2 q) q+ n( P" {% R& h9 v, ?/ @
- bdev = RAMBlockDev(512, 50)) r2 o" v: o% U" W- X! O: t; f
- os.VfsFat.mkfs(bdev)
; `% {5 ~6 ]) i. n, e - os.mount(bdev, '/ramdisk')
复制代码 0 |, J8 i% U/ |# E
$ L& }; J* s' X# v) a: B" M- E
6 u. o3 q5 V0 |( x支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
9 z" r) X0 Q/ c# W. a: u6 O& n - def __init__(self, block_size, num_blocks):& C% U, r" Z, r: {* e- l
- self.block_size = block_size2 q: O, U8 j& h/ H- Q' [
- self.data = bytearray(block_size * num_blocks)4 Y! Q+ z3 z6 U% d ^
: L2 h' m* g8 I- {2 [- def readblocks(self, block_num, buf, offset=0):
$ U( f: t) y. `/ R" t$ j - addr = block_num * self.block_size + offset
& Y- ?% o7 c0 I6 E$ N - for i in range(len(buf)):, ~ T( D0 O7 `& n6 u1 s
- buf[i] = self.data[addr + i]* K* ?5 L K4 I* i4 [9 E
- 5 J2 s; ]0 Q2 T! ~
- def writeblocks(self, block_num, buf, offset=None):
1 B7 H3 k- @/ Y4 e - if offset is None:
0 d: K1 t" [2 l6 y9 n - # do erase, then write' v( b% e1 m7 u9 K4 g. X& p) B
- for i in range(len(buf) // self.block_size):
, Y' Q8 f4 ]2 P* T! l - self.ioctl(6, block_num + i)
: E [2 M% P4 {8 ]- p0 C% X - offset = 0
2 e2 S, d, E" [: \: `! B' Q - addr = block_num * self.block_size + offset
8 g, r5 H( O) o# v; z - for i in range(len(buf)):/ c$ p; F* ?; |0 h' b/ b( C9 Q, _! n
- self.data[addr + i] = buf[i]* O: F1 v6 l5 w, E; Y9 F
, {; J, N2 J, O( ?- def ioctl(self, op, arg):
9 N2 D {8 m3 d0 [8 z - if op == 4: # block count
) i+ \# w: L# R* D - return len(self.data) // self.block_size; Q' d, t V' M; _, ^8 S
- if op == 5: # block size
5 d9 k% r* ~7 R; | - return self.block_size
" \9 {5 U: H( s$ r# M - if op == 6: # block erase
$ J8 y; X0 w$ X0 u - return 0
复制代码
7 V$ t6 O0 z9 X6 i
. K9 D1 p ^; a8 P' [/ R
3 w" s4 @: K. z) _由于它支持扩展接口,因此可以用于littlefs: - import os: y5 m W k* g" c4 \" J
j% e% D0 X* W" i- bdev = RAMBlockDev(512, 50): g6 V, g0 E+ G6 P
- os.VfsLfs2.mkfs(bdev)
( |1 F) p( u* E/ b - os.mount(bdev, '/ramdisk')
复制代码 7 d; V9 w2 ~1 [3 R
0 P9 V r6 W. D1 V* R1 I n( r" S9 }& } e
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:
1 {: n3 ^) Y6 g9 ~: m' B - f.write('Hello world')+ z: ^4 y7 g1 q5 o1 n2 y
- print(open('/ramdisk/hello.txt').read())
复制代码 % B' }( I5 w' g" E& C+ N
3 P6 r- {( J$ V$ l$ Z6 o5 q) j
% @0 v" S3 e2 _) c: R* t% b1 b) T$ c+ N) e0 B7 G6 B% m% `& L/ b
7 i/ y4 G6 ~( p文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。 ; M( v9 x p" E' X
FATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32, v) T2 Z5 o) B# @9 N) U7 Y4 ~
- import os0 j/ z5 N& f4 p2 Q0 w+ t
- os.umount('/')
( ?/ k; `& w+ x4 i) m - os.VfsFat.mkfs(bdev)
2 P# { a) n' n - os.mount(bdev, '/')
' }$ O. W* ]6 @1 H0 ^( ? m/ m* O# R
+ H5 S2 x! N7 x; ^- # STM32$ k z/ Q% N2 C" Z: E
- import os, pyb% Z9 G5 Q* W0 f; j
- os.umount('/flash')
: b) u5 y% h1 }! l/ _3 l - os.VfsFat.mkfs(pyb.Flash(start=0))
) k9 o& _& U( c - os.mount(pyb.Flash(start=0), '/flash')
. S1 D% }8 [0 b1 R8 `! h - os.chdir('/flash')
复制代码
" {* C! @' r$ j1 \# e" r. M$ R) `( M/ C+ Z9 l; ^& t
# ?9 O+ C% R* g
; |: q8 L3 o* l2 u. k' N
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295. 8 m: U1 t$ B3 J) A
注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
* B; A) O( O( j) \. [, ] - import os; g: L/ B r# a8 n
- os.umount('/')* Y# I$ e9 h! B9 Q) Z- ~; q" T
- os.VfsLfs2.mkfs(bdev)& }: h- @. j# x r1 g
- os.mount(bdev, '/')
" g" e) A4 u2 c. Q1 U4 I
8 w/ p" A' q- ~; n- a- M- # STM320 h3 [ S- ~; ]3 Y9 G$ a
- import os, pyb5 d* l2 H& l+ S [; m4 n
- os.umount('/flash')
/ s1 y9 ]( s, ~5 ~ _ - os.VfsLfs2.mkfs(pyb.Flash(start=0))4 W1 S3 v0 [* ]0 q
- os.mount(pyb.Flash(start=0), '/flash')
) x# J* w8 l: F( [7 i/ ` - os.chdir('/flash')
复制代码
& a7 M! _- k7 D8 h
% u3 ]. d' h# o1 f3 z( V- i; T4 C7 w" y" d
. a1 A% I0 }! ~: M8 _1 S( t7 ?- i混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb
9 P ^8 ]4 ?' p. \) P, \# y - os.umount('/flash')& L8 H) @; V7 K7 n9 V
- p1 = pyb.Flash(start=0, len=256*1024)- Q* A' Q) r9 r5 d, U5 u
- p2 = pyb.Flash(start=256*1024)
, \5 [& _) Q8 M( ~+ `4 o7 L& P - os.VfsFat.mkfs(p1)
0 S0 Q; m( l# s+ H/ S/ {' N8 o. r3 o - os.VfsLfs2.mkfs(p2)
0 `% i3 J$ L2 _ - os.mount(p1, '/flash')
6 h' ?+ i( Y( H+ \1 i- K - os.mount(p2, '/data')& F1 w! Z7 Q0 T6 M e
- os.chdir('/flash')
复制代码
, ~# c7 x7 Q) q7 {2 v
$ t% p4 l0 I! B$ r+ ]: ]6 j" z
& |0 r' s! [% T( T! |# ^7 N这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb
; z4 E2 d+ k d- [" B# P, l - p2 = pyb.Flash(start=256*1024)% A* @- i' `$ j. T
- os.mount(p2, '/data')
复制代码
' ^/ j) s8 c" ?8 N: h' m% {
3 v' a8 |7 k a& @! c k' p, o5 o- L9 H: @ n0 ]% A
来 boot.py挂载数据分区。 / B- y7 `0 ^ ~+ J
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os) ^. E" t7 t7 x/ y8 k6 h
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')& X1 b; X. W7 Y* ]* i9 V
- os.mount(p, '/foo')
复制代码 & o: j% X) @7 k0 m9 ^
: ?; C: J I6 T/ O
! e: u& y/ Z' H$ X
! z8 t& y& m! O& D7 n( z* Y+ ]7 R$ R) Q1 s
) _3 m; c/ N: l4 x, ?* F! W |