micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 186446|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3272

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

5 ~! ]6 _, F6 `( ^" a) 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 上,主文件系统挂载在 /。

% V) E6 H4 q5 U- a' I4 Y5 H- T1 ~
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


& a% h- {7 {4 MESP8266

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


- a  N% N  b5 s$ b& j- C, {ESP32

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

8 l5 n5 e! e! _6 K
% `! ?8 p2 c6 X! k
自定义块设备

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

  1. class RAMBlockDev:
    3 M: f; Y: a1 l' j, I1 P9 {
  2.     def __init__(self, block_size, num_blocks):5 X1 [5 Q/ G: B6 z. q
  3.         self.block_size = block_size/ V  ?, V' S; k4 t  f
  4.         self.data = bytearray(block_size * num_blocks)6 [+ f) d1 e0 e- y% [( P
  5. 1 r7 ^+ `/ }( f8 q
  6.     def readblocks(self, block_num, buf):; W$ p7 f6 C2 I+ u, E1 r' U
  7.         for i in range(len(buf)):* N. }9 z2 l' g- y
  8.             buf[i] = self.data[block_num * self.block_size + i]; {4 z; ^+ c) N# j$ T1 l/ E' v0 U$ Y5 s

  9. 9 ?7 ~# J; {. o; F' E: n
  10.     def writeblocks(self, block_num, buf):
    1 \! o0 a1 ~( O; t
  11.         for i in range(len(buf)):
    $ g- R4 `: J. _0 \# R  }" F# ^
  12.             self.data[block_num * self.block_size + i] = buf[i]  H& s; l- y6 h! d

  13. 3 _( V3 r( j: h1 q# h+ L: i9 v8 j
  14.     def ioctl(self, op, arg):
    , L3 b% U, M8 K0 k" C- J; @+ N; o! t
  15.         if op == 4: # get number of blocks; K- C- D: e3 D) t
  16.             return len(self.data) // self.block_size" p1 W% S1 O: @) z* u9 o
  17.         if op == 5: # get block size
    , L( d# j6 P1 J) S
  18.             return self.block_size
复制代码
/ x- \  [% \2 o. h" u" ~1 F  r, p# `

0 j3 _) w5 d. ?9 [' t* {
% r# Q  S9 H$ F, R- _$ w; p5 g- [$ r7 Y/ h

它可以按如下方式使用:

  1. import os. M+ ?/ f4 e  Y1 M

  2. ; P! d; l, M2 R4 c1 m4 s) C
  3. bdev = RAMBlockDev(512, 50)
    6 b6 k, q- C2 p# V. J; M
  4. os.VfsFat.mkfs(bdev)  K4 w8 x  g" B8 w9 i
  5. os.mount(bdev, '/ramdisk')
复制代码
3 C$ k' d! H/ \' N; N5 i

# @+ {# z2 i+ i$ }4 z* p* \1 k1 w6 V$ T- u* T1 L$ `

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

  1. class RAMBlockDev:
      R' N' r" B: H8 a! {# f
  2.     def __init__(self, block_size, num_blocks):
    . D* j7 ?% F  a/ Z
  3.         self.block_size = block_size
    3 A* o8 u* `8 e( ]( v& X
  4.         self.data = bytearray(block_size * num_blocks)
    1 S3 ~' [! y: g( l2 c
  5. 6 L- D) j8 N3 v. D6 z1 j
  6.     def readblocks(self, block_num, buf, offset=0):9 O6 l8 v; B: F% J; z8 g, \
  7.         addr = block_num * self.block_size + offset
    8 b/ f: A- P6 s, o* l; }0 v/ ?3 u
  8.         for i in range(len(buf)):
    ! U* `4 |0 i2 b* x. U+ q! f' B' t
  9.             buf[i] = self.data[addr + i]) O! [+ e2 \( r
  10. 4 A% z2 n1 w* D. q3 d/ S2 X
  11.     def writeblocks(self, block_num, buf, offset=None):$ y# [8 G  e! E3 Q4 a
  12.         if offset is None:+ e4 M3 Y) |6 X" g0 x7 h
  13.             # do erase, then write: O% m2 o5 L/ x& E3 t' |0 f
  14.             for i in range(len(buf) // self.block_size):
    - H& k% ?1 x! j& z6 P0 x
  15.                 self.ioctl(6, block_num + i). Z! K- i0 X+ V! e4 O2 Y, \2 t
  16.             offset = 0, A& O+ z* m; Y; ~; `$ o/ A
  17.         addr = block_num * self.block_size + offset" m! \2 ?- D: H0 ^/ n7 y  E1 o
  18.         for i in range(len(buf)):- {+ V8 ^" L0 S1 ~& c& I+ O& S
  19.             self.data[addr + i] = buf[i]7 T- w2 P) h  `! y6 n

  20. % \0 ]7 n  N$ H6 ?0 J0 }5 E
  21.     def ioctl(self, op, arg):' Z, X6 k; k6 ?( d
  22.         if op == 4: # block count
    4 }$ Q- |. X9 S+ I# _- [& @8 T
  23.             return len(self.data) // self.block_size
    % D; r& w0 q. I5 h2 E: P
  24.         if op == 5: # block size
    ) S3 I9 l/ b9 E8 L
  25.             return self.block_size
    / N: R+ L+ J& a3 t7 T+ m
  26.         if op == 6: # block erase5 i* }' k; d4 e  I2 g+ f
  27.             return 0
复制代码
8 N' M- R: {$ W- {. N* V
$ K; ^( @7 f# [, ?) g0 B1 s  [
2 `4 k. f* `# w6 x1 {

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

  1. import os
    7 V; ?; }' _. }
  2. - Y- X/ g6 l6 y+ w; B% t
  3. bdev = RAMBlockDev(512, 50)4 M2 p! ?8 z# C, D) H1 L: @
  4. os.VfsLfs2.mkfs(bdev)) X* L. r, `+ o. w! x
  5. os.mount(bdev, '/ramdisk')
复制代码
: N# {& q, b' L( Y3 @, ?* J4 V

& Q4 b7 v3 W- V3 q, j) b0 f% n+ q4 ~  Z. C! P

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

  1. with open('/ramdisk/hello.txt', 'w') as f:4 ]! J9 x: D" ~2 _9 q. ~$ C9 [
  2.     f.write('Hello world')
    $ y+ W# K" z2 e9 q
  3. print(open('/ramdisk/hello.txt').read())
复制代码
- r' a4 s- e  A0 D' O4 H

) ?+ Y, c+ I1 g0 L; V* C" }: S
& L3 Z5 o4 l7 M& E2 w5 J+ \
! h" q! N3 j4 [% Z8 ^2 C. w- \

# a% l0 ^3 k# `2 b6 _文件系统

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

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

5 A3 A+ ^1 ?1 m# a/ J1 L4 e) G
FAT

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

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

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

  1. # ESP8266 and ESP32# y) i3 j2 }* @3 l
  2. import os/ [- I2 f* I& U7 b
  3. os.umount('/')
    ) c' j& e: c. p+ l, X: J! j
  4. os.VfsFat.mkfs(bdev); v# o9 }7 b/ S7 Z0 r# ]
  5. os.mount(bdev, '/')
    8 a+ P, C  q& {

  6. ) l) R9 A, A' v4 r9 A7 ^1 t
  7. # STM321 R* z. U% u5 w* B% [0 E7 c
  8. import os, pyb
    8 [6 l# D9 U: Y3 r7 X( [4 R" q
  9. os.umount('/flash')
    5 g1 `4 n" _& V7 X5 ?
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    ! A6 N# V( P* ?) Z4 R; E
  11. os.mount(pyb.Flash(start=0), '/flash')
    % P9 J5 A  {. n" N
  12. os.chdir('/flash')
复制代码
# N: R' o: Y* @( z7 n* b( n) f
" m$ D2 V. g. j& K2 m8 j, R

; h* H$ a1 i( `8 s+ g# i; ?( {- ]; ^* m1 H. [
Littlefs

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

笔记

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


# l5 r1 I) u3 R3 Z3 u

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

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

  1. # ESP8266 and ESP32
    : S  T1 m: x/ P- n- o, H
  2. import os5 \3 P" ]" Z. x9 _. W! V4 S( n% G6 N
  3. os.umount('/')* ^/ r+ m6 l$ Z4 L! a) N
  4. os.VfsLfs2.mkfs(bdev)
    ( T/ N1 f# t/ k$ `
  5. os.mount(bdev, '/')) Q  O; q! W2 s0 {5 K, ?

  6. 2 g* x9 v9 C. W1 h* j3 g1 x0 H
  7. # STM326 e7 n5 k" ~+ j4 X* j+ N
  8. import os, pyb+ z" `' Z  `. ^0 g/ Y4 B& \: t
  9. os.umount('/flash')6 ^0 A1 u$ U. s7 h5 f- L) a9 N
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    0 }! q+ }/ f+ G& c
  11. os.mount(pyb.Flash(start=0), '/flash')4 T5 V; g6 {- M8 g- G* F
  12. os.chdir('/flash')
复制代码
% H* f% }3 ?8 ~2 z9 V

