micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 77388|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

2698

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


& R# V9 j6 ^$ a- y

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


) `! x0 @2 [$ i& h块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


  d9 o: I! D8 gESP8266

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


4 F% J! e, D+ S  y! f9 JESP32

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


. b( s& }. n" Q. k5 E( W# V9 y4 m% l9 M8 x; S: V& [$ q3 g* x' [6 t! g
自定义块设备

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

  1. class RAMBlockDev:; ]; g5 _) C% w
  2.     def __init__(self, block_size, num_blocks):1 n' {* J% Z$ ^) o' \
  3.         self.block_size = block_size* e0 W! u  ?$ n1 [4 f2 i7 G
  4.         self.data = bytearray(block_size * num_blocks)0 ^- _( n7 o! c/ |/ I1 t

  5. : S( a( K5 T; f2 d: t* k0 \1 _. L
  6.     def readblocks(self, block_num, buf):6 @7 ~2 I  u" }% D
  7.         for i in range(len(buf)):' o/ t( K8 {: ?2 h) [+ t
  8.             buf[i] = self.data[block_num * self.block_size + i]
    ! V5 R3 s8 m$ I& z6 q" o
  9. 5 b3 f3 z  `4 ?
  10.     def writeblocks(self, block_num, buf):
    % {+ ~% Z+ D- w
  11.         for i in range(len(buf)):
    6 y' f& z& R1 i+ }
  12.             self.data[block_num * self.block_size + i] = buf[i]
    % G" k2 ^; x; j) _' N, v
  13. 5 h; |) u. t7 f0 V. `1 {
  14.     def ioctl(self, op, arg):% S3 Z7 Z! }! m) x
  15.         if op == 4: # get number of blocks
    / W" f6 O( p2 t: w
  16.             return len(self.data) // self.block_size
    & C; Z8 U# r. T% H* y# W) L# ^  _2 G
  17.         if op == 5: # get block size
    # i$ q. J% H+ Z" a# M
  18.             return self.block_size
复制代码
& A! s/ c+ i2 H, R
& S1 j0 w; Y& a
" B9 j/ j) e8 x

它可以按如下方式使用:

  1. import os9 Q4 v& B5 G! z2 c9 P
  2. 6 U5 x' P, b) D) W! W
  3. bdev = RAMBlockDev(512, 50)/ t4 z" C4 D; ~; x  H/ U
  4. os.VfsFat.mkfs(bdev)+ i9 N+ n4 K9 k; Z" I/ A
  5. os.mount(bdev, '/ramdisk')
复制代码
2 z/ e/ ?5 ^' q

4 O) l4 y; |3 o. s* U4 @4 }" h% Q, I# K. W. v% v" P! u- Y8 \

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

  1. class RAMBlockDev:
    ( a& z; I2 r  x; J: a8 e0 ], t9 t
  2.     def __init__(self, block_size, num_blocks):
    / w  q7 t/ u+ y1 w, j8 `
  3.         self.block_size = block_size
    # _( |( {9 V" a9 w
  4.         self.data = bytearray(block_size * num_blocks)$ F- [5 D' ?2 `7 j7 s% ^' G. M

  5. * Q! T1 ?1 W% n! v* I, E3 W. L8 _
  6.     def readblocks(self, block_num, buf, offset=0):
    6 c8 Y5 |2 h# w2 j# E" y  s) [5 X/ n
  7.         addr = block_num * self.block_size + offset
    . f2 @! z. `2 d
  8.         for i in range(len(buf)):
    % L% H( v4 @: Z/ d2 V; b- W7 l& Y
  9.             buf[i] = self.data[addr + i]
    2 p/ r" K- {+ [0 c- m5 R+ Q

  10. 9 I6 _4 f( y8 T  p' R+ E$ @
  11.     def writeblocks(self, block_num, buf, offset=None):
    ' I! y7 T' @' U/ O  @
  12.         if offset is None:4 z9 l; p0 A1 w- {! r0 Y
  13.             # do erase, then write
    : ]- k" Z$ c) G6 a3 y
  14.             for i in range(len(buf) // self.block_size):
    4 u  A3 ]0 n9 i* {' }7 l
  15.                 self.ioctl(6, block_num + i)
    8 P0 H3 q8 R% q( L/ u
  16.             offset = 0- Q! x0 S$ o* E1 i
  17.         addr = block_num * self.block_size + offset
      r$ x8 k& l, v, ]8 _
  18.         for i in range(len(buf)):
    3 }6 @+ a8 f, ?* x: v+ ?/ Y( \
  19.             self.data[addr + i] = buf[i]
    5 b  h- _4 B! p: a' }5 D' D
  20. : ~' m! E8 x& }% \2 F6 P7 O" @
  21.     def ioctl(self, op, arg):% R& r1 ?7 G, d' I  \( J
  22.         if op == 4: # block count( u5 X: |5 k) w$ C- V- r" l4 r9 N
  23.             return len(self.data) // self.block_size
    & ?3 c0 X& g5 I8 V2 F, G) I
  24.         if op == 5: # block size
    ) s  T6 `% R! r; {
  25.             return self.block_size4 L% m: _! l: j' }- H; Q  k. m
  26.         if op == 6: # block erase! Z5 @  @1 q, N1 x# w& d& r
  27.             return 0
复制代码

7 t* H7 V3 r) ~3 q' P# d
! m, N; d$ W' k/ ^: Z, p8 h. S6 h$ M$ M9 Q  F. l2 P/ s. p

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

  1. import os  G5 B8 }- M# J5 `0 ~& Y6 O
  2. 3 X  R8 z& p1 I8 f) K$ M$ [- U* Z
  3. bdev = RAMBlockDev(512, 50); V9 l1 J$ V2 s! s$ k; k# }
  4. os.VfsLfs2.mkfs(bdev)$ c8 X+ h1 S) l9 P. F9 w
  5. os.mount(bdev, '/ramdisk')
