micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 178155|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3238

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

. x8 O( D# f6 G- F$ s/ N! m9 @

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

; M- n- m3 ?# F  U: u' Q$ K3 t
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

& d+ I1 s; S9 k+ B' ]
ESP8266

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


, L+ [5 J+ U3 d) c0 bESP32

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


4 I; r- b4 c# N
% {0 _  z, X4 W; V自定义块设备

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

  1. class RAMBlockDev:
    , n: w& Z9 V3 I) }: E
  2.     def __init__(self, block_size, num_blocks):
    % j9 j% F! j+ n: I1 O: V
  3.         self.block_size = block_size- R9 @- e/ d% i+ M+ \! z+ ?
  4.         self.data = bytearray(block_size * num_blocks)
    " g4 m' x" l8 w4 T- h! _

  5. ; A1 e6 B0 C  y6 H- o+ k/ C
  6.     def readblocks(self, block_num, buf):
    8 O  @( {, Y: W* r8 ~
  7.         for i in range(len(buf)):2 F! G- i" u: B. D. d# |
  8.             buf[i] = self.data[block_num * self.block_size + i]5 J+ \6 z! G1 Y7 Q6 A
  9. 5 [' o6 F8 W. a3 c6 n+ D) h( N
  10.     def writeblocks(self, block_num, buf):. X. d% H/ {/ u5 }  o/ i& N
  11.         for i in range(len(buf)):
    . W. x6 \# t6 q* I( j
  12.             self.data[block_num * self.block_size + i] = buf[i]' d7 D8 O" B( N
  13. ) x1 B. x: F: n$ r. n
  14.     def ioctl(self, op, arg):/ @; X4 Q. ^" b" D* t
  15.         if op == 4: # get number of blocks' ~4 G" F6 O4 A0 z: L, O; T+ L& L0 d
  16.             return len(self.data) // self.block_size0 r! r( ^# B0 V  X, V: w: _7 Q
  17.         if op == 5: # get block size
    0 G( z. m- z( o
  18.             return self.block_size
复制代码
$ m8 ^4 d2 ^+ G

! ]/ ?7 }: b( f2 E. O: A) ^( `( e# }0 b( c

它可以按如下方式使用:

  1. import os6 [' m' k) M* x$ b% b- p
  2. 2 l6 |; q' ]1 m$ q0 X: o
  3. bdev = RAMBlockDev(512, 50)# T/ ~5 W5 E5 J
  4. os.VfsFat.mkfs(bdev)9 c! s0 ~, E' ^* T
  5. os.mount(bdev, '/ramdisk')
复制代码
! w7 s4 X9 j, w/ M  {% y
) X# W$ H/ g9 t/ C; R
5 m; d+ v4 i& O' B# y5 \8 Q# p

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

  1. class RAMBlockDev:
    ! H  c' o. z% S/ H' g$ c! H
  2.     def __init__(self, block_size, num_blocks):
    / v# S+ D, X3 U/ ]/ b
  3.         self.block_size = block_size
    % i$ g; M6 ?% |9 M5 I" `4 ]5 V
  4.         self.data = bytearray(block_size * num_blocks)
    3 ~3 {6 a7 X) S) n# s# h  N3 ~
  5. ) ~% d, D9 Z' |0 b! d8 d
  6.     def readblocks(self, block_num, buf, offset=0):8 P: e3 R/ B! q$ ^9 z* ^! r
  7.         addr = block_num * self.block_size + offset" x- W; k+ \/ @/ j
  8.         for i in range(len(buf)):# c6 n1 w2 s( G. |( \
  9.             buf[i] = self.data[addr + i]
    6 Q2 W4 W1 e1 {. l0 s
  10. ' f( R2 K2 o' Z( W) X: Y
  11.     def writeblocks(self, block_num, buf, offset=None):/ P8 |- m9 {! v! H6 y
  12.         if offset is None:
    2 J' _! Z1 Y  M( y
  13.             # do erase, then write9 e1 H# V5 }- x3 E- ?+ \5 W
  14.             for i in range(len(buf) // self.block_size):, r8 n: q& k: \5 Q
  15.                 self.ioctl(6, block_num + i)
    " y9 g; w* Q/ q% Y1 `
  16.             offset = 00 p3 ?' y8 k8 r9 e0 c
  17.         addr = block_num * self.block_size + offset
    1 x: o) Z/ k! o$ d  s- O
  18.         for i in range(len(buf)):" r7 d$ L, ?4 K2 d* h
  19.             self.data[addr + i] = buf[i]
    : [: I8 M4 s9 `7 Y  p" s

  20. 5 Q% K" V* g3 L9 M& D0 w2 R
  21.     def ioctl(self, op, arg):
    * Z$ q1 U0 `# d8 Y8 a8 V
  22.         if op == 4: # block count( L* J/ x! Q, s$ I) b; t  V, ~
  23.             return len(self.data) // self.block_size
    ' e0 a4 A& M' L- H# h* R4 r
  24.         if op == 5: # block size- g8 o7 z- ^2 o- q6 c
  25.             return self.block_size
    ' Z/ C9 s) O, b7 @4 t2 d
  26.         if op == 6: # block erase
    ' }3 v$ k3 |8 G9 M
  27.             return 0
复制代码
# x: \+ Z1 U0 Q. A" z! d$ p7 A4 m
: R* _1 B/ K9 T; ?

+ l) w. l+ V4 G4 ?6 B. M

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

  1. import os
    + N4 e$ f( S0 r+ w

  2. 6 v% V* U- ^. Z0 Q% t8 h  w
  3. bdev = RAMBlockDev(512, 50)0 @- {/ `: P' M% G4 B! x
  4. os.VfsLfs2.mkfs(bdev)
    " e/ `4 C6 V' {
  5. os.mount(bdev, '/ramdisk')
复制代码

% q+ y. q# p9 `& b# e. H* y( ]8 e7 S0 X$ w8 T( G
+ J% }5 a0 X( `: y+ \2 ~& f* [; d

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    - z7 U2 }6 j8 E1 ]: l9 E. l. d
  2.     f.write('Hello world')# ?1 g8 k. \' @
  3. print(open('/ramdisk/hello.txt').read())
复制代码
0 ]- C( D: [" l1 p

: e$ t3 a# }9 z* a2 U8 l. z9 e
  [: H, K: P( Q1 e  T( F0 Y0 ?7 q, e' Q  z

+ M% `+ p7 }, Z文件系统

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

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


* K. M2 \9 }2 m$ u$ [7 p5 uFAT

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

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

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

  1. # ESP8266 and ESP32
    , t7 f& T! N, h3 y8 p
  2. import os) S7 O$ V0 j* d3 }
  3. os.umount('/')3 m# P" r# S$ x/ `$ G, }
  4. os.VfsFat.mkfs(bdev)  e7 |- s0 T. B( R, O  Y3 @+ D. O' D
  5. os.mount(bdev, '/')* I( C8 Y* ?# o& g

  6. 0 J1 L9 `* y* t$ c6 K: y5 B
  7. # STM32% p; P- e+ m/ U. s& y
  8. import os, pyb3 Z/ G7 G% s6 ]4 d7 b- o
  9. os.umount('/flash')
    2 |& d+ K9 k$ }
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    ' [! L& M) Z8 K
  11. os.mount(pyb.Flash(start=0), '/flash')
    ; {) o# G% D) ]- }& C4 a
  12. os.chdir('/flash')
复制代码
5 j! M* o2 H+ G7 X) [9 S2 E, D

4 Q: l' y( h0 k5 W* ]
; R  P2 R, y! S# E& F9 j7 l, ^+ F+ x" A; N& B% M
Littlefs

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

笔记

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


7 X: d' }  M! G% v

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

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

  1. # ESP8266 and ESP32
    * `& S- v. Z8 S9 ~
  2. import os# R6 ~" S. }- v+ ~0 ?9 C. |+ v7 m$ G
  3. os.umount('/')* t7 s  p. D, T! k  h
  4. os.VfsLfs2.mkfs(bdev)
    / U* @# q6 k. g1 z4 b3 ?0 k
  5. os.mount(bdev, '/')% ?  q" p3 J  Z* ~  O, G

  6. 4 G* u) C3 S# W& m. G2 G0 G
  7. # STM32  N% h( |5 A$ x, e
  8. import os, pyb
    / j8 P8 I  v0 J8 O0 F* _
  9. os.umount('/flash')
    : s7 l4 a8 F% @* j: o- e0 t0 z
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0)), o  q- d' S: r) A1 ^' y# ~
  11. os.mount(pyb.Flash(start=0), '/flash')
    . I3 V$ X. q5 Z1 u- y* C# f8 d% Z2 h' z
  12. os.chdir('/flash')
