micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 77523|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

2704

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


8 a& F4 n- r' x% O2 m5 D

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

% j4 {: a+ a# h- u: N5 p. ]4 N
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

7 V" _% D7 k, ]9 Y
ESP8266

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

* W  R$ \9 T  G0 p0 [
ESP32

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


" v5 E; _0 C8 M  [3 K2 N0 O
7 |- O4 `9 g: u2 e自定义块设备

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

  1. class RAMBlockDev:
    % C- X, L% W% n0 G
  2.     def __init__(self, block_size, num_blocks):
    ' e- G/ C' H' R! V) s
  3.         self.block_size = block_size, y$ H. X) z6 U8 v1 L% z( h
  4.         self.data = bytearray(block_size * num_blocks)
    ! C# B5 d. q5 e7 I

  5. 7 \1 N0 I' l$ u) F! @* d
  6.     def readblocks(self, block_num, buf):  L5 E7 `3 g5 c, g! Q) q
  7.         for i in range(len(buf)):
    & M7 J8 k/ w$ y: r" E
  8.             buf[i] = self.data[block_num * self.block_size + i]5 X9 B7 a3 _* b, \2 O  l
  9. * O" Q$ ~, p! _, |0 X4 n
  10.     def writeblocks(self, block_num, buf):
    % i8 P, r1 P4 b$ e0 D/ N
  11.         for i in range(len(buf)):
    ! J5 [2 |) {2 c' e8 d
  12.             self.data[block_num * self.block_size + i] = buf[i]
    6 G2 Y1 c( J4 P/ S2 |: r# t, e
  13. . `) J! z9 L! t( T+ c
  14.     def ioctl(self, op, arg):1 ]2 x: p8 }1 N) W
  15.         if op == 4: # get number of blocks
    8 D$ O2 T( L+ X' x0 }- O$ K  u, Q
  16.             return len(self.data) // self.block_size
    1 @0 R: [5 z0 x7 X2 k
  17.         if op == 5: # get block size
    2 ~) o/ Y8 ^* d5 R3 i0 Z3 g4 Q
  18.             return self.block_size
复制代码
  x5 V% Y) R. H( h/ j
/ R4 q  @( c7 n' x! S' L+ C

$ `  z" u* l" Q  B: A4 F) h

它可以按如下方式使用:

  1. import os
    ) s; t  ]- g8 S$ j8 c
  2. 6 X6 u' V1 z9 V- ^( n
  3. bdev = RAMBlockDev(512, 50)
    * w  z, ], X- M+ K
  4. os.VfsFat.mkfs(bdev)- F$ o- F3 R( }7 t& W0 ?. Q8 Z
  5. os.mount(bdev, '/ramdisk')
复制代码

