micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 188517|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3276

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

1 ^4 N1 C: T: @4 `5 C* S

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


; }6 A; ?$ S' H1 E" O% ^1 D+ b块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


/ o8 ]8 `5 Y' `( \" W, ~ESP8266

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


' |1 j4 |( X- t: vESP32

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


* L- x: Y' f, Y
3 [0 T1 n* i7 h# e8 @( d- f自定义块设备

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

  1. class RAMBlockDev:8 Y6 [" }) K5 b. m
  2.     def __init__(self, block_size, num_blocks):
    & f' Y% @, r9 }# s( S& I
  3.         self.block_size = block_size7 x0 u& ]7 k+ |# t7 Y
  4.         self.data = bytearray(block_size * num_blocks)
    6 Z# [9 A1 s5 Z0 E3 d' C  H
  5. ! b, b% H5 f, L  `' T
  6.     def readblocks(self, block_num, buf):+ Z1 f) \; |3 F4 x# m! p
  7.         for i in range(len(buf)):4 B. `. {7 }' \
  8.             buf[i] = self.data[block_num * self.block_size + i]
    & f( F, Q+ u1 H

  9. + L8 M1 `6 n/ N$ c1 L! X. x2 u; D% `
  10.     def writeblocks(self, block_num, buf):
    / R' b% W( b- s" j
  11.         for i in range(len(buf)):( g0 t+ K' K* `* k  Y
  12.             self.data[block_num * self.block_size + i] = buf[i]& K! @2 x' Z7 m. Q

  13. ; ?: [2 b& ^" l& W/ Z" `+ I
  14.     def ioctl(self, op, arg):
    8 X' W6 l1 U' R. S
  15.         if op == 4: # get number of blocks+ B8 B: B3 d; ?$ A
  16.             return len(self.data) // self.block_size
    ; M% v7 z8 H3 s+ ~! n: N/ t
  17.         if op == 5: # get block size
    3 `+ f) K$ r9 S2 C. d
  18.             return self.block_size
复制代码
5 Z4 [. E! d% `& U) b3 v: r5 a

3 X0 w8 E& A4 t6 [8 @5 |7 a0 ?
2 U! X. L8 O9 r1 }

它可以按如下方式使用:

  1. import os
    # y' L* M( S& {- q- T8 J  |- C
  2. 7 K/ c; E. @& h- T! F. s
  3. bdev = RAMBlockDev(512, 50)
    1 n. z, |; R" r. L6 C, ~* _) M
  4. os.VfsFat.mkfs(bdev)
    : V% C. ]! x6 v2 O5 B
  5. os.mount(bdev, '/ramdisk')
复制代码

& C! B7 L' P$ L  D* A1 h+ H; B6 ?
; I3 g) J- P. B$ O/ c5 p
: t  [( `' V# B5 v: e

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

  1. class RAMBlockDev:  q( y9 U, A- ]4 x4 b
  2.     def __init__(self, block_size, num_blocks):
    & i% A. b. t1 \* M$ K  Q! S9 [
  3.         self.block_size = block_size
    % B9 d0 B. \9 `" _1 w
  4.         self.data = bytearray(block_size * num_blocks)0 G( J, X9 w& h# g3 f7 j

  5. & ~: u! \6 X3 f; K, G# f" @- ^8 p1 d
  6.     def readblocks(self, block_num, buf, offset=0):# y* f  h% K) ^3 B
  7.         addr = block_num * self.block_size + offset
    - m5 t% U$ z1 K9 t
  8.         for i in range(len(buf)):! a& d* P9 l7 E: I3 E: d
  9.             buf[i] = self.data[addr + i]# I( q+ i$ M0 A+ s3 k

  10. # p5 I5 F, _$ f7 q- U, y; b' J& o
  11.     def writeblocks(self, block_num, buf, offset=None):" R2 N! d& m( ?& D
  12.         if offset is None:
    . ]' R1 j3 `' e9 K+ x9 D% l" f
  13.             # do erase, then write
    % x8 ]% J* \) p3 G& o9 @) ^) C# Y- Y
  14.             for i in range(len(buf) // self.block_size):
    : z  B& n  a( C5 N6 |- U3 ]* v# s3 ~
  15.                 self.ioctl(6, block_num + i)
      X, N1 V/ {* E8 k+ n& l
  16.             offset = 0
    * y7 J- z: E  \
  17.         addr = block_num * self.block_size + offset& E( i+ E; o. B* l' ?1 C' H0 M
  18.         for i in range(len(buf)):$ ~' j7 [" P2 p  A4 ^2 H' a/ L
  19.             self.data[addr + i] = buf[i]
    ) b6 z* I/ J+ H& Z$ P$ d
  20. ' w1 M7 i) v" Z* t1 N
  21.     def ioctl(self, op, arg):
    0 L% R# r; r) p
  22.         if op == 4: # block count* c! J' L8 Z1 }. \6 c" h
  23.             return len(self.data) // self.block_size
    $ v+ ^! k) @; e2 B
  24.         if op == 5: # block size  q: H- ~4 l4 o0 g
  25.             return self.block_size
    " B4 i, g% X+ D- P/ `" U0 Q  W) n( U
  26.         if op == 6: # block erase
    7 m. z( j! P' C
  27.             return 0
复制代码
3 b3 P  A6 s, }: o; v+ E. @7 X7 r3 W/ b

: M. r! N' E# I8 h& V: e2 n, }, z- t" C# S! I

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

  1. import os, l) [4 \0 Y: c" a

  2. # Z, F5 B6 S( w; n* s
  3. bdev = RAMBlockDev(512, 50)
    ; k5 G4 c# }4 o7 _2 Z! U) @
  4. os.VfsLfs2.mkfs(bdev)
    + ~: K: {, b$ s; ^7 }5 S
  5. os.mount(bdev, '/ramdisk')
复制代码

$ r- ^6 G. o; P0 Z0 @
4 Z& |1 @& _$ Y2 h' A7 R  ?. \; ]
3 J( ]/ Q1 ~& ~; e% p; L  @

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

  1. with open('/ramdisk/hello.txt', 'w') as f:9 y, G' [+ d1 ?9 A
  2.     f.write('Hello world')0 m2 V+ l' h6 P# I3 E, g8 [3 m
  3. print(open('/ramdisk/hello.txt').read())
复制代码

1 C9 N! j: Q; `+ A/ x  L6 e4 K$ J& w4 J# z3 S  `( E+ Y

( Z2 T) [# o9 K7 A' c5 z
4 |& |" P& z9 j
& i% @9 n2 u4 @2 p' `! r
文件系统

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

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


- w. {/ O; S$ u+ L# DFAT

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

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

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

  1. # ESP8266 and ESP32
    - C9 |0 D: G. h* a$ w
  2. import os
    ( i6 F0 d8 _( Y8 y
  3. os.umount('/')6 n6 x* a* r5 m# V, \6 t
  4. os.VfsFat.mkfs(bdev)7 s6 `) U6 u. X9 c, w2 J
  5. os.mount(bdev, '/')' ^, [  J, F, V8 E2 W# x
  6. / ~/ r  C) s1 \
  7. # STM323 c; d6 o2 }( V- L4 F/ N  Y0 a
  8. import os, pyb) d$ h4 U6 {* s* ?' z
  9. os.umount('/flash')# p1 Q+ B( i1 S% x+ J; f
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    9 ]- f+ \0 r# f8 ]
  11. os.mount(pyb.Flash(start=0), '/flash')8 d+ U. e  q/ E2 d
  12. os.chdir('/flash')
复制代码

, M- [1 _: ~- z( d  b5 @1 G+ D. g, `
2 ]* e6 m* T2 X( W2 h& z
$ d1 Z9 S# ^( r4 g% M) L7 j+ G. b6 C- E1 V( w7 B. a3 d
Littlefs

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

笔记

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


; Y6 l4 a* M& |3 Y

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

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

  1. # ESP8266 and ESP32
    ! H) d' ?# E! W( ?8 N6 E3 @
  2. import os
    1 N% k2 O- o; X$ q3 s; [
  3. os.umount('/')
    4 ]% q8 |6 L6 ]8 c( [
  4. os.VfsLfs2.mkfs(bdev)! O& ~2 [- _1 j8 Z! Z3 @
  5. os.mount(bdev, '/')
    # d# b0 _% x# K8 {, K! i0 x. ?
  6. 8 r; w: x. ~7 l8 p; x4 A: I
  7. # STM32
    7 |' l- Q% m, P. s! P; j2 r  Z4 P
  8. import os, pyb
    . H- V0 p/ a) q0 Y* `
  9. os.umount('/flash')$ B9 b5 L1 o% o' e
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    5 G. x5 K1 c7 B* Z/ n
  11. os.mount(pyb.Flash(start=0), '/flash')
    ! y- ?  d2 |+ x8 E# ]* R1 k$ Y
  12. os.chdir('/flash')
复制代码
6 [* X0 W* J  s

0 N0 J- [; w& m( ^0 U! h5 d0 C4 C4 L' w* L) f& ]
, b& P/ Y$ J9 X2 s. e( ~; ]
混合 (STM32)

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

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

  1. import os, pyb' _& n4 V4 y# A$ f
  2. os.umount('/flash')
    % f& ?2 o, j* l6 t& u6 ]3 e
  3. p1 = pyb.Flash(start=0, len=256*1024)# @. N6 S& p: \& Z
  4. p2 = pyb.Flash(start=256*1024)* Q0 U5 Q9 {- I4 E/ I
  5. os.VfsFat.mkfs(p1)# L# T& v0 N$ k8 n2 R9 r
  6. os.VfsLfs2.mkfs(p2)
    # P( L8 J3 A9 v
  7. os.mount(p1, '/flash')
    5 U. i2 Y, J' r9 i: b, D$ t" P
  8. os.mount(p2, '/data')2 l* n7 z  a: _+ Z
  9. os.chdir('/flash')
复制代码

) M5 r$ M3 m' P! n6 `
( a2 f: S; q5 D7 Q8 P, H" l/ {' O" M
7 L9 I' m3 `# }' g1 d

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

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

  1. import os, pyb
    1 T/ `; O  @9 L0 Y. W' }0 g4 m
  2. p2 = pyb.Flash(start=256*1024)
    2 P0 D0 ?. A0 Q7 N* B3 Y$ {  k
  3. os.mount(p2, '/data')
复制代码
* u. b# l7 F/ o3 @1 }7 F; L

! R3 g1 \* ?8 m& k- F) H: e9 C- A2 I$ J

来 boot.py挂载数据分区。

" X) E# `6 I4 @. p5 M
混合动力(ESP32)

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

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

  1. import esp32, os# s0 i* m$ s6 P8 \/ }; |
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    $ Y$ Y' ?- u- F
  3. os.mount(p, '/foo')
复制代码

& l* M" e$ n7 p5 v! h; ]3 {
0 L% y9 Q' ]0 _9 x- \. C
4 z: ?4 V  y7 K! i7 ^0 ?% z% @& x, a" A4 V; a6 a0 ?+ n& ?: K
3 K& k7 v& f  g: j% m0 L
8 ^3 R+ s. Q3 F6 \5 W8 n

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-19 08:18 , Processed in 0.187200 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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