micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 44590|回复: 0

使用文件系统

[复制链接]

24

主题

24

帖子

2354

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


$ W0 Y; T9 M5 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 上,主文件系统挂载在 /。


4 j  R4 t, E5 P! n3 Q! ~- x6 i块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


1 K2 g+ f( C% M' d1 B9 B# ]ESP8266

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


% x5 t& v6 f) m  g* v* eESP32

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


3 q; |  D3 d/ l5 \
" ]0 F* ^' @+ m$ q8 _0 q" U自定义块设备

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

  1. class RAMBlockDev:9 e6 w$ B3 T# y6 I' o7 E
  2.     def __init__(self, block_size, num_blocks):! I% d$ t6 a, v8 ^+ i( k# W
  3.         self.block_size = block_size& F' d; T7 [/ F! a) {5 b
  4.         self.data = bytearray(block_size * num_blocks)3 b2 j/ T/ \. K3 b" N+ d. `/ f
  5. 4 I( r; Q$ A' t+ C7 c3 M
  6.     def readblocks(self, block_num, buf):; f1 T& S% ^" \: j/ Z, l
  7.         for i in range(len(buf)):
    + c+ F3 x- C: O' n/ @& v
  8.             buf[i] = self.data[block_num * self.block_size + i]
    1 G( T' L- \; r' Q5 j

  9. 0 H. T' w! n# T0 x" E
  10.     def writeblocks(self, block_num, buf):6 l1 ~7 U. m4 W
  11.         for i in range(len(buf)):% s9 o% s' q1 o) c7 b2 O
  12.             self.data[block_num * self.block_size + i] = buf[i]
    4 u: z7 ?, G  D% Y! g

  13.   f/ ~% }" k$ P; [+ i
  14.     def ioctl(self, op, arg):
    / r, R2 ^1 G& b% I# U4 r
  15.         if op == 4: # get number of blocks- S  D2 ?" U, |- ~( t; x( c( l! t
  16.             return len(self.data) // self.block_size
    5 n( B. A( ~: G( \
  17.         if op == 5: # get block size- o6 |" r5 n$ l2 }
  18.             return self.block_size
复制代码
3 o7 F* ?7 h7 a2 A' t5 W3 k8 y
; Q. N" N: q9 A  Z' k) d

7 D$ C7 r0 v) i) t

它可以按如下方式使用:

  1. import os
    1 E2 \% y/ H2 ]9 |

  2. + [+ c* s" y0 N) j5 P# m' K
  3. bdev = RAMBlockDev(512, 50). o! ?3 V. B3 }2 i' M
  4. os.VfsFat.mkfs(bdev)5 h2 U7 t" f1 C3 B  I& ~# T6 J
  5. os.mount(bdev, '/ramdisk')
复制代码

- [4 A$ F. a: X( n" N8 W7 j9 W7 h3 D1 i( ?8 m# k

+ ^8 Q9 C5 V) j$ y

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

  1. class RAMBlockDev:
    9 D, e2 @- G- q$ [& J2 }
  2.     def __init__(self, block_size, num_blocks):
    , a0 G/ \( |) M# l! N9 u
  3.         self.block_size = block_size' v5 u9 D0 @8 r- `' t( D1 R8 v
  4.         self.data = bytearray(block_size * num_blocks)
    / Q! N' i; W4 y1 I$ q( F" ]) H
  5. ' S) l  |; i4 v( u( h6 W
  6.     def readblocks(self, block_num, buf, offset=0):
    * H! @( C3 H8 l  ~$ F5 r
  7.         addr = block_num * self.block_size + offset
    $ U7 f- ]/ ^" K( }3 r
  8.         for i in range(len(buf)):
    ( g. X4 G4 q& D, I
  9.             buf[i] = self.data[addr + i]
      k" ]$ i  b) X

  10. ( B. w: b, o# Z8 V( H4 \4 t6 `, N
  11.     def writeblocks(self, block_num, buf, offset=None):1 l8 h  z3 |. O- @5 s
  12.         if offset is None:- j, b; A. S% i9 t
  13.             # do erase, then write% e6 x. E6 E$ Z: Y3 c: q0 Z- R
  14.             for i in range(len(buf) // self.block_size):
    ' i$ q+ f4 F. j1 H- ]/ |% y
  15.                 self.ioctl(6, block_num + i)5 I: ^+ ?  w' ^! @
  16.             offset = 0; G$ w, q! r. W. P. Q7 V3 B" p$ d
  17.         addr = block_num * self.block_size + offset' Q( r5 |$ V  b! w0 O6 F6 f' Z
  18.         for i in range(len(buf)):
    : A' @4 ]2 J- ~$ Q7 }  ]( ?# Q7 P
  19.             self.data[addr + i] = buf[i]
    % @9 h0 C0 C4 S% ?. w9 m! L

  20. / ~- P- |8 K* \! Q
  21.     def ioctl(self, op, arg):
    4 x! i7 M; |1 R* a9 \
  22.         if op == 4: # block count
    + A! n5 m+ |' s8 S+ R. B$ a
  23.             return len(self.data) // self.block_size: O! d- K5 o, ?  r8 f6 ]# C6 N( b
  24.         if op == 5: # block size" R" F- H, R" k1 \% r8 P
  25.             return self.block_size1 E& S4 h# q: u8 ^: n& O
  26.         if op == 6: # block erase
    3 S5 x7 A- _* h' g! M! s
  27.             return 0
复制代码

! t4 f. M7 h  s9 T
! v+ M4 y5 ]; P6 d5 E; Q& B: m8 {7 z$ A5 t" L

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

  1. import os7 M7 B2 l, Q0 h/ k+ ], h
  2. 9 Y4 G" N$ W) m8 }
  3. bdev = RAMBlockDev(512, 50)! G7 U$ F+ }- D6 [& |) R; K/ m
  4. os.VfsLfs2.mkfs(bdev). c8 a/ d1 y( x
  5. os.mount(bdev, '/ramdisk')
复制代码

7 C$ B3 B8 @+ I5 v: W# O1 `: R& e- }( s- c: E

. C" ]# Q  _* r8 o% G

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

  1. with open('/ramdisk/hello.txt', 'w') as f:$ M! Z* ^9 l# H- [) D5 G2 I
  2.     f.write('Hello world')' l# _' L- ^  J4 r5 x$ v
  3. print(open('/ramdisk/hello.txt').read())
复制代码

/ T3 B6 ?. k  f! U' C* M' n5 ?3 d  g' ?* ]! T
) P5 z2 t1 I5 j6 h
6 D( o# e. L0 _+ h% D6 P/ s$ Z/ B

0 J+ _& A) }7 v8 P: @9 r文件系统

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

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

7 S* T5 N) B$ M8 r
FAT

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

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

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

  1. # ESP8266 and ESP32
      z6 p- o. M( b" L/ i
  2. import os4 Z7 T# M6 ?! [2 N/ Y8 H
  3. os.umount('/')
    % N" f( X0 k3 @
  4. os.VfsFat.mkfs(bdev)
    / f- \: Y1 Y6 ^1 g! y6 \3 [
  5. os.mount(bdev, '/')
    " g( r: F/ a  C" h4 ~' p' d/ e

  6. ; Q. F7 O  u. P8 l
  7. # STM32% Y& c' ^0 @+ Q+ y
  8. import os, pyb
    ( {, a. v9 N0 k/ l: j8 @, j; I8 W( k
  9. os.umount('/flash')- u& [6 U2 N8 w7 |1 B9 y
  10. os.VfsFat.mkfs(pyb.Flash(start=0)). B. D% K" \. N. s: R& B; v( _# _) s
  11. os.mount(pyb.Flash(start=0), '/flash')) ]9 M0 J7 u2 D2 ]
  12. os.chdir('/flash')
复制代码
6 |) f+ f+ g1 z

0 o5 [1 T8 R. O% m  |: r! v5 u8 j! ^" i+ ]% ~: M
' N9 t+ f7 ?! Z  j% N5 P' A8 G* v8 X
Littlefs

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

笔记

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

$ |. U+ F& c# Z  z; T

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

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

  1. # ESP8266 and ESP32$ d! o( e$ D1 Q4 @
  2. import os
    . N. I3 U: U' b3 D0 v
  3. os.umount('/')2 r: U* n# S$ \, K; |$ E
  4. os.VfsLfs2.mkfs(bdev); E; X" C2 N  n5 S' g4 ^
  5. os.mount(bdev, '/')
    : ~! _0 w% y0 ?0 {3 x, }5 P

  6. # C* q6 B1 u/ P& j1 p3 p) s( j
  7. # STM32
    3 j# u( @1 f) r2 @7 O! \7 `7 n# h- K: A
  8. import os, pyb
    ; g' d+ Y3 F, B$ [+ i& L
  9. os.umount('/flash')  A0 Y. L0 D5 J! q
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))) l6 _  W" d, Y
  11. os.mount(pyb.Flash(start=0), '/flash')
    1 ]$ L! D5 `5 x, M7 ~4 C
  12. os.chdir('/flash')
复制代码

5 _3 ?; ~. Z4 {8 w% F/ A& ^7 T. Z: C' ?

$ [  L5 f! q7 L* ~" x! p9 C2 ^
$ s% o9 E$ W8 U% q! ], {混合 (STM32)

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

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

  1. import os, pyb; l3 L6 ]4 B# f9 ]4 n- e/ e
  2. os.umount('/flash')  v& E/ ?3 T0 b$ I+ u
  3. p1 = pyb.Flash(start=0, len=256*1024)" t3 Z) p- y& }9 \
  4. p2 = pyb.Flash(start=256*1024)
    / T+ e! r% C. x; ?
  5. os.VfsFat.mkfs(p1)
    & r* Y3 H/ }9 ]9 s, N
  6. os.VfsLfs2.mkfs(p2)& H1 {. E  T- H+ o: S
  7. os.mount(p1, '/flash')" W8 J9 R5 f" _- g) c6 B2 }
  8. os.mount(p2, '/data'); L6 r* _& |/ a8 t: V) T. I  ?
  9. os.chdir('/flash')
复制代码

( U: b; d, ?% o6 ?6 u
/ b: X8 E. y. J! N0 Q' n  ]  x6 y2 q$ i! V9 ^- ?5 u

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

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

  1. import os, pyb5 e) U5 o  z5 J- ^" R' L
  2. p2 = pyb.Flash(start=256*1024)7 |& f; |8 h8 M6 l8 l+ d) j9 ]
  3. os.mount(p2, '/data')
复制代码
% }: m1 e! p" K) H  H( A) O8 s( n. P

( [* P4 r8 @% ^4 H- {( y+ @) n% ]" Q$ [  F+ C( `% }8 R+ M

来 boot.py挂载数据分区。


) A8 s; i8 f' k& s) L0 l$ k混合动力(ESP32)

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

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

  1. import esp32, os
    & ^  X3 A! V: o: M( s
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    : e# `' C3 O) _  f
  3. os.mount(p, '/foo')
复制代码

2 G* A) y# s8 B- f' \' {3 m8 O7 D  W( x9 P/ C

1 `# ]1 d; P" o  N8 O8 n2 N! x: @. I% g3 e# D. o

) I: ^! G: G, k2 Z3 `4 I, ?, W) c2 S) ~! G. [

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2024-5-20 23:40 , Processed in 0.187200 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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