! H7 ^" K4 d! x( f8 W
, `) V5 i& R/ ~! X+ |
) f7 U, A# V% Z8 @9 W# n

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

  1. class RAMBlockDev:
    % _& C# C& p; k* G! v
  2.     def __init__(self, block_size, num_blocks):) Q: p% @+ g1 x; o
  3.         self.block_size = block_size# W' c( I- A5 M
  4.         self.data = bytearray(block_size * num_blocks)1 T4 [' D4 F- T4 g

  5. : U" ?5 G0 M) j1 |9 Q% X0 G3 H% J
  6.     def readblocks(self, block_num, buf, offset=0):) O* F6 o7 y  S0 Z/ w
  7.         addr = block_num * self.block_size + offset- u" ?8 j' a: q( @* `
  8.         for i in range(len(buf)):
    1 ~  U" l4 N! R. R
  9.             buf[i] = self.data[addr + i]( G/ Y- G! t5 R/ q

  10. ) J" V7 `3 }! \  \0 R5 t
  11.     def writeblocks(self, block_num, buf, offset=None):
    $ t; `: d+ F1 {/ Q- j( n+ ?
  12.         if offset is None:
    ! T. m6 i! r" ?" V) F9 ?9 U. g
  13.             # do erase, then write& p, D& U$ G! X
  14.             for i in range(len(buf) // self.block_size):! C& q; S( _4 e) c2 v$ C: _
  15.                 self.ioctl(6, block_num + i)7 n3 \) u. ~  o1 A
  16.             offset = 0& p3 U# c) s5 t% B. G/ O% K2 P
  17.         addr = block_num * self.block_size + offset
    7 d8 E0 {; y5 B+ a. _) e3 v" U9 G
  18.         for i in range(len(buf)):( ?3 p- J" s/ ]/ A, }! D/ l
  19.             self.data[addr + i] = buf[i]# C; {& h; r4 U

  20. 8 x4 P; j! a! t+ O
  21.     def ioctl(self, op, arg):' v8 s9 Y8 @1 y6 Y. `# f
  22.         if op == 4: # block count6 P) Y8 T5 Z7 W$ s
  23.             return len(self.data) // self.block_size
    ' V: G0 l+ v  x3 U- m# ^
  24.         if op == 5: # block size
    $ f1 P* k8 M" `3 t0 F. N6 _
  25.             return self.block_size
    9 x& G0 l; L  B) n, v0 Y& f3 @0 A& t: x
  26.         if op == 6: # block erase
      ]5 W0 X- i, ]( t( o
  27.             return 0
复制代码
. g$ k9 u. d' ?2 H8 e1 |

( x) ~0 `* J, a; @) E
+ t/ L" m5 D7 W2 n# U

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

  1. import os
    4 O& K/ Q# i4 g1 h; L$ D
  2. : O; W% A' u# P( j4 h* Y: \
  3. bdev = RAMBlockDev(512, 50)
    2 v5 K0 |5 D% L' @7 p$ S
  4. os.VfsLfs2.mkfs(bdev), l4 X2 c% M! H3 c5 @
  5. os.mount(bdev, '/ramdisk')
复制代码

9 `8 K! s" T7 W4 h" W3 P
5 h+ M* e( G' z. ]4 ^7 h/ u
/ S8 v; s, ^  ^: W

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

  1. with open('/ramdisk/hello.txt', 'w') as f:* z4 D3 H& _( I4 o2 ]$ C
  2.     f.write('Hello world')5 @4 s7 `: C! j/ D; ~& [
  3. print(open('/ramdisk/hello.txt').read())
复制代码
! J1 T( o- [2 F) t/ S5 J3 s; P. b

1 G' N! M* |: n9 u5 Y2 N2 i3 N& }, T
( M3 P5 F& [) ?

- h- H7 m) v& O& a+ w  j7 F文件系统

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

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

+ k4 }& |7 n7 u8 K( y
FAT

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

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

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

  1. # ESP8266 and ESP32& P' D( o6 W  l' y+ t  F
  2. import os
      o# \: M' H  e% m8 y% X
  3. os.umount('/')" B' @7 L. @) e! C* {
  4. os.VfsFat.mkfs(bdev); B- E) y" Q7 `2 g6 C
  5. os.mount(bdev, '/')
    " W& _. i! e; a4 r; y

  6. / x1 \/ u4 _* D# C5 |
  7. # STM32) L1 c6 w9 l0 L& a, X& j" [. `$ E
  8. import os, pyb
      o6 Z# w1 g( [, J; y4 j
  9. os.umount('/flash'): [9 K" F/ f' A& M9 ~/ k, Y
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    . S- H% h1 C, i' W; F5 m
  11. os.mount(pyb.Flash(start=0), '/flash')
    & M" J( ]8 ?7 S  ~& u
  12. os.chdir('/flash')
复制代码

8 S" [( Z4 m/ _
& x+ O) W/ n* ?; p" n& h* B9 E5 W1 V1 w

4 B9 {0 M( M1 R3 R9 f% ]" p6 I/ iLittlefs

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

笔记

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


: p7 m5 I# G- u7 p7 M$ [5 s* ~" q" ~

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

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

  1. # ESP8266 and ESP32
    + s4 |9 G0 n- p0 z
  2. import os
    & e) q  S" Z6 i7 c. a$ d
  3. os.umount('/')
    3 [2 P- T/ t5 ~% _: J& A
  4. os.VfsLfs2.mkfs(bdev)- P" c5 ~$ v. l9 v4 e6 [& i6 n, n
  5. os.mount(bdev, '/')' k! `/ v9 i! T: O' ?: k6 C

  6. 6 q2 X* u; l- r3 O: p
  7. # STM32
    ( k4 n0 D! s( |" g% Y
  8. import os, pyb
    & o9 f8 x- B! p9 `. H
  9. os.umount('/flash')" {4 t$ O* O( ]7 E' m* ]0 C& u% t" j
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0)): }3 M5 p! T2 A+ _# T2 |5 Z  Y
  11. os.mount(pyb.Flash(start=0), '/flash')
    ) i! l5 U- {( z- e5 H  a3 C( g! K
  12. os.chdir('/flash')
复制代码
+ v* t; e# f* p, ~) E* G, l

( X) ^& f; ~" D' Y6 [$ R; o5 v0 E" [0 \# R: N( u/ P* G; a- Z

/ O% I  @0 M& |. {; w; i混合 (STM32)

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

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

  1. import os, pyb
    % `  \. [( T9 e/ W( x2 N& [; L
  2. os.umount('/flash')2 Z  V2 g$ P! e8 }+ Z9 B* a
  3. p1 = pyb.Flash(start=0, len=256*1024)
    4 u. k5 Z+ ^. s1 Z; h6 y; Z) y
  4. p2 = pyb.Flash(start=256*1024)
    ' Y) s1 |+ U$ J0 n4 o
  5. os.VfsFat.mkfs(p1)1 q9 Y% C, E. h- |. G5 P
  6. os.VfsLfs2.mkfs(p2)
    ! N5 D  I6 F- Q/ U
  7. os.mount(p1, '/flash'); Z/ h4 C3 O5 I- ~1 L0 k
  8. os.mount(p2, '/data')8 r: r+ E& ^" j) M  j: Z
  9. os.chdir('/flash')
复制代码
1 f$ y0 M) F9 R

2 V# a4 @/ k+ ]6 B- k
8 c+ M9 x& M: i: ^) w8 ?9 g+ Z

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

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

  1. import os, pyb% h2 T; E! [3 {1 ^; H5 l
  2. p2 = pyb.Flash(start=256*1024)  F' O; k+ |0 W. I! R
  3. os.mount(p2, '/data')
复制代码

' ^! c9 K! ]) b# e. Z' C3 Q. \) H- H- @1 ]/ E  U! ?
% q: {/ ]* m% i  I) d

来 boot.py挂载数据分区。

" ]( W2 g3 m+ j# v; }
混合动力(ESP32)

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

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

  1. import esp32, os+ y8 U' [- R; Q9 I+ d
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')$ }. u5 h4 s$ ?0 U( ?6 b2 G
  3. os.mount(p, '/foo')
复制代码
) P. M4 }9 G$ \7 E; f
& Q+ e+ K6 N8 O

. u, T$ w3 k! P5 l8 o* L; i
5 g& X" L( r7 ^/ a6 ]
# \: E4 h$ c  T8 A
* a! r9 D9 X3 a  m- K  y

本帖子中包含更多资源

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

x
回复

使用道具 举报

0

主题

2

帖子

4600

积分

新手上路

Rank: 1

积分
4600
发表于 2024-11-25 10:14:39 | 显示全部楼层
回复

使用道具 举报

0

主题

2

帖子

4600

积分

新手上路

Rank: 1

积分
4600
发表于 2024-11-25 10:16:31 | 显示全部楼层
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2024-12-5 03:28 , Processed in 0.187200 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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