复制代码
5 Y4 Y0 Q" [4 {) b
6 C& ?2 f$ `$ l

% p2 d! }# {% l6 s4 k+ C
2 G4 M+ }' {. x9 u8 }/ H. Y; ?& C混合 (STM32)

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

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

  1. import os, pyb) m4 p5 O1 V, \# A6 C& t& `$ [" n
  2. os.umount('/flash')
    * q9 u$ G# U0 `0 t# Z7 D. Z
  3. p1 = pyb.Flash(start=0, len=256*1024)
    1 t' A: n9 `, p: o+ d
  4. p2 = pyb.Flash(start=256*1024)+ O" N5 g7 R# p+ {# N
  5. os.VfsFat.mkfs(p1)
    ; C0 @, b, V  d* ]1 {; [$ p6 \
  6. os.VfsLfs2.mkfs(p2)
    6 g( e& b% M# k2 R5 A( u7 N0 Z
  7. os.mount(p1, '/flash')$ ~4 N* f# d4 g6 }$ E
  8. os.mount(p2, '/data')
    ( q& u  R9 t& ]( b6 n
  9. os.chdir('/flash')
复制代码

0 r: g' I- e* X( ^- [
6 ]* s  C7 x0 l. X4 r# b& k4 I$ E. ~4 s+ N$ U

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

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

  1. import os, pyb3 w& x- a4 Q1 K2 X
  2. p2 = pyb.Flash(start=256*1024)
    ( @6 Y- b/ w$ A7 d) H3 u7 P7 f
  3. os.mount(p2, '/data')
复制代码

8 T2 K% P6 ^9 |' H# p0 d' r. {/ S! v6 L4 P1 e7 n- R
  B* k  n& m' ?3 x

来 boot.py挂载数据分区。

3 |: E7 b0 Y# {# D8 W
混合动力(ESP32)

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

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

  1. import esp32, os, z; w5 g6 I5 m6 L2 ~: ]2 o3 e
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')6 c" w  w( S$ f( f/ M- U% Z
  3. os.mount(p, '/foo')
复制代码

2 e: l2 L$ i1 B" X' a" f( i! S) d$ |
8 W; ^! H5 ?5 s$ y% R8 [; E+ b4 V8 X

9 |/ a- F% g; Q0 I  b* V+ Z
4 E7 q/ [1 y5 A8 j( V0 I
% l$ g1 c, x* Z* }9 Y- u% b# X0 l' B

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-1 19:00 , Processed in 0.124800 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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