micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 185472|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3270

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


* Q+ {& O( b3 g8 h% _) 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 上,主文件系统挂载在 /。

$ i- }, G! C8 R) A+ T" p% t- }
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

  X) s" w8 G' c; w2 W& n7 J
ESP8266

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


" F+ C8 B; W4 F: OESP32

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

  o, s' r( f8 }( X
, u% ^+ G, |  W
自定义块设备

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

  1. class RAMBlockDev:8 N6 l7 I2 s' W" F% F' D' q
  2.     def __init__(self, block_size, num_blocks):
    ' ]0 m' n. r' F3 t, b
  3.         self.block_size = block_size
    : S: N# l4 A; @2 t7 n, t
  4.         self.data = bytearray(block_size * num_blocks), o- p) `, w* M, X5 B* p
  5. 1 Q+ ~3 I! A/ v! W; M
  6.     def readblocks(self, block_num, buf):
    7 y) Q3 W/ m% K# p2 q' z
  7.         for i in range(len(buf)):
    9 d6 z* l! q" p* T
  8.             buf[i] = self.data[block_num * self.block_size + i]& |0 V4 e& z7 m: [8 }

  9. 1 [% v. k0 z+ y
  10.     def writeblocks(self, block_num, buf):  v& v1 r" H3 Y  x( x1 f
  11.         for i in range(len(buf)):+ \8 W4 S4 }- _2 n, Z! F* p
  12.             self.data[block_num * self.block_size + i] = buf[i]
    : }. c4 y) q: g0 |& Q, V

  13. & }$ q& H) }. Y0 c2 Z( ~! j
  14.     def ioctl(self, op, arg):
    + q0 r  A. H# \+ ?8 _! k
  15.         if op == 4: # get number of blocks4 o3 ?5 T. r  p, j
  16.             return len(self.data) // self.block_size, }8 D7 G8 [* d( P. I$ Q( ]
  17.         if op == 5: # get block size  w. q$ n; J% k+ Y7 F8 ~# i
  18.             return self.block_size
复制代码

* ~  h# v' K; k: Y1 S$ b( f& s0 n) C  }( C; q

) B- I7 h4 ~6 \- w: V

它可以按如下方式使用:

  1. import os% h* |8 u/ _3 p5 ]( ^

  2. 9 K9 W7 o0 Q8 b0 v9 B9 }8 A% V
  3. bdev = RAMBlockDev(512, 50)3 i9 c0 T$ w- w; q  d
  4. os.VfsFat.mkfs(bdev)9 y  P' I; U4 a, j. h
  5. os.mount(bdev, '/ramdisk')
复制代码
/ g! P9 I. S' A
( p. r3 j& m3 `' ^/ V5 H$ K  l, H: X
3 `6 }! Y% H/ q5 B" n

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

  1. class RAMBlockDev:
    / i4 h: R; m1 k, D! i" S" Q
  2.     def __init__(self, block_size, num_blocks):
    0 `8 e) y( @4 @) ]8 L0 f5 \+ G
  3.         self.block_size = block_size  a6 X; A9 B, W: Z2 a: Q
  4.         self.data = bytearray(block_size * num_blocks)$ W8 P3 F: x. r8 T8 D+ p/ N9 z8 \
  5. 9 L+ U/ N0 _) a' A
  6.     def readblocks(self, block_num, buf, offset=0):
    6 `' M. @) n# `/ M
  7.         addr = block_num * self.block_size + offset- d" s' i. A4 W- G9 r) h/ {
  8.         for i in range(len(buf)):
    9 z* p/ |% z; J5 f7 }: D
  9.             buf[i] = self.data[addr + i]. E4 ?" \1 o: y. r! P; M2 p

  10. : _* A9 G/ B; I
  11.     def writeblocks(self, block_num, buf, offset=None):& f! ]# _% c  K% \3 }  j6 b8 B' s& R
  12.         if offset is None:
    3 X$ {0 W8 V' n1 |- g; K$ X6 ^
  13.             # do erase, then write7 G  \, w) O  A1 N/ E/ @( ]
  14.             for i in range(len(buf) // self.block_size):$ O  M/ `/ _" M: D, o' L) P& y7 T2 R! E
  15.                 self.ioctl(6, block_num + i)1 h. S. j, H. V+ V9 S# J& R  q
  16.             offset = 0: ]# }5 O' ^2 a5 R! G
  17.         addr = block_num * self.block_size + offset
    9 a2 {9 Z: ^) `' F) s- D$ s
  18.         for i in range(len(buf)):
    - _4 m1 I* N% o  u4 t
  19.             self.data[addr + i] = buf[i]. e8 P  T8 u+ r9 E

  20. , r1 G8 R; A, D
  21.     def ioctl(self, op, arg):
    0 D: o0 ?. g& t) w6 |! L
  22.         if op == 4: # block count9 l7 R) F: V* B" N
  23.             return len(self.data) // self.block_size
    / Q) H' W6 @% M
  24.         if op == 5: # block size3 |4 b$ r# O$ i4 x
  25.             return self.block_size4 Y* [8 M# d8 ]3 D9 a2 M+ y8 A
  26.         if op == 6: # block erase% C5 \* c2 O  c6 X
  27.             return 0
复制代码
/ m6 ~8 X; ?" Z" J) D9 G
5 ~) s8 h9 Q- G& h  x" H  T

8 u, v: _, B; F. b- P6 F. C! M

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

  1. import os
    , K; S4 O; m, a' l- @

  2. . P- A6 o5 q: d2 \/ P
  3. bdev = RAMBlockDev(512, 50)' i0 o2 E8 z7 G: l+ L: x# i
  4. os.VfsLfs2.mkfs(bdev)" |% N' [: n4 ^7 }: N6 j3 h% G
  5. os.mount(bdev, '/ramdisk')
复制代码

- Y% F" y; W- K& ^+ m# N+ |* c' j/ U- V6 p8 ?* {
6 E% M4 \/ g+ r8 ~' X/ i# J6 O

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    . a' l: k; W5 a
  2.     f.write('Hello world')
    0 E- B  n- _2 H0 X, ~
  3. print(open('/ramdisk/hello.txt').read())
复制代码

: W, S: p8 B# N3 V/ y9 }* L' Q1 V* K" l% `

5 ]9 p! l7 Q0 m, k* r4 C6 t( j5 w! I( `! H: k6 z2 h- d# v" J, ?/ S
6 c! e/ w9 v; J; h
文件系统

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

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


8 d' g" p+ m* K/ b& AFAT

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

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

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

  1. # ESP8266 and ESP32" x! v% J1 _3 _7 `) \8 v9 O( {
  2. import os
    : g1 U; ]& a/ n  }
  3. os.umount('/')" D& ]3 f; R$ L
  4. os.VfsFat.mkfs(bdev)
    , c/ \6 t' X" B3 B3 y5 E2 m
  5. os.mount(bdev, '/')# [* H: C8 W; J9 @# D4 I- P

  6. 9 w* G9 f! I) ]! r
  7. # STM32
    % d- h( e2 S! Y) }
  8. import os, pyb9 Y/ M4 j7 `( {+ q# P
  9. os.umount('/flash')
    2 m  |( d. o% a: q+ Y( S, }
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    ! T- Z* t' _) F9 Y/ i9 H9 S6 b
  11. os.mount(pyb.Flash(start=0), '/flash')% ?2 _# b* E% P+ G* h7 r
  12. os.chdir('/flash')
复制代码
* L) L- M" E: w- U9 _0 H8 J" J

' _  c  t2 I2 T$ _
1 j" @5 k& b* f: ^. X- g6 c5 z/ u. b, J3 A& r3 Q
Littlefs

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

笔记

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


! G  \( g: T& e

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

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

  1. # ESP8266 and ESP32& |. H+ d$ {% T" H/ |
  2. import os
    # g% V4 k# ?* D; w" T
  3. os.umount('/')
    % `- Z- k- r& J' p2 g
  4. os.VfsLfs2.mkfs(bdev)9 @( w$ N6 Y' _& k4 v
  5. os.mount(bdev, '/')" d4 L$ E" @& J9 j
  6. . U' P1 m  F( c" Y& K0 z
  7. # STM32* g7 b- V& N8 K' N  v2 S2 `8 u
  8. import os, pyb- O0 q" v9 R0 R3 W2 ~
  9. os.umount('/flash')
    / _4 H/ c% P) u6 X# V
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))" I( {+ S% D5 ^- I) T! r
  11. os.mount(pyb.Flash(start=0), '/flash')4 h$ y: y1 ~: \& f# O! P: w
  12. os.chdir('/flash')
