micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 173824|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3236

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


: A1 D# P" h1 k) [! d( V! X

本教程介绍 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 上,主文件系统挂载在 /。


; Y0 x; d' d, Y/ y3 Q& I% e块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


4 x  p$ m1 |4 H& Y4 C0 a3 rESP8266

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

& V- h3 v  }5 A. o% a" V
ESP32

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

4 X. o' g3 r7 Y1 z8 e
1 v) |: e2 q( h& V6 E, K9 [7 g
自定义块设备

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

  1. class RAMBlockDev:
    , P/ O2 `2 ?" T+ f, g" {
  2.     def __init__(self, block_size, num_blocks):2 f  H  w8 J3 B3 S1 e7 b8 u' N
  3.         self.block_size = block_size1 w3 b! r9 B  o2 a1 h
  4.         self.data = bytearray(block_size * num_blocks)) B5 J; ]$ a% s
  5. " [, \/ O+ Y* N3 }! F
  6.     def readblocks(self, block_num, buf):
    9 Y- v( @$ r- l5 i7 ?9 U
  7.         for i in range(len(buf)):
    6 D. R2 ^" B  U+ S6 h& \
  8.             buf[i] = self.data[block_num * self.block_size + i]
    " @" q! H. E/ Y  Z. ]9 X1 n: [) j

  9. 5 n# X3 j; g4 W7 x) W
  10.     def writeblocks(self, block_num, buf):
    0 e* Y) a6 D2 M$ ?5 e& r$ ~
  11.         for i in range(len(buf)):
    / e6 ]: F- w$ Z8 U/ F
  12.             self.data[block_num * self.block_size + i] = buf[i]
    - T) I% H) @% v# J3 \8 s: O5 X1 e

  13. . {2 L' M7 R$ a& d
  14.     def ioctl(self, op, arg):
    * v! }- d2 i5 i; Y/ U8 I/ e
  15.         if op == 4: # get number of blocks7 A  q9 D1 X- V" M# ~
  16.             return len(self.data) // self.block_size
    , \2 e0 C' C: q- N) P/ N5 Y
  17.         if op == 5: # get block size
    " g: E+ J  t" m! @+ w
  18.             return self.block_size
复制代码
: T' k5 m9 @/ `( X: G9 x! K9 @
9 ]3 b( f, Y' e2 X$ v/ h) B, A. w

" J# a( d3 P  {9 A) }8 G

它可以按如下方式使用:

  1. import os
    0 r8 T4 |8 Y6 K0 ?4 y% U
  2. * M! {6 A% Z, ?6 ]2 E8 G4 t
  3. bdev = RAMBlockDev(512, 50): Q8 j4 Z7 H: O1 v
  4. os.VfsFat.mkfs(bdev)! S7 J! A: B" t3 o  r- s
  5. os.mount(bdev, '/ramdisk')
复制代码
4 i9 V9 _5 H; Y: g

4 X, H6 |% r1 E( ?# m4 x
) q3 i7 k8 [+ c: x1 m2 ]" O3 X

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

  1. class RAMBlockDev:0 ?# E4 g* B% @( y6 J
  2.     def __init__(self, block_size, num_blocks):
    / \/ ^$ m, B# j* l7 Z
  3.         self.block_size = block_size3 j) L4 L$ B; W
  4.         self.data = bytearray(block_size * num_blocks)5 ?+ @$ b% G6 Z( M8 R, G$ S$ W

  5. 1 x! T. A0 @+ E1 T$ r
  6.     def readblocks(self, block_num, buf, offset=0):
    9 |' g3 O" z  u" t4 Y" _
  7.         addr = block_num * self.block_size + offset
    ' K6 M/ m  U( G2 x+ W
  8.         for i in range(len(buf)):+ m/ F' c$ Y7 v2 c% k7 L( t1 }
  9.             buf[i] = self.data[addr + i]: b7 z: w; n: m  `7 N0 |" _
  10. $ K; ]- C5 O& O' s' I; {
  11.     def writeblocks(self, block_num, buf, offset=None):: s% C7 r! p, z
  12.         if offset is None:
    " I) Z* }* B3 W; e
  13.             # do erase, then write
    8 S+ p; ?% `2 Z: m9 k+ {" t" |
  14.             for i in range(len(buf) // self.block_size):
    + i& U6 `% l* n/ u2 N# x
  15.                 self.ioctl(6, block_num + i)
    8 W& s( l1 d/ Z
  16.             offset = 0
    # H" y+ J; g/ Z( p" m$ m
  17.         addr = block_num * self.block_size + offset
    1 X, e9 Z9 h; t% `! K% |# _
  18.         for i in range(len(buf)):
    & g  E# A! F1 S: e
  19.             self.data[addr + i] = buf[i]
    0 c7 \. ^% _  D3 P# f

  20. * |! t7 k) O; D9 j
  21.     def ioctl(self, op, arg):
      o# Z# m  H- \  s
  22.         if op == 4: # block count9 F1 Y: K; v! ~9 F. j7 l
  23.             return len(self.data) // self.block_size0 M1 T0 q, ~" N% F' m2 n
  24.         if op == 5: # block size7 E' n0 h5 `" M4 Z2 F7 Q% b& I
  25.             return self.block_size
    3 t3 j) B) k; i; f3 y8 K  Z
  26.         if op == 6: # block erase
    ' J0 }- u) y7 n; Z
  27.             return 0
复制代码
# l: I; G1 b  B# r8 X6 F

2 v$ D- Q1 {7 c6 |$ x2 |9 a, t0 b# G8 ~- L  ]9 h8 s' M+ P. ^8 U

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

  1. import os2 s* c' M% g7 @  C- G) u! F# }4 i: G
  2. + B9 H0 I4 |; A0 ]$ I) C
  3. bdev = RAMBlockDev(512, 50), Q: F, v$ }) E) ^3 J2 v
  4. os.VfsLfs2.mkfs(bdev)
    9 \/ O5 P/ E9 [0 K
  5. os.mount(bdev, '/ramdisk')
复制代码
7 ]( c( V3 u& B# o; {+ t9 b

8 |7 T) [7 a" x/ }( U- R5 }& V
( m, c; O. v  h% m

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

  1. with open('/ramdisk/hello.txt', 'w') as f:' Q# P$ `, ^' [$ \6 X6 ~! w3 z
  2.     f.write('Hello world')9 [, p% f6 `  r  ^! L. |
  3. print(open('/ramdisk/hello.txt').read())
复制代码

% `: S/ D1 {0 G. S! D6 r: z/ B4 Y5 P' U. K/ S
0 H, E( S# C) I( L

! V) \8 o) ]6 t* F7 [# L$ G+ R" _
* K% N  }+ c: T6 Y# m( p
文件系统

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

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


! _# C4 S6 }9 {+ `FAT

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

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

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

  1. # ESP8266 and ESP32
    ) c6 A) l0 G, h! L. l- D
  2. import os
    " l! a' w+ y$ `& M* I/ n
  3. os.umount('/')5 P+ Q1 E) u5 `" v# Q+ c
  4. os.VfsFat.mkfs(bdev)
    ' S/ [" _5 C3 w8 j
  5. os.mount(bdev, '/')" x1 l+ k  E8 _$ w$ x

  6. 4 Y9 ]2 H1 t+ {' A( w1 Q! h) w
  7. # STM32
    : p/ d. ?4 c; q+ Y/ h1 T8 Y
  8. import os, pyb4 s7 ~7 R: `1 Y& {+ G
  9. os.umount('/flash')  d% _- L8 P  L
  10. os.VfsFat.mkfs(pyb.Flash(start=0))0 L6 D- y" k1 h  q8 N9 D
  11. os.mount(pyb.Flash(start=0), '/flash')$ n1 B- x! B' y" Z5 a
  12. os.chdir('/flash')
复制代码
0 {: p. N4 D! _5 W
& `, C0 S- a8 f5 R
; \& L' L2 n: J+ z% \" X% x

( n. N0 V5 v( y/ f- F2 wLittlefs

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

笔记

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


7 B+ V5 N8 o- t+ ^* W; t' D

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

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

  1. # ESP8266 and ESP320 t: ~0 k# `; }& ]. m
  2. import os
    8 o4 ]) @4 w7 o6 J& A: k7 F
  3. os.umount('/'): [4 s1 h$ |0 m! h! t. b* ~- Z
  4. os.VfsLfs2.mkfs(bdev)
    $ U; @/ d, g9 \% p# h; U
  5. os.mount(bdev, '/'); E9 r' ^! }- K4 Q5 C
  6. . g: W; f6 X+ `; B
  7. # STM32
    / J. U0 V% T+ I1 C1 q1 I3 c
  8. import os, pyb
    ) K$ J$ k6 y- U) ?9 f, ~8 K
  9. os.umount('/flash')
    / B+ n! I9 k  a! a. m1 ^& f3 F
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))0 t9 @% U1 a4 U5 C
  11. os.mount(pyb.Flash(start=0), '/flash')7 d: L, H8 q0 |0 [0 n
  12. os.chdir('/flash')
复制代码

: ]) F! P' F1 p. y  g& M5 Y) R/ M+ M+ s. P8 e  @# X0 C$ }

9 s3 _' w' v9 ~; T' a3 A) t' `( n0 [) O( R$ h% a
混合 (STM32)

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

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

  1. import os, pyb
    , e- O0 q/ @  d3 @5 Z
  2. os.umount('/flash')8 |- i/ G& }3 I  K$ t5 }6 L
  3. p1 = pyb.Flash(start=0, len=256*1024)
    ; `" F0 ~7 a( L' a6 w
  4. p2 = pyb.Flash(start=256*1024)
    % E; ]  w9 t# A9 i
  5. os.VfsFat.mkfs(p1)
    6 m: d; N- e" x3 |
  6. os.VfsLfs2.mkfs(p2), _1 N, B7 U0 `+ U  H
  7. os.mount(p1, '/flash'): i% Z! @) `+ Q3 k
  8. os.mount(p2, '/data')
    5 l+ J) \1 T/ g0 a7 p5 K
  9. os.chdir('/flash')
复制代码

+ X' x; A: E  Y/ e2 U  m% G
# |) \: M; v2 n; D8 l
0 g, h% ~& o6 a0 c1 ~! A, {

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

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

  1. import os, pyb
    + z; [* S/ U( u" ^
  2. p2 = pyb.Flash(start=256*1024)* r5 N5 [7 I1 o: d7 g+ D% {+ u& ]
  3. os.mount(p2, '/data')
复制代码

7 h7 |6 p* ?  T& N2 h& V: K6 }
4 j/ ^5 q7 m: U& ~; `9 G& v: l/ Q+ R* F- |3 p7 E

来 boot.py挂载数据分区。

& N+ j# g" p; _- S( H* {+ e
混合动力(ESP32)

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

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

  1. import esp32, os
    , ^2 x. z' j  u  z
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    $ u2 z( H) S  y& A! g
  3. os.mount(p, '/foo')
复制代码
$ X+ Z" }4 s: ~
, M& Q& }$ X7 z; F  T
( V# I1 C4 _( X5 ^

3 Q7 Y6 T+ a/ D, F. a* k
8 h! N" n9 t5 y$ v1 J) c
/ a& F( S, L2 f; G

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-6-28 09:22 , Processed in 0.171600 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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