使用文件系统 内容 使用文件系统
2 Z/ G/ v' N, E. ?, [7 c0 v虚拟FS 块设备
: G1 n, e! Z1 f内置块设备
, }1 P3 ^' H+ D0 F ?) Z' a自定义块设备 ! i* |6 m7 D; N$ x1 j, E2 h5 |
文件系统 " o: `/ h. j6 t) P% x- ~# |
* L' O; b/ S" [0 s( x
2 Z. N1 ^0 J" h; c % b7 k% J# B: ?1 a3 Q
本教程介绍 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 上,主文件系统挂载在 /。 ; \2 ~5 a5 c# |) i
块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。 ' X1 U' s( \# X {% F9 [" F. u, s- B
ESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。 ' x/ U. B' y: m5 W
ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。
q2 e9 F: s* _/ H) T- I/ c9 F5 T" V; b N3 E! A8 }9 W9 D& |
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:; E9 Z# l6 m& c: ^$ k0 V2 G
- def __init__(self, block_size, num_blocks): Z0 x$ F( X' I5 N1 T& b3 ]/ z
- self.block_size = block_size0 }/ \) ?: ]. J$ c% [
- self.data = bytearray(block_size * num_blocks)4 r9 n9 ^* ^; F
' ~" @1 x/ e& y; y) o* h4 b. f$ o+ p% a- def readblocks(self, block_num, buf):& b: i5 T K# |" k2 \
- for i in range(len(buf)):; O( U* R3 _/ b! s9 \, M
- buf[i] = self.data[block_num * self.block_size + i]% G4 {8 c. ]8 s# y& r
- 1 o" O* u* k1 Z7 `/ Z
- def writeblocks(self, block_num, buf):
9 |1 ~ r7 S% y* w - for i in range(len(buf)):
, [( Q' e# c3 E" \) _ - self.data[block_num * self.block_size + i] = buf[i]9 I! _% @ [8 ~! j$ a# P8 J4 U
- l& a! ~$ Y2 e! Y9 U5 r- y- def ioctl(self, op, arg):
3 A( ^2 h! [& N# C5 F) ?# z - if op == 4: # get number of blocks& W; `- H/ w7 z" r0 G
- return len(self.data) // self.block_size4 j! ]( S# b% _& p0 \& Q3 }
- if op == 5: # get block size
4 X) S% Q3 \: d1 G8 m6 e - return self.block_size
复制代码 W: C. f2 C' E7 d. g9 y# O
0 u% w) W: w" U
: m; z/ l* S$ s" g+ j$ K. [它可以按如下方式使用: - import os
0 d8 L3 u8 O1 o, e3 s - + d3 c7 N, n6 d+ _$ e
- bdev = RAMBlockDev(512, 50)
, }* @( {4 {1 x4 F/ t - os.VfsFat.mkfs(bdev)/ [8 M& _" T2 O0 G
- os.mount(bdev, '/ramdisk')
复制代码
2 q" N& @! b; B) a& U* ?
, E8 |9 j/ }0 K5 g6 [. R9 _ _0 Z8 I- e# f0 t |
支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:
# ?- V6 \& }! ?. u, A - def __init__(self, block_size, num_blocks):9 x) ?4 K- X% C5 ^: J
- self.block_size = block_size7 q0 v4 R# _( K; j. X6 e
- self.data = bytearray(block_size * num_blocks)
3 s+ B! {( }7 h3 u7 h! T - 6 h. T$ D8 @, b, R, y* Q
- def readblocks(self, block_num, buf, offset=0):9 o# D2 d$ B' U; B4 d
- addr = block_num * self.block_size + offset
! ^8 Y$ W4 ?$ D, T* G6 ^' c - for i in range(len(buf)):$ ~5 @& u! k: ?. U$ H* B0 Z
- buf[i] = self.data[addr + i]( n+ Z% s0 y4 u) }! s- h" x ^' h
- * ?" m( q: ]) p# c) v
- def writeblocks(self, block_num, buf, offset=None):
5 C/ |7 U* P' @$ ]9 L! o - if offset is None:
- B3 ]+ _" C6 T - # do erase, then write
4 b# M" W ~6 X1 {3 M8 X - for i in range(len(buf) // self.block_size):
: l: x1 U; V1 s - self.ioctl(6, block_num + i)- V1 { N8 A1 g4 c* D
- offset = 0' Q0 e0 N* S) z
- addr = block_num * self.block_size + offset, @0 W( @. }# i
- for i in range(len(buf)):% b ]4 O- }' D* {6 _; W! |# j
- self.data[addr + i] = buf[i]. ~9 e" f& ?. A. j$ D% v
- ! i/ `6 V$ I, u2 s: e- s. x
- def ioctl(self, op, arg):
+ q& E O1 Q1 u% f; r# N$ N0 T - if op == 4: # block count
2 x* S$ o* }5 R/ R - return len(self.data) // self.block_size4 c# M) Y% f0 B# ?. ~
- if op == 5: # block size+ V- J4 F9 b' ^4 v1 y
- return self.block_size4 I) _5 [$ m/ U( f
- if op == 6: # block erase
3 D8 _5 _* |: w - return 0
复制代码
4 ]7 @$ P+ D: P, o U7 s2 k o; I8 i' U5 d; R. M. S: L
& g3 q8 Z" X# B9 c3 f由于它支持扩展接口,因此可以用于littlefs: - import os+ V7 r- @% ~( g- g
- ' d9 O- I: \' C2 v; x- k
- bdev = RAMBlockDev(512, 50)! t9 e( ^3 v- ]2 W
- os.VfsLfs2.mkfs(bdev)7 S" \; I2 w' J5 ^( k
- os.mount(bdev, '/ramdisk')
复制代码
4 m8 \3 s$ ]! x5 K, c
, {6 u8 H4 T1 E L
* r* o3 e, A% w; C# l/ S+ t) t一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:5 ?6 C0 ^/ X- g5 Z3 }
- f.write('Hello world')2 G. G( I! g$ [/ S! |/ e# g0 U
- print(open('/ramdisk/hello.txt').read())
复制代码 - ^. o% b1 \3 b5 ]: }; _
, q% |% D& F" [$ A! z, Y- V( V% x1 z6 e$ e# D* u
; `5 I9 @# I7 h7 @
& l* w1 D H% q/ k. h& I2 R$ O( e+ w文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
, L& m; [9 l) `2 kFATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
6 \6 o+ u: z. a' @3 q% W - import os1 o) \, V; o7 o5 l5 b6 b
- os.umount('/')1 L/ d, r. L" Z
- os.VfsFat.mkfs(bdev)
4 ^7 `9 M* j. h0 m- {+ h& P+ A - os.mount(bdev, '/')$ q7 A" g' d! f2 W
) |" Q" f" R# N8 f$ Y; Q6 |2 l- # STM32' o& `- Z2 k5 ~/ l% d2 i+ o
- import os, pyb" n- f) }! x, n$ X8 m( `( `# |4 A
- os.umount('/flash')8 ^5 A+ i% o0 P! W
- os.VfsFat.mkfs(pyb.Flash(start=0))
( v: ^$ R: `* C$ x8 o - os.mount(pyb.Flash(start=0), '/flash')0 w9 B' E+ H) p. a2 Y) ^
- os.chdir('/flash')
复制代码
! z/ Z% V# p! G! D8 L, K; V2 ?! D/ x r4 g" m# L
2 M( n* ^+ t! L; D, F! K( v
" G& G% O. H9 ?" ?; m
LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
2 l' k& K; O1 M. c注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP324 n3 ^" |6 w- ~; B. [6 b
- import os( T- B7 {* ~: j/ j D0 B) K
- os.umount('/')
4 w$ k5 J( r/ ~* {. N, C# K6 w - os.VfsLfs2.mkfs(bdev)
( t* r: v8 C/ W$ X4 f& @& N - os.mount(bdev, '/')7 [6 f. N3 u0 {* w
- 3 [; X) B: @/ o6 w) q
- # STM32
: w9 b+ ]. v6 [1 y! O0 `' X* _2 v9 e - import os, pyb$ h8 g T( V: H: s, {
- os.umount('/flash')- {& j% q& E' x% ^% @. Y* Q
- os.VfsLfs2.mkfs(pyb.Flash(start=0))0 b A* ^' ~8 D6 N' {
- os.mount(pyb.Flash(start=0), '/flash')
& l$ j' e( W: s. B& U - os.chdir('/flash')
复制代码
2 m0 R5 j: ]' c! u% r3 e5 B
$ G& |3 [2 w6 g. n9 Q0 m2 I6 w" b: [! x, i
* |2 t, a# s: K* M' n: H' L混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb l1 K. N8 C+ e3 @9 Q! s
- os.umount('/flash'). B2 @3 p+ V1 m( e8 Q. r
- p1 = pyb.Flash(start=0, len=256*1024)
% l% E" m L c7 C0 X - p2 = pyb.Flash(start=256*1024)( r o3 W7 _* f9 n" `! n
- os.VfsFat.mkfs(p1)1 k) S& B2 `5 T) ^
- os.VfsLfs2.mkfs(p2)' ?; [. {2 {; S
- os.mount(p1, '/flash')
6 n. }" C" J+ t6 B3 P& M$ ] - os.mount(p2, '/data')
2 v6 w1 | ?. X% q+ ~" Z - os.chdir('/flash')
复制代码 0 W" C' H6 H2 ^: Y
7 j4 v* b1 U3 i8 u" u
8 j6 K: V9 ^2 s9 H3 G- D5 s4 n这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb9 g6 i4 M) e$ O& C2 W& `
- p2 = pyb.Flash(start=256*1024)
1 `- y% \' A' n4 ?1 v - os.mount(p2, '/data')
复制代码
8 ~# C1 a8 r0 X2 L- w: C& J* t% K+ |# ?1 v
9 }" K* [. \* u; ]( F, S来 boot.py挂载数据分区。 7 F1 f' Q$ H% X- L0 |4 e
混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os
3 H5 A; a1 X1 O5 z* c+ [6 n! f - p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')( C; H9 Q' d. W4 b2 V
- os.mount(p, '/foo')
复制代码
2 n2 g. I" Q2 b, E) A
}) K2 y" q9 C$ {6 D- s6 h% r
4 L& ] ~/ Y: r! V/ A+ _5 l; M$ s& w! J
! f/ E& w: j' e/ {
8 D& O* |; F( \( E* I3 ] |