micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 188484|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3276

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3276
发表于 2022-1-20 10:06:07 | 显示全部楼层 |阅读模式
使用文件系统

内容

& g1 J# u& A- l, ^( a' K

本教程介绍 MicroPython 如何提供设备上的文件系统,允许将标准 Python 文件 I/O 方法与持久存储一起使用。

MicroPython 会自动创建默认配置并自动检测主文件系统,因此如果您想修改分区、文件系统类型或使用自定义块设备,本教程将非常有用。

文件系统通常由设备上的内部闪存支持,但也可以使用外部闪存、RAM 或自定义块设备。

在某些端口(例如 STM32)上,文件系统也可以通过 USB MSC 连接到主机 PC。pyboard.py 工具还为主机 PC 提供了一种访问所有端口上的文件系统的方法。

注意:这主要用于 STM32 和 ESP32 等裸机端口。在带有操作系统的端口(例如 Unix 端口)上,文件系统由主机操作系统提供。

虚拟FS

MicroPython 实现了一个类 Unix 虚拟文件系统 (VFS) 层。所有挂载的文件系统都组合成一个单一的虚拟文件系统,从 root 开始 /。文件系统被挂载到这个结构的目录中,并且在启动时工作目录被更改为主文件系统被挂载的位置。

在 STM32/Pyboard 上,内部闪存安装在 /flash,可选的 SDCard安装在/sd。在 ESP8266/ESP32 上,主文件系统挂载在 /。


! \  l, J0 }! H# j! [, u# B块设备

块设备是实现 uos.AbstractBlockDev协议的类的实例 。

内置块设备

端口提供内置块设备来访问它们的主闪存。

开机时,MicroPython 将尝试检测默认闪存上的文件系统并自动配置和挂载它。如果没有找到文件系统,MicroPython 将尝试创建一个跨越整个闪存的 FAT 文件系统。端口还可以提供一种机制来“恢复出厂设置”主闪存,通常是通过在开机时按下按钮的某种组合。

STM32 / Pyboard

pyb.Flash类,可以访问内部闪存。在一些具有较大外部闪存的板上(例如 Pyboard D),它将使用它来代替。该 startkwarg应始终指定,即 pyb.Flash(start=0)。

注意:为了向后兼容,当构造没有参数时(即 pyb.Flash()),它只实现简单的块接口并反映呈现给 USB MSC 的虚拟设备(即它在开始时包含一个虚拟分区表)。

' {5 r; M2 y5 [; c0 f* Z
ESP8266

内部闪存作为块设备对象公开,该对象 flashbdev 在启动时在模块中创建 。默认情况下,此对象作为全局变量添加,因此通常可以简单地作为bdev. 这实现了扩展接口。

; f1 W! z# R7 K) h
ESP32

esp32.Partition类用于实现为板限定分区的块设备。与 ESP8266 一样,有一个全局变量 bdev指向默认分区。这实现了扩展接口。

# U7 i4 H: `* D# }* f
4 I8 I4 B: ]! u. h) W$ I
自定义块设备

