使用文件系统 内容 使用文件系统
$ H" S' ~# m5 L l5 ] c虚拟FS 块设备 5 S. a5 c5 v( ^2 s: ~
内置块设备 \7 R* ^, j/ n- ]5 _; R
自定义块设备 $ y6 [ {! B- E" `* |( p
文件系统
/ x2 g k, v; f7 c, X
5 F, R9 u/ z2 V4 S0 c N5 T, l
/ Q7 i9 C* Y6 P 6 t/ z1 [; X/ `) H( c( A( r9 M
本教程介绍 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 上,主文件系统挂载在 /。
* v6 w( }6 V6 _5 G- b块设备块设备是实现 uos.AbstractBlockDev协议的类的实例 。 内置块设备端口提供内置块设备来访问它们的主闪存。 开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。 STM32 / Pyboard该pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。 注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。
/ W0 _3 @! D, cESP8266内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。 * e7 Y: n) J" i9 y
ESP32esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。 / f. ?! H3 O4 R0 k6 t) m$ o
. K/ u; A2 t& B4 ~+ a
自定义块设备以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray: - class RAMBlockDev:
S7 M9 Y8 l/ A1 y/ G - def __init__(self, block_size, num_blocks):, u( w+ j, }2 [! j( H
- self.block_size = block_size6 b. I* h3 _$ D2 w' q2 \
- self.data = bytearray(block_size * num_blocks)* F8 s4 }# ]* m
; ?- j$ h* D" r$ A- p- def readblocks(self, block_num, buf):
: M! }% D) m( w% |* ~ - for i in range(len(buf)):: m$ u5 I( \- ]9 Q" K* y, S+ J3 u
- buf[i] = self.data[block_num * self.block_size + i]% x2 s- b4 G* c, |( ?
! l' o2 ~0 @' r x/ G- def writeblocks(self, block_num, buf):
5 S P& D8 a$ I - for i in range(len(buf)):$ f8 C U3 C k% G9 V6 t" Y! {
- self.data[block_num * self.block_size + i] = buf[i]7 X/ {1 w8 B0 `" M* x
& H: u" ^' F. a/ N2 Q- def ioctl(self, op, arg):
/ G/ h/ `: V; ^' j+ o" A' Z) {- p - if op == 4: # get number of blocks& G* E0 G2 @1 R
- return len(self.data) // self.block_size' s" m8 j# r' [* [
- if op == 5: # get block size
! q$ j7 Q$ M( ] - return self.block_size
复制代码 0 G H" F2 V7 E5 J8 t0 f# U
% ?- C, h+ w: d/ C. A* ^
* h7 T, y, g& `0 \! C它可以按如下方式使用: - import os; {5 I! O* G+ {9 u7 ~
$ u: G* a1 [8 w3 s; |- bdev = RAMBlockDev(512, 50)
+ e7 R3 l2 V9 p( p - os.VfsFat.mkfs(bdev)
; D- j! |# w& P% X - os.mount(bdev, '/ramdisk')
复制代码
: p' ]/ A; G# n# z
9 ^+ X" I, |7 `8 @. X) B
) i, ~8 j2 d1 a% C! n, v3 C支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks() 和 uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是: - class RAMBlockDev:1 }) S, O1 w; K1 @: k6 e: c0 W# P" t s
- def __init__(self, block_size, num_blocks):
2 b1 e6 G6 y( |& {) F9 |" G - self.block_size = block_size
4 J& c, s1 X& Q, S7 M1 N- g - self.data = bytearray(block_size * num_blocks)+ U( z7 h ?2 v/ F% @7 |. y. |: S
- / q1 g1 [' [ x3 P( Y* t# R
- def readblocks(self, block_num, buf, offset=0):( u& \, T! ~, Q
- addr = block_num * self.block_size + offset% ^6 |, {7 r8 l; N, k
- for i in range(len(buf)):+ L; G. \( U) J7 D7 \: a
- buf[i] = self.data[addr + i]
) s# {* {6 k% P" q$ H0 W - * Q; m& u! d) f7 j+ L
- def writeblocks(self, block_num, buf, offset=None):. [& L* B6 I5 ?5 y0 k' M$ d% x' ?
- if offset is None:
( w9 s( [& q/ X; h4 M0 u1 c ~ - # do erase, then write. t T" |: t5 \+ [+ W
- for i in range(len(buf) // self.block_size):9 c! y$ k& `. s3 M
- self.ioctl(6, block_num + i)
) R# Q+ I( N8 [9 A* j - offset = 0& T% e E' [0 e6 C8 r1 s( |; T
- addr = block_num * self.block_size + offset
/ ^& z+ U" U* e - for i in range(len(buf)):% I! f, X! m# N: x5 i
- self.data[addr + i] = buf[i] q. P2 g* ]! V
1 m+ _' u0 Z( n+ @5 H) Z0 n- def ioctl(self, op, arg):
: n3 V+ g8 @, K! M - if op == 4: # block count! g: @* n" Y | ~: G F
- return len(self.data) // self.block_size5 x0 x/ T* ?: q2 k
- if op == 5: # block size0 ^! U1 T! L* a; ^& O- E
- return self.block_size D" P: Z3 m% z
- if op == 6: # block erase' j: Y, y+ C5 H/ B: Z
- return 0
复制代码 : |" m; z2 @% e5 L9 e5 Q
( [. A: i! Z G% n* m7 F
0 _2 e( X* h9 o
由于它支持扩展接口,因此可以用于littlefs: - import os" ?) b5 q8 ?# f: ]
1 j4 u. ]* U" X4 Q: b- bdev = RAMBlockDev(512, 50)
F+ m3 C) R. Z' Q& L - os.VfsLfs2.mkfs(bdev)
d6 t0 p5 B' @( G& u - os.mount(bdev, '/ramdisk')
复制代码
( W" A: k3 l( o. u1 ? f& E" A+ D8 _$ U1 _5 w4 N O
! n) n% {2 W5 _ j4 n/ ^9 B
一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如: - with open('/ramdisk/hello.txt', 'w') as f:: i. R" i- a- a
- f.write('Hello world')
7 o+ E5 a' Y% {# {* }3 b; { - print(open('/ramdisk/hello.txt').read())
复制代码
# x3 w* c4 a, L7 J; e9 b! e% S
9 t; P' q- W0 p8 W
4 j2 J' Y- }8 e+ ~0 q( ?
% @. I+ P- n8 F9 l' }6 R- x$ X T4 h8 B$ `
文件系统MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2. 下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。
( W1 d9 p# m5 H1 z! o5 fFATFAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。 但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。 要使用 FAT 格式化整个闪存: - # ESP8266 and ESP32
, {& \3 q$ |; B# j5 n5 \, Z7 m- E - import os! E" z* d' t, F/ [6 L
- os.umount('/')* N$ ]- Z1 z/ p, ~/ q% V) L& W
- os.VfsFat.mkfs(bdev)
i/ ?: s* ]6 \# m' T - os.mount(bdev, '/')# f7 u& L5 H8 a* ~0 U [8 O$ F7 o
5 [; N$ j \5 }3 o7 G9 X3 y5 G7 f- # STM324 S" J" p9 v+ Z; e+ `! I8 V# E
- import os, pyb
6 @- G& j1 [( o5 z/ E/ K) p# ~ - os.umount('/flash')+ K5 d5 x! y( ~9 z% C7 C
- os.VfsFat.mkfs(pyb.Flash(start=0))9 o. ?2 E( b2 q# \ T( D
- os.mount(pyb.Flash(start=0), '/flash')
4 y; U) \8 l# i3 ?( k - os.chdir('/flash')
复制代码
" I @- F6 k' ? l0 F6 v
( p" f* {: c4 Z; V2 t4 f" N" H1 @, o6 l1 R! j8 a" B/ n" L2 b
6 L- h, N9 Y* S _" k& h0 H0 I& w2 ~LittlefsLittlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。 笔记 有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347 和 littlefs issue 295.
) u% B4 V( Y& S% w注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。 使用 littlefs v2 格式化整个闪存: - # ESP8266 and ESP32
; Y/ Q% ~: P. L% M+ C - import os
3 n# u5 c7 {) U# B! ~& [* O - os.umount('/')
" ]2 V& n) [* N6 Y - os.VfsLfs2.mkfs(bdev)8 R: |( e5 Y8 n8 ?+ j
- os.mount(bdev, '/')
4 x: q3 S+ w7 E$ _9 M Q# ^0 _
% B8 _6 }3 o+ s2 Z0 t) ]- # STM32. ]( J7 _) _2 R( k: p b7 j
- import os, pyb1 R4 b/ G* ?5 ~4 a! _5 q2 z
- os.umount('/flash')& `3 J( F+ Z. |$ W) h6 P
- os.VfsLfs2.mkfs(pyb.Flash(start=0)); s; V/ W8 L# D2 h% V
- os.mount(pyb.Flash(start=0), '/flash')
5 E% i' x" O( K0 A: J7 u3 D0 b4 @ - os.chdir('/flash')
复制代码 0 [ [; j1 d# f" T+ ?5 I
4 F$ u# ~- o" N- D, f) r$ ]% V$ x& X6 _! c. B
- g* i2 Y0 ?$ f; p! k2 T3 i混合 (STM32)通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。 例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs: - import os, pyb( ]6 Y ^. U% n! F% p7 @, g
- os.umount('/flash')
, x& m* P9 f9 `/ }# J! R - p1 = pyb.Flash(start=0, len=256*1024)
. ?0 }- k) o. g5 S2 b - p2 = pyb.Flash(start=256*1024)
6 B) x) q* d3 z9 x - os.VfsFat.mkfs(p1)9 C; F0 l; @& z7 ~& ^ H5 A
- os.VfsLfs2.mkfs(p2)
6 z- \* o8 {8 W* o4 O v - os.mount(p1, '/flash')
" G# Y, d3 u$ d8 D - os.mount(p2, '/data')
& l* _& H# j& A4 P - os.chdir('/flash')
复制代码 2 `9 M. A! [5 G) x/ e; S
" Z2 L# v8 ]* b+ V1 p- x' P& l# A& w4 g! R* ]; o. N% D: R
这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。 偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加: - import os, pyb$ A! T% ~$ z! r: @2 B! ~+ D
- p2 = pyb.Flash(start=256*1024)
' V6 s1 M5 ?2 o- C - os.mount(p2, '/data')
复制代码
9 N2 i! e9 z! \4 F, s) l {0 l& B! ^4 D: v! t; m( l0 b
' O; y/ q! q; D8 m来 boot.py挂载数据分区。
2 _3 F8 `& P+ b% B+ d混合动力(ESP32)在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。 启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用: - import esp32, os" A0 a. L3 [6 G
- p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')" I4 M2 G8 o/ L" I, J, m
- os.mount(p, '/foo')
复制代码 * F1 h1 B1 P$ I! y- q1 \3 L7 l/ K
! ]! h" x8 e4 g; J0 c' c2 V8 U6 A% j, Y; d# p
3 I7 e1 G8 i& c9 P$ J( T
* V: i6 p! l) l- a$ C9 |. L* A9 R7 O1 c" W+ A ?
|