- p7 l5 \* \# D' F) G& g: S! U' V" L: h. `  ]' w
+ h2 s3 J% |( n" W; ?
混合 (STM32)

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

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

  1. import os, pyb
    4 C# s$ Y5 F  Z; W
  2. os.umount('/flash')
    # I. Y. ?3 v, l1 \( U
  3. p1 = pyb.Flash(start=0, len=256*1024): z) `3 T8 M9 Z  g1 s0 X
  4. p2 = pyb.Flash(start=256*1024)! }: f, a9 V2 p8 h: s, F; s
  5. os.VfsFat.mkfs(p1)
    1 Y+ g- U2 H5 R* g3 b
  6. os.VfsLfs2.mkfs(p2)
    ) V/ N7 E1 _7 V1 p
  7. os.mount(p1, '/flash')
    0 n& b0 h% R% U' k  \. e/ \# l) A
  8. os.mount(p2, '/data')
    $ W  V" B. M9 y- |0 B
  9. os.chdir('/flash')
复制代码

( N9 u6 y* b* S: o/ E  U, V9 w
$ \/ P. ~2 x9 {' p) C- S" m2 `# a
6 t6 J. j7 O0 z- j# C! U

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

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

  1. import os, pyb
    - N1 Q% |- V/ y, w5 u
  2. p2 = pyb.Flash(start=256*1024)
    1 A# i( S7 l; F5 }6 K
  3. os.mount(p2, '/data')
复制代码
$ l) g+ M5 x$ [. E- q( j) N2 J

/ s1 \+ _; ]5 o7 y1 a* y* Y7 |) i: m" \8 V1 g

来 boot.py挂载数据分区。

1 M8 s6 S5 p/ r" }+ \
混合动力(ESP32)

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

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

  1. import esp32, os- j1 a7 [: L. B# d' b+ W/ z
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')- ~) ]4 I% l1 I/ w# D
  3. os.mount(p, '/foo')
复制代码

, |7 m- _3 @# }
0 r5 o  S( [$ H! f6 F3 Y% u! ~( ^4 f0 \1 \+ T' m6 W
- b& Q- \  f+ l) L5 G

# c, Q9 |1 I2 M+ }
2 y" Y3 I/ V" `: \

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-15 05:54 , Processed in 0.202800 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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