micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 178856|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3240

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


. N, |: f. a. r  _9 r

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


# o. q2 A( n) h  Z, ^% S% h* M0 v/ K块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


( v( ~0 {4 c3 L8 m4 I) ^ESP8266

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

3 u" E7 V& {! t0 [1 L; r- J  O2 D1 W
ESP32

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

7 n( q( A: _5 f7 K

, f- B5 r: W0 l7 c自定义块设备

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

  1. class RAMBlockDev:8 a  F% P# \2 _0 F
  2.     def __init__(self, block_size, num_blocks):+ A8 A6 F/ H! u9 S6 X; a* q
  3.         self.block_size = block_size
    , M4 ^0 X0 u9 S6 {' D4 y! l
  4.         self.data = bytearray(block_size * num_blocks)
    8 {+ f" C4 I2 Q# ~) ?4 C1 c2 i& E

  5. 7 Y2 [- t* j6 ?3 e" [
  6.     def readblocks(self, block_num, buf):
    . n% O0 i/ `* I0 }0 Z
  7.         for i in range(len(buf)):
    : @  P- M* v. }
  8.             buf[i] = self.data[block_num * self.block_size + i]& _% J3 F$ Y5 m6 ~  M2 a% G
  9. $ J( I4 K6 O# ^4 j; F2 R* j0 t
  10.     def writeblocks(self, block_num, buf):
    1 U$ v8 S8 j" ?  \6 w" q
  11.         for i in range(len(buf)):
    0 n  x: C- W' ~
  12.             self.data[block_num * self.block_size + i] = buf[i]; l, A' \( z) e" U% @
  13. . g. H0 V$ V# [3 u" ]( s% Z
  14.     def ioctl(self, op, arg):
    8 E1 k. F7 k2 V: r* G9 {
  15.         if op == 4: # get number of blocks
    : B" s# ?5 e7 e
  16.             return len(self.data) // self.block_size& ?) ]$ B2 c- {0 A& |; k& V
  17.         if op == 5: # get block size: L% I  t1 p9 f& i. b/ R: c
  18.             return self.block_size
复制代码

% G2 S  P( ^" J7 s) u, c! @6 a
* _: [$ e, L* \& I1 q  A

它可以按如下方式使用:

  1. import os
    1 I( ~% Z- ]' x! E7 ^) K7 m

  2. 0 `6 W* a7 g1 z& S2 c& A# @
  3. bdev = RAMBlockDev(512, 50)2 u# U3 _1 q, C5 N) u! N
  4. os.VfsFat.mkfs(bdev)4 l' C; O8 u7 ]+ d( l5 g' m
  5. os.mount(bdev, '/ramdisk')
复制代码

( e! A# p; r# ]7 m1 S
& T, ?  S" ~% H
) f  J* }0 e/ b3 v. d5 a8 X1 S

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

  1. class RAMBlockDev:
    $ h7 ?+ V; X, @& j
  2.     def __init__(self, block_size, num_blocks):
    2 ~& e7 z  n1 W% B" j* p+ I1 G
  3.         self.block_size = block_size
    # W: K7 m) c# q% X1 j" P
  4.         self.data = bytearray(block_size * num_blocks); H# ]9 z8 F: z' w

  5. + s4 j! C1 S5 t4 t6 M
  6.     def readblocks(self, block_num, buf, offset=0):- n  ^, c( U. S+ ~- a+ s$ F' D
  7.         addr = block_num * self.block_size + offset7 [9 a) N2 ?( _$ _
  8.         for i in range(len(buf)):# C1 I9 R) ~& Y. K& I2 W
  9.             buf[i] = self.data[addr + i], S7 I/ p9 `/ a/ A$ U5 U

  10. 9 G7 A$ p0 r2 ?6 x$ Z0 E4 h
  11.     def writeblocks(self, block_num, buf, offset=None):
    % c5 q; s% w: A) Q7 O& h
  12.         if offset is None:: i- b+ m0 A. ]2 \7 K) H
  13.             # do erase, then write
    * P! r* K1 W! R2 n0 k
  14.             for i in range(len(buf) // self.block_size):$ C: k9 W. `" R; r' }. y- |
  15.                 self.ioctl(6, block_num + i)2 z- f: n2 ]& i$ K/ T- P# Z
  16.             offset = 0
    2 S2 t$ e" h- |' u
  17.         addr = block_num * self.block_size + offset) @9 ]% H" [- d* f
  18.         for i in range(len(buf)):0 ^# D( l& N- Y7 N
  19.             self.data[addr + i] = buf[i]7 W/ G( o' C, ]2 r! h

  20. 8 _* d' X2 L6 |" m
  21.     def ioctl(self, op, arg):
    * p6 c# X; l' E* M
  22.         if op == 4: # block count9 f! ?1 T! v9 x9 O" f2 f
  23.             return len(self.data) // self.block_size
    4 n) {% v5 {) B
  24.         if op == 5: # block size4 L) U  C7 Q/ H2 a& d
  25.             return self.block_size
    $ n8 c' Q- x( a/ u+ T! D) r
  26.         if op == 6: # block erase
    ( I8 R7 X1 x+ e9 v
  27.             return 0
复制代码
/ i+ K6 q, _9 C9 X7 S: }
' b) c3 `/ w! ~5 R# S6 D; ]

# z) D% i5 O; k% w) f$ x1 i

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

  1. import os, v( P* F( W; A9 ]/ f( t' n
  2. ! W& h! P4 V) N9 k$ O0 w0 K
  3. bdev = RAMBlockDev(512, 50)
    : m" Y9 L) j/ i2 K- W0 I
  4. os.VfsLfs2.mkfs(bdev)+ E* v; t7 d% l" N2 U6 f. F! g1 ?7 [( J
  5. os.mount(bdev, '/ramdisk')
复制代码

4 {: N& A0 y* e* t7 p$ T( N5 ?- n( U  N1 e* I% g3 F- j

" g6 Z# `: t: q) \6 {+ H" S, m4 A

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

  1. with open('/ramdisk/hello.txt', 'w') as f:# v, d8 e% I- f; U- r6 W+ A: P
  2.     f.write('Hello world')- m8 K$ i6 B9 t( _9 g9 _: G
  3. print(open('/ramdisk/hello.txt').read())
复制代码

7 f: c' R; ]5 i: G/ r; {1 G2 d
, `! [- D" C3 i/ f1 [( d! p# f
8 x3 L' W) o& i8 [; D  J2 B
* o' a  e7 Y8 H
/ I( [) @5 X) i" j. ?5 ^# s
文件系统

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

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


' P7 P1 r& \5 \& ]0 w5 y2 {* YFAT

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

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

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

  1. # ESP8266 and ESP32
    5 I9 k0 F9 I+ z% [2 x
  2. import os; v3 V" p5 E& [% I6 D3 c
  3. os.umount('/')
    + ~  \+ S$ u' N
  4. os.VfsFat.mkfs(bdev): h9 f6 i0 J3 s" ~# N' F/ A
  5. os.mount(bdev, '/')
      z( {4 R2 c) J

  6. 5 O; }5 A  A# S. A
  7. # STM321 G% K+ D$ t9 J, l( g4 I. D
  8. import os, pyb
    5 ^+ C# U6 q3 g$ i2 r* s& ?
  9. os.umount('/flash')( t. D( n/ D1 S! L' J: p
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    3 x6 N5 ~5 J9 W1 T# e1 d; W$ ^
  11. os.mount(pyb.Flash(start=0), '/flash')& G- f, _' t. X* o7 _, Y
  12. os.chdir('/flash')
复制代码
$ J' ?) [$ C, u7 v) K4 n6 j
" ?1 J" P' u# p' V2 |! f  S
! Y7 E1 L5 D, r3 |& r! @

  M6 H0 F4 X* @+ \Littlefs

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

笔记

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


7 N; K" }2 }+ Z

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

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

  1. # ESP8266 and ESP32
    ! U; {4 Q" N6 _: ?/ t3 D4 w
  2. import os
    : w7 @! U! }3 ~' s- `
  3. os.umount('/')8 V  L1 t: V) g. ]
  4. os.VfsLfs2.mkfs(bdev)
    ; q  j6 ~, p% _# \; a
  5. os.mount(bdev, '/')6 f$ Y& U4 i1 s6 o

  6. 1 P7 A. S  T" Z2 g
  7. # STM32" J) X: G, k' Y- N5 g7 x
  8. import os, pyb* O' B' v) |7 y3 r! C' }
  9. os.umount('/flash')
    # k6 t# v1 G& J. Y2 ]5 S
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    ) `' n+ {3 I; M" q3 @
  11. os.mount(pyb.Flash(start=0), '/flash'), ^/ s( z  V! N
  12. os.chdir('/flash')
复制代码

# N. ^/ Z- p; c! i& F9 S( i& G& z' r( x( V4 G. k8 E

8 \6 [, u  c: U  g! w3 p. @4 U2 b$ w" ^4 r& u8 \# J
混合 (STM32)

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

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

  1. import os, pyb
    ( e  m* t# I+ ^4 r2 p4 ^
  2. os.umount('/flash')
    6 b* ?" q/ U8 x9 n
  3. p1 = pyb.Flash(start=0, len=256*1024)
    # Q4 z( W, S( C% ]
  4. p2 = pyb.Flash(start=256*1024)/ i$ ]8 m/ C2 k
  5. os.VfsFat.mkfs(p1)! o) ~; T" n" z; v3 p+ L0 A% c
  6. os.VfsLfs2.mkfs(p2)
    # X5 D' R% F! R$ L: }. `# j; \
  7. os.mount(p1, '/flash')
    1 H, o6 o% C: n9 T6 s
  8. os.mount(p2, '/data')
    # t5 ^( k! W; ~9 R$ u
  9. os.chdir('/flash')
复制代码

- g/ x; v; O8 q4 `  E8 s9 d" [& j) {+ k8 ~0 V, G! l' g0 B
, K6 d0 j6 p, o$ V( ]* G* Y; x

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

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

  1. import os, pyb$ E  Q4 X) r, _( W7 n, [7 ~5 f
  2. p2 = pyb.Flash(start=256*1024)& o9 T5 R6 A. w1 _3 I! n3 \$ O
  3. os.mount(p2, '/data')
复制代码

/ U% M% |, @) e1 X! G) S& Z- U8 ~' q" h& y1 q8 C4 \
# R9 K$ D: n. C( c- }. L

来 boot.py挂载数据分区。


  u) ~' U4 B5 @, G: A( E7 Q混合动力(ESP32)

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

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

  1. import esp32, os
    7 ]4 @6 L$ f& X: s1 f# N5 p
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')! n: s8 @4 ?2 X; `
  3. os.mount(p, '/foo')
复制代码
3 ?6 ?* M% d% p5 e

' {( h) f' p5 N  b4 i4 K0 l0 v$ l2 D. ~  ~; R: w9 W

/ b2 q/ e0 ]9 w$ {$ `+ \4 D

" w, r- L3 y, H) d! g$ a. `/ J& Z( @: s* e

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-2 13:58 , Processed in 0.171600 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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