micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 188504|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3276

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

! g% Z9 g8 v, l+ B: E

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

) }7 u6 f" j! S, Z" e/ N: b) d
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


. V2 F  i- _2 N# {& V. GESP8266

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


% R7 _0 o2 ]: m; @ESP32

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


  Z/ x2 j! k7 y* I- v, `3 K* Y1 \% K$ v+ I
自定义块设备

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

  1. class RAMBlockDev:
    ( N% z- E. r  ^! v
  2.     def __init__(self, block_size, num_blocks):
    . A) G7 w9 W- O6 C, j# E$ R
  3.         self.block_size = block_size
    7 n' ~+ z' r3 k
  4.         self.data = bytearray(block_size * num_blocks)
    : D) v% o* `* f/ \

  5. 7 L' b6 }7 T; N, r2 ?
  6.     def readblocks(self, block_num, buf):5 d8 k( w8 V2 f8 t2 ]3 g6 C0 h- ^! ?
  7.         for i in range(len(buf)):
    ( i, ^# T+ X" t2 K" h
  8.             buf[i] = self.data[block_num * self.block_size + i]' `! D. V! |/ M& u

  9. 2 Q/ @6 u, A9 U( s: ^7 j$ _' m
  10.     def writeblocks(self, block_num, buf):' m% v! }, D, J0 H: |
  11.         for i in range(len(buf)):
    9 B! S$ Q: o6 Q" E+ n  Z" k
  12.             self.data[block_num * self.block_size + i] = buf[i]
    1 Z: R' q9 R, @
  13. % x$ e  `$ X. T+ Y7 O$ z4 ^8 u
  14.     def ioctl(self, op, arg):
    8 q5 ^7 V0 c9 ]' d1 P
  15.         if op == 4: # get number of blocks$ M1 e9 ?# Q/ K+ c
  16.             return len(self.data) // self.block_size
    1 D: W+ P2 |$ `  v& U: G5 [4 _
  17.         if op == 5: # get block size& m* }2 }8 n: X/ B) q, ~* o
  18.             return self.block_size
复制代码

8 {- i" R+ w' Q- P/ U  e
) {  W0 I  M* D3 h$ e9 E1 J5 ]. O% s
! ?& P  z. Q8 i, q: \3 F

它可以按如下方式使用:

  1. import os
    5 g4 N% u, ^2 S/ b( a$ X

  2. $ d* j* R' {* Z7 d# f
  3. bdev = RAMBlockDev(512, 50); b7 ~$ ]' [7 V) \2 ?
  4. os.VfsFat.mkfs(bdev)
    ! ~3 g2 x9 d9 n
  5. os.mount(bdev, '/ramdisk')
复制代码

" }* F9 G5 P* z) q4 R# T3 R  [# Y2 Q; w/ r% ?8 X& M& J5 x

6 \- {+ \2 n7 L. _

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

  1. class RAMBlockDev:! \4 C% i8 j5 t, I; s; J' X
  2.     def __init__(self, block_size, num_blocks):
    ! |/ J9 k2 m7 H$ v
  3.         self.block_size = block_size1 D8 d" L* Z- Y, _7 R
  4.         self.data = bytearray(block_size * num_blocks)' p' k+ Z. ?% M2 z( |6 {' p- y# m. b
  5. - r- V3 M" X: l# f
  6.     def readblocks(self, block_num, buf, offset=0):$ H9 E9 z6 r( r9 C  W. [. S- O) b1 e( \
  7.         addr = block_num * self.block_size + offset, X4 K# Y. b9 f+ \6 {
  8.         for i in range(len(buf)):
    - t. t- b8 S0 @8 B( ~
  9.             buf[i] = self.data[addr + i]
    ) O4 D8 N0 m0 t" A3 P5 @  k- u$ U7 B
  10. 7 q$ Z& Y7 a3 U3 _
  11.     def writeblocks(self, block_num, buf, offset=None):
    5 \1 X, @/ T, }% ~, ?; ]6 I7 \
  12.         if offset is None:5 Y: ~; e" A# {# ?5 I2 D- ]9 w4 Q: J
  13.             # do erase, then write+ U: ^4 t# e6 I, o' B2 g& o7 h
  14.             for i in range(len(buf) // self.block_size):1 w7 J: i2 {5 W; g* w% k
  15.                 self.ioctl(6, block_num + i)
    % y" o3 x& `- e  h( c
  16.             offset = 09 ?( ^8 @& l$ ~3 y% k2 D( c3 K" J
  17.         addr = block_num * self.block_size + offset3 e$ s* |/ }3 X' |- ^4 S
  18.         for i in range(len(buf)):
    9 i2 l  y: C, d8 l: F
  19.             self.data[addr + i] = buf[i]
    / W- Y8 B3 e3 N0 \' Q$ [
  20. 1 \$ a7 l. ^# I- V) T" }8 i
  21.     def ioctl(self, op, arg):
      `0 N, H' H2 G" z
  22.         if op == 4: # block count
    0 z- ?9 z9 U6 h% n6 \
  23.             return len(self.data) // self.block_size3 \" m. f7 F; y) F( R4 ^3 `7 s
  24.         if op == 5: # block size; O9 i; v( G" @" }
  25.             return self.block_size7 z, w6 }: d4 q+ r! ?: q& i
  26.         if op == 6: # block erase
    * w5 u* t4 [5 P4 X: h+ P
  27.             return 0
复制代码

' D  q( ^0 P* D% _/ Q* v; a4 k) G& A4 M: d% g2 }/ ]% A* \/ K! Z6 Z
/ f! X# L" V1 W

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

  1. import os5 Z. l# i8 ^% Z& T- e

  2.   v/ H% J# R: P& p
  3. bdev = RAMBlockDev(512, 50)9 q+ j4 H5 c4 J+ n
  4. os.VfsLfs2.mkfs(bdev)
    # U9 @! r, h9 v
  5. os.mount(bdev, '/ramdisk')
复制代码

. Q/ Z/ B2 k8 R' Q: K/ f4 A+ j9 t/ I7 S3 {

/ c5 P1 y8 J/ N8 \& H' B6 ]# ^

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

  1. with open('/ramdisk/hello.txt', 'w') as f:/ y4 f$ C0 v% e1 {
  2.     f.write('Hello world')
    ' k  Z  Z* o$ n3 j, x6 T+ c! k
  3. print(open('/ramdisk/hello.txt').read())
复制代码

! l, R( t# W! ]/ d) p# \. J% e
; t% N; m! l3 g; G3 F: v6 m9 \- j; J

" a8 o# I8 h" M2 L
) v9 _$ \7 `: D  q6 C. U
文件系统

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

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


; \% V: l/ x5 }FAT

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

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

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

  1. # ESP8266 and ESP321 F7 [  |4 F  b+ Q: ?" `  w  h3 c
  2. import os( b4 D* h2 ]- [# [9 _# z5 Y4 G
  3. os.umount('/')
    & j: S* ?3 f% {6 P2 q" I
  4. os.VfsFat.mkfs(bdev)/ f8 w' I# t$ k5 |. Z0 B2 Z
  5. os.mount(bdev, '/')' J3 `- r/ Q- }+ U
  6. : L! n7 t1 {' r' C" M: Z; M; l6 g
  7. # STM32
    6 D' g8 P6 S  _6 g! W
  8. import os, pyb& I  M5 [3 L* [9 I, q0 z" x* F$ D
  9. os.umount('/flash')6 o& h5 _+ y* [/ w# y1 F0 m
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    5 j" [9 g+ J& y  h7 y
  11. os.mount(pyb.Flash(start=0), '/flash')
    ! X0 ?& Z' Y; v" m/ r" ?
  12. os.chdir('/flash')
复制代码
" Y0 G. H; V; g; o1 ?

8 W8 x- C# G" N9 T8 a' U' c# o, f$ ^& }% s+ s8 y
+ g; f* H9 [7 q( o8 Z
Littlefs

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

笔记

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

% V+ X- C' v' l

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

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

  1. # ESP8266 and ESP32
    % Z8 X" q; x! K' D4 k5 z4 }
  2. import os
    8 U6 C1 e. p( J: f; ]2 K
  3. os.umount('/')
    & m9 @+ E5 J; A. h! }% E
  4. os.VfsLfs2.mkfs(bdev)
    6 t$ U6 I: y5 Z7 l/ r
  5. os.mount(bdev, '/')
      U8 A( [% a4 r, Q& u
  6. 1 d! X& L; ?% X) E) V
  7. # STM32" O) n4 b5 g6 M, F
  8. import os, pyb% G% A+ b" |0 s7 j* o; s
  9. os.umount('/flash')6 S8 z' u% \# F1 E
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))5 A, F$ n# R2 P1 k8 i
  11. os.mount(pyb.Flash(start=0), '/flash')
      V  P' b& J6 n' b! g2 _
  12. os.chdir('/flash')
复制代码

1 ^7 [+ d6 f1 Q& H7 S4 t0 r; {" ^( `2 ?5 z0 |! D  W$ D# k. S
" K& m. x4 k* |3 [
. I+ b. w* C' M  p
混合 (STM32)

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

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

  1. import os, pyb
    2 i- Q' M( P# _' \( U
  2. os.umount('/flash')
    0 `1 F+ s4 Q; t8 [8 K
  3. p1 = pyb.Flash(start=0, len=256*1024)0 X* W& n/ _. m! n& a
  4. p2 = pyb.Flash(start=256*1024)2 z9 t2 j* u/ m* b4 t& j) c. s. u
  5. os.VfsFat.mkfs(p1)2 U) b" q& q' J% o
  6. os.VfsLfs2.mkfs(p2)
    * [% s3 @! }5 \* g7 k& _- q
  7. os.mount(p1, '/flash')
    * z0 I5 m6 e" s( ~( W
  8. os.mount(p2, '/data'): G6 H: ]+ Y) r, u: a- v; H
  9. os.chdir('/flash')
复制代码
7 \9 l% X- R  R' h5 q

+ \7 E) b& Q$ |: h! s2 \# h4 |& e$ |; x

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

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

  1. import os, pyb
    9 J. v1 ~9 E+ E4 B/ h1 J
  2. p2 = pyb.Flash(start=256*1024)' P# m, `# ?! x0 {
  3. os.mount(p2, '/data')
复制代码
9 P- _* f2 J+ x
9 s, f5 C9 R* U* r0 \
& g# g5 h' |0 J' `! ~, C

来 boot.py挂载数据分区。


! r# c# g7 S6 H& _混合动力(ESP32)

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

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

  1. import esp32, os
    2 H- `% S$ |& K( {' V2 [
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')6 k: U0 V8 g+ ~2 y  S
  3. os.mount(p, '/foo')
复制代码

4 N" B" w" j; [: x. z. x* L& q) `$ M/ @
4 O. B3 k/ Z6 i5 \9 H. l
- T, W4 |9 a; I& _4 W
/ {$ w$ k1 ~+ v' q

! k) V8 ?: ?* [- F0 H; _

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-19 07:48 , Processed in 0.171600 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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