以下类实现了一个简单的块设备,该设备使用以下命令将其数据存储在 RAM 中 bytearray:

  1. class RAMBlockDev:
    4 n! H$ F% x& n$ |' k
  2.     def __init__(self, block_size, num_blocks):
    1 `5 b1 c: p9 b+ J3 r
  3.         self.block_size = block_size
    ( C% f6 ^$ [5 I+ [, b: J& R* A/ K
  4.         self.data = bytearray(block_size * num_blocks)9 u1 @3 s. a( C3 v3 g1 S, v1 F/ B

  5. 6 V, @) `9 [9 h+ h
  6.     def readblocks(self, block_num, buf):
    ) `, q4 \. F  O+ j2 H0 q5 F+ G, T/ ]
  7.         for i in range(len(buf)):; ?! o/ z; J! b7 x
  8.             buf[i] = self.data[block_num * self.block_size + i]
      Q2 u6 A2 e* B' a& P/ L2 y& b" V

  9. 8 R9 p5 U: Y1 N( R8 A3 H
  10.     def writeblocks(self, block_num, buf):
    4 k* g; d  I1 C9 M  V6 r( j' a
  11.         for i in range(len(buf)):
    ) C0 Y, k0 Y5 Z- }
  12.             self.data[block_num * self.block_size + i] = buf[i]% C8 h+ H2 g, f  z5 d
  13. # Z, ^: u2 F; k
  14.     def ioctl(self, op, arg):% W0 o. ^1 A1 g6 X: ?* H
  15.         if op == 4: # get number of blocks
    1 n3 \8 E( ~7 K
  16.             return len(self.data) // self.block_size
    & X! W$ l/ w2 d5 e2 g2 a  _: J: W
  17.         if op == 5: # get block size
    " R6 I+ S" |- S" t
  18.             return self.block_size
复制代码

7 c8 x  E! [( \$ A* ?! d
/ n' k1 U8 l+ l
* T5 m( s2 |- j1 ~

它可以按如下方式使用:

  1. import os
    / F; ^1 h3 k8 @+ D8 b
  2. * J5 X8 o( C; `  C
  3. bdev = RAMBlockDev(512, 50)
    ! }( ]9 q5 f% @6 J
  4. os.VfsFat.mkfs(bdev); d8 y/ K2 o* R. \0 n/ a4 M' r
  5. os.mount(bdev, '/ramdisk')
复制代码
5 n  v$ H( \: z  m( l) z+ D% z" Q2 f- c- E
" v$ s0 a8 U- U' g6 _/ }

# S* ^0 R* g! y! p1 r0 I) G

支持简单接口和扩展接口(即 uos.AbstractBlockDev.readblocks()uos.AbstractBlockDev.writeblocks() 方法的签名和行为)的块设备的示例 是:

  1. class RAMBlockDev:( y7 a8 j! S6 p" J# M9 e4 z+ B$ X) f
  2.     def __init__(self, block_size, num_blocks):+ J8 i. S& M; \4 V5 T) e% R3 d6 e
  3.         self.block_size = block_size5 J- M2 l4 |* y. w
  4.         self.data = bytearray(block_size * num_blocks)
    * ?' a5 g. W6 m& }

  5. ) p1 \7 V- }; n! J' P. b6 o7 F
  6.     def readblocks(self, block_num, buf, offset=0):! P1 d( J3 I2 Z! ?  D
  7.         addr = block_num * self.block_size + offset
    5 q  M; D4 C6 l4 F2 m
  8.         for i in range(len(buf)):8 c) \$ n& q4 t0 R9 ?
  9.             buf[i] = self.data[addr + i]4 ?1 H5 W. v# K- c
  10.   a8 ~8 n" f- F" d* U. {% X
  11.     def writeblocks(self, block_num, buf, offset=None):4 r  B* C  J! y# F4 f" ?& L0 G) M
  12.         if offset is None:
    5 V0 b3 q: Z2 j  a* ~% ?* b) B
  13.             # do erase, then write
    , d) v) d* y' F+ G3 C* o
  14.             for i in range(len(buf) // self.block_size):2 C/ p8 C* y1 v4 w
  15.                 self.ioctl(6, block_num + i)
    3 y5 }( r/ z" U( m3 r
  16.             offset = 04 C3 p# o6 `: h% t) w+ h
  17.         addr = block_num * self.block_size + offset0 E, K! X3 C! ]/ F) M
  18.         for i in range(len(buf)):5 A2 f0 o- q! Q
  19.             self.data[addr + i] = buf[i]
    " @- w' r$ }7 I. ~! X' c
  20. " ^2 ]* e% u8 C( L; u
  21.     def ioctl(self, op, arg):7 M- S! o/ v7 x0 R
  22.         if op == 4: # block count9 e" u  m( R: p" I
  23.             return len(self.data) // self.block_size
    3 v2 ?( f9 y8 R" c5 L7 Y% L: l
  24.         if op == 5: # block size, A4 M4 t' l2 o1 U/ X
  25.             return self.block_size/ b! l( K2 {3 }, V1 s
  26.         if op == 6: # block erase, ?$ S+ _  @/ `+ L4 @% p5 ~" b
  27.             return 0
复制代码
2 B- T# S4 T3 F
/ P2 X( M9 N, K$ l) E) o

) I% C) z$ m( e8 o" h

由于它支持扩展接口,因此可以用于littlefs:

  1. import os
    6 B, w' z  K; D4 t9 u& E9 T
  2. 5 ]/ }+ z3 @6 Y! L4 c
  3. bdev = RAMBlockDev(512, 50)9 @0 F: |# T6 n6 ]
  4. os.VfsLfs2.mkfs(bdev)7 J% F+ |+ I7 I, ]* N/ s% X
  5. os.mount(bdev, '/ramdisk')