复制代码
( I' F( m0 N- `; {  w
/ L0 S: O: r9 q/ \% G
) O% |9 d# c0 Q! @' a3 l

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

  1. with open('/ramdisk/hello.txt', 'w') as f:( W7 U. w: I2 Z+ L6 f
  2.     f.write('Hello world'), e, z. x. X; a! M1 e
  3. print(open('/ramdisk/hello.txt').read())
复制代码
, m( L! Z! u# k3 Y  b
" T2 n7 f; ]' e! D) C0 [* U

6 I  {% g  a( p1 K  P" E6 ?6 U; Y  m$ M- |1 A- `5 N) P7 f

* x$ Z% l% F0 R/ Y2 b5 a7 C文件系统

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

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

  J* d( a5 H" g- y
FAT

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

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

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

  1. # ESP8266 and ESP32
    . M. z$ I; O" w) w5 H1 U8 [" c6 `
  2. import os
    1 r8 l- d! \5 V& ~# c8 g
  3. os.umount('/')
    , j, Z0 Y& J) X" E6 P0 b" H0 r
  4. os.VfsFat.mkfs(bdev)
    8 V( V8 Q. P# B' Y$ i% ?/ a* R
  5. os.mount(bdev, '/')  _0 A. C2 O" s2 v& ?- t, B- ^

  6. * g+ C3 R; A9 W2 h9 g7 q6 y' G
  7. # STM32
    / h4 q9 ~, k9 V2 Y8 P: v- C% i
  8. import os, pyb
    . D) z# u7 U; Q( B9 x
  9. os.umount('/flash')  F' @* p; s1 c
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    : |8 b) y% n% ^2 Y; ~% n
  11. os.mount(pyb.Flash(start=0), '/flash')
    ! r9 s( r# p/ Y$ l3 a' v! p* h
  12. os.chdir('/flash')
复制代码

  d1 i( p9 c. N1 f5 q% s$ k% z, I" X- g# F; y9 g$ t% L* ^

/ K9 G8 B, C8 F- ^
: E& C$ A7 l$ p0 z  y8 |Littlefs

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

笔记

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

2 P3 Y! _2 w. g+ H

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

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

  1. # ESP8266 and ESP32
    " c, @+ H2 `2 d6 H: p, }1 C, M) O& n  C
  2. import os
    2 b: t; z8 m" U8 @
  3. os.umount('/')
    + X, E; g3 F' L/ j$ ?
  4. os.VfsLfs2.mkfs(bdev). }2 Y: e! \# f1 v& v) F; p
  5. os.mount(bdev, '/')
    7 i( M5 h, |) ~: w% q. q7 d

  6. * c; @0 ^' T3 `8 ^4 a
  7. # STM32
    $ o8 v: ]% g% c/ ]' W3 H4 Y( ^
  8. import os, pyb
    - `8 |+ q% ?7 h  w! x$ x) w& h/ P
  9. os.umount('/flash')* P) B+ u; M( e1 w3 k0 G: X8 X
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))' n4 F% o* E. y* Q- i* F# R
  11. os.mount(pyb.Flash(start=0), '/flash')) n; f: f$ [5 N/ f6 B% M; D
  12. os.chdir('/flash')
复制代码
( x1 `# D/ l8 W9 t) W