复制代码
7 q6 f8 O5 }; Z  o! N) U
9 O6 T9 u% ?+ d- X
5 O3 z0 t* ]* g
/ [- V1 C; G1 i! h& O  l& N
混合 (STM32)

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

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

  1. import os, pyb
    1 n3 Z. T9 r/ M4 a
  2. os.umount('/flash')" s7 m( O% Z* {3 Q9 G
  3. p1 = pyb.Flash(start=0, len=256*1024)# w$ A, w8 ^" P1 z5 ]9 M6 n+ \% I
  4. p2 = pyb.Flash(start=256*1024); d7 T* Z5 I6 z9 j
  5. os.VfsFat.mkfs(p1)
    . b3 g& {& t4 w% R% k
  6. os.VfsLfs2.mkfs(p2)) ]3 Q! u: X' ^6 \/ K& U
  7. os.mount(p1, '/flash')  y% C) G/ k0 F  L4 M2 x
  8. os.mount(p2, '/data')
    $ n( W: L9 L4 \, z+ n  h  k7 b
  9. os.chdir('/flash')
复制代码
1 P% w2 V3 o. Z$ d9 m. X8 r1 d( s* f

+ |1 \+ _" N! P- j! Q
5 [1 z5 U6 A5 a; F& g: M1 w% {5 T

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

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

  1. import os, pyb
    4 }, y2 J0 d8 U& Z+ F  U- i
  2. p2 = pyb.Flash(start=256*1024)9 f6 f1 q6 I: G+ f  q( G
  3. os.mount(p2, '/data')
复制代码
7 D! s1 `* [" S- b
# C/ b& T  l6 \

" E3 W4 {  k* p1 w! z

来 boot.py挂载数据分区。

1 |9 ?% `( {9 N  ?2 C. P
混合动力(ESP32)

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

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

  1. import esp32, os: }7 M! M% l; K3 {! `' [
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo'). l& x: ^/ @. \4 c3 q
  3. os.mount(p, '/foo')
复制代码

& Z7 d9 M2 y: W! m3 i8 B8 b
0 q5 h/ u) f8 Y2 x8 y) j5 M2 \+ \+ e+ g& M( O. J

0 A7 B. v3 a- g# ?# c0 w. u8 h7 G
6 s" ~( a6 @$ R* e! I5 f

6 b( a! x9 w% s, o4 O& Q) a) [/ m' j

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-13 09:54 , Processed in 0.187200 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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