复制代码

% s$ z% R$ m, J' r" G
, P" J) q) [4 E- i! e% p/ m
; V! l: D* V5 `+ u) v

一旦挂载,文件系统(无论其类型如何)就可以像通常在 Python 代码中使用的那样使用,例如:

  1. with open('/ramdisk/hello.txt', 'w') as f:
    ! Q! D# t* v7 {
  2.     f.write('Hello world')
    - X% f- C& n  u3 K7 p2 s: b
  3. print(open('/ramdisk/hello.txt').read())
复制代码

# `1 O' h0 U1 P0 A! i. X9 \. e+ F$ t1 P) t; ~

0 [1 |: {7 @; Y7 M! p7 @( z( K$ g2 E2 }# t. K

2 ?1 Q7 {* w; g/ @4 g1 ~文件系统

MicroPython 端口可以提供 FAT、 和 的实现。 littlefs v1 and littlefs v2.

下表显示了固件中默认包含给定端口/板组合的文件系统,但可以在自定义固件构建中选择启用它们。


! d5 j9 l8 b1 @$ X& lFAT

FAT 文件系统的主要优点是它可以通过支持的板(例如 STM32)上的 USB MSC 访问,而主机 PC 上不需要任何额外的驱动程序。

但是,FAT 不能容忍写入期间的电源故障,这可能会导致文件系统损坏。对于不需要 USB MSC 的应用,建议使用 littlefs 代替。

要使用 FAT 格式化整个闪存:

  1. # ESP8266 and ESP32" S5 i& s/ T% L5 l% T
  2. import os: U' \( F- F& b1 D# t
  3. os.umount('/')
    1 P7 a* {% ~* ^' W) `9 q
  4. os.VfsFat.mkfs(bdev)
    ' X( B8 L, F/ ^# |0 ^2 R
  5. os.mount(bdev, '/')
    ( K8 x( `! t8 s1 j& S" q( h6 C+ l

  6.   _7 l. t4 ]: B; O
  7. # STM32! J% l8 l+ b$ m# q% `: |
  8. import os, pyb. V, i0 ~0 G! \. c
  9. os.umount('/flash')# f* M# R3 {1 ~
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    # J4 {1 z' O3 u6 E( G8 f9 Y
  11. os.mount(pyb.Flash(start=0), '/flash')
    8 i& b- X/ A  u5 Y6 T
  12. os.chdir('/flash')
复制代码
7 I) q7 M9 y8 y8 ?* m
& Y& R9 Q, z& P. f4 B$ k

: K* w' q8 {% E* J4 R
9 z  U/ F' y  M" @# ?. S* Z) t4 tLittlefs

Littlefs是专为基于闪存的设备设计的文件系统,对文件系统损坏具有更强的抵抗力。

笔记

有报告称 littlefs v1 和 v2 在某些情况下会失败,有关详细信息,请参阅littlefs issue 347littlefs issue 295.

/ b, p5 b# P! V' u3 Y5 \. g. m+ b

注意:它仍然可以使用 littlefs FUSE 驱动程序通过 USB MSC 访问。请注意,您必须使用该-b=4096 选项来覆盖块大小。

使用 littlefs v2 格式化整个闪存:

  1. # ESP8266 and ESP32
    2 g* |2 {; J% z7 F4 Q2 X( ~
  2. import os
    3 R+ v& ]1 S6 E9 \1 D+ }
  3. os.umount('/')& k6 o& a) O- f9 n" K
  4. os.VfsLfs2.mkfs(bdev)2 [2 m& b+ W' H4 q- M( W* w9 {
  5. os.mount(bdev, '/')6 q: q% |# v$ g' ~8 l
  6. + ^* o  \% S( Y, N6 x3 a- W: C, J
  7. # STM32) N8 F8 H! Z+ ^
  8. import os, pyb
    ! a& b7 J8 p- o" @0 ~3 k
  9. os.umount('/flash')
    6 ^, \  y$ Y& E* y9 P
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    $ v4 A+ ^% g: V" h
  11. os.mount(pyb.Flash(start=0), '/flash')
    & G4 W- `0 o4 {2 @6 u# q* s/ {
  12. os.chdir('/flash')
复制代码
# J+ b& E: H# D! Y, ^1 Q/ a' L
; ~" g/ k" R; g1 Q' e

0 Y6 \4 ]7 M2 W" f# Y# E1 |$ G. }
( Y, r( r! z9 b& I2 L4 m混合 (STM32)

通过使用 start 和 len kwargs to pyb.Flash,您可以创建跨越闪存设备子集的块设备。

例如,将第一个 256kiB 配置为 FAT(并通过 USB MSC 可用),其余配置为 littlefs:

  1. import os, pyb. w+ g$ P- x8 k4 J/ m: W. f2 k* P
  2. os.umount('/flash')$ L, p7 u  b$ G9 N9 b( n
  3. p1 = pyb.Flash(start=0, len=256*1024)
    / b8 ]# B% f# i+ f* s
  4. p2 = pyb.Flash(start=256*1024)
      @& M8 o) q% O, E2 d
  5. os.VfsFat.mkfs(p1)
    , o2 U- W7 F7 d/ I( O4 p" j
  6. os.VfsLfs2.mkfs(p2)
    . ?. p0 g5 D# p  b& v! S
  7. os.mount(p1, '/flash')
    ! ?3 Z3 n" b- I7 t$ X# N4 Q
  8. os.mount(p2, '/data')' K- D' U2 Z/ W+ ~/ i4 J$ v
  9. os.chdir('/flash')
复制代码

" _/ C% _! G& H6 o" c2 I& f# c9 K' F/ M. }) }9 l
. l! b5 u5 b1 a3 E' P5 F

这可能有助于使您的 Python 文件、配置和其他很少修改的内容通过 USB MSC 可用,但允许频繁更改的应用程序数据驻留在 littlefs 上,从而具有更好的电源故障恢复能力等。

偏移处的分区 0 将自动挂载(并自动检测文件系统类型),但您可以添加:

  1. import os, pyb. K, _0 ]. L$ F. J: L9 l
  2. p2 = pyb.Flash(start=256*1024)* v4 C7 H; l4 r# l
  3. os.mount(p2, '/data')
复制代码

  J) d7 n# s" |$ P+ u( ?. H0 k  W' o4 z3 @7 E8 }1 }: {

7 t% b0 d' m. V- f" e+ v- X

来 boot.py挂载数据分区。

, A  Q% a2 ?4 F6 {
混合动力(ESP32)

在 ESP32 上,如果您构建自定义固件,您可以修改 partitions.csv以定义任意分区布局。

启动时,名为“vfs”的分区将被/默认挂载,但任何额外的分区都可以boot.py 使用:

  1. import esp32, os
    4 z9 T, R6 H1 H9 y: G% b7 D
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    " |) w* f( f, [, M' w
  3. os.mount(p, '/foo')
复制代码
" D% V- @7 g* f* Q% b& n
; V# q; G: i6 R5 d) i
* O! f' y' ]# E4 w! M

% C$ r/ f4 }$ Y3 t

# U! w4 |" g$ |5 i" h  i# J; `3 p) }9 E& F

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|micropython编程爱好网 ( 粤ICP备14010847号-3 )

粤公网安备 44030702001224号

GMT+8, 2025-7-19 07:02 , Processed in 0.202800 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表