! G0 q" ~! u6 F8 J5 I% |- Y
, e8 O; J7 U; I4 c% r9 ?) L
) O- L' B- d. B, Q5 S混合 (STM32)

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

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

  1. import os, pyb
    9 u7 O' K# ^2 R1 M2 V
  2. os.umount('/flash'), c9 d! p7 ~: C6 S
  3. p1 = pyb.Flash(start=0, len=256*1024)1 U6 F3 e6 \8 u3 r# D: e( S  E5 P$ Q
  4. p2 = pyb.Flash(start=256*1024)
    1 \. p; Y2 K' Y# X6 r
  5. os.VfsFat.mkfs(p1): p2 W9 P. M9 f, v- Y& Y. H
  6. os.VfsLfs2.mkfs(p2)" m: I0 n( z+ _+ Z4 X
  7. os.mount(p1, '/flash')
    1 H- t7 ~) H) P$ |0 l
  8. os.mount(p2, '/data')7 y' M4 {$ d! o0 @0 I% a$ o
  9. os.chdir('/flash')
复制代码
6 g0 j6 E8 M5 D: h

& j) Y5 r! Z; ^# M. j3 D+ B) @* f' z! m3 U# a

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

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

  1. import os, pyb5 z- D1 I) U% }' L5 d5 _
  2. p2 = pyb.Flash(start=256*1024)" t% S* |9 \: y
  3. os.mount(p2, '/data')
复制代码
. h! p' J7 m6 P8 U. O
# G2 m/ Z& I4 R2 p2 D

! o0 t, K. d2 r4 J- {/ H

来 boot.py挂载数据分区。


, ?. @8 P! R! L2 X% s$ N- Q- A混合动力(ESP32)

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

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

  1. import esp32, os3 k$ Y' C: C! Z/ M" Y
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')) V& m; o+ N- N* i
  3. os.mount(p, '/foo')
复制代码

1 f! V4 x- {! G, j, h7 _# y" g: J8 {+ r. N1 w
% L! F& Q; `0 n0 H  f' X
- W1 C8 ?7 i6 R

* M$ Z: R; X, [( d! y
* k: G7 ]$ V9 s

本帖子中包含更多资源

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

x
回复

使用道具 举报

0

主题

2

帖子

4600

积分

新手上路

Rank: 1

积分
4600
发表于 2024-11-25 10:14:39 | 显示全部楼层
回复

使用道具 举报

0

主题

2

帖子

4600

积分

新手上路

Rank: 1

积分
4600
发表于 2024-11-25 10:16:31 | 显示全部楼层
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2024-12-4 16:57 , Processed in 0.156001 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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