micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 141875|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3156

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


3 `9 S! X& G- N. N8 T  G. I

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


6 b( h& l% j* X2 y! D- X6 L块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


, y* }! K6 [$ Q, S0 C; o& UESP8266

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


) b, s6 m: F2 w9 `( zESP32

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

# N! i& ~! ]$ t" K7 d
  {! W$ l3 F  G! T  ?
自定义块设备

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

  1. class RAMBlockDev:7 q4 F) ?  U  }, W. n
  2.     def __init__(self, block_size, num_blocks):
    . x. |9 c. _& ?& G  q# h
  3.         self.block_size = block_size% b8 R) E* H# X
  4.         self.data = bytearray(block_size * num_blocks)+ t) S- j% m8 d4 k

  5. 2 w  \. k7 G2 x% ^9 \: }
  6.     def readblocks(self, block_num, buf):' ?' }3 k/ I$ D. p$ ?6 s5 }
  7.         for i in range(len(buf)):
    2 T7 r* Y) a+ ^1 C4 f
  8.             buf[i] = self.data[block_num * self.block_size + i]
    / S9 E( g; @/ d- t; }

  9. ; U, _2 q( B1 w4 `+ h+ g
  10.     def writeblocks(self, block_num, buf):
    ' h. }0 i( C9 T1 \
  11.         for i in range(len(buf)):; U- d! a+ H5 J7 X1 a. W
  12.             self.data[block_num * self.block_size + i] = buf[i]
      ~3 H. H) j, u0 g
  13. / |; k0 s" B( a4 c" f
  14.     def ioctl(self, op, arg):# u0 U( n% [4 u3 R/ L# ~9 k  u. I
  15.         if op == 4: # get number of blocks
    " g1 @" g0 c  T; T
  16.             return len(self.data) // self.block_size* ^* o5 x( F( l" ^# k' q
  17.         if op == 5: # get block size2 d% W: f. N3 T5 }3 H# L
  18.             return self.block_size
复制代码

. p# d# s0 X' i8 b& q8 [* \: N7 u" D. B' \7 r5 z

; `: N8 r. D5 a2 Q0 k

它可以按如下方式使用:

  1. import os" q6 W2 `/ r' {! p4 c. @5 ?
  2. " T) S1 D6 ]3 V
  3. bdev = RAMBlockDev(512, 50)
    ! M2 E5 R4 n; V: k
  4. os.VfsFat.mkfs(bdev)
    ; t4 h8 Z4 ~7 a& E* O
  5. os.mount(bdev, '/ramdisk')
复制代码

5 y3 ~1 q. M9 B) D" ]- l( r
6 Y2 q9 ~% V: P  V8 t, P4 N
" i) y- e+ U" {# e, Z2 V; L

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

  1. class RAMBlockDev:: y8 ^* k8 p$ a$ |9 \5 Z0 e& e6 Y
  2.     def __init__(self, block_size, num_blocks):
      H1 B9 _4 k: J
  3.         self.block_size = block_size
    7 J' v* f/ U- _7 ~. h
  4.         self.data = bytearray(block_size * num_blocks)( b5 Q0 S/ m2 @  w

  5. # j! n  Y' U) K
  6.     def readblocks(self, block_num, buf, offset=0):5 u% q: }2 t; y) f
  7.         addr = block_num * self.block_size + offset
    " O! f. c; O- g7 C3 k
  8.         for i in range(len(buf)):& z) L9 }7 }8 |
  9.             buf[i] = self.data[addr + i]! [2 {2 y+ o8 i# s8 A

  10. & U! b  y: p" Z6 ?
  11.     def writeblocks(self, block_num, buf, offset=None):
    2 b  S$ S7 s  a/ p$ d& [4 ]# g
  12.         if offset is None:" b6 H+ o1 j- A
  13.             # do erase, then write
    & _1 j9 L$ c9 g0 U# c
  14.             for i in range(len(buf) // self.block_size):
    8 |0 g% j7 R4 X# l1 ^) O
  15.                 self.ioctl(6, block_num + i)5 X" |' y3 K1 Z
  16.             offset = 0
    6 N5 b5 q5 N+ b0 O
  17.         addr = block_num * self.block_size + offset5 }' y( h( D4 @8 Y3 x- I; }
  18.         for i in range(len(buf)):. \7 D, w1 k; s% L3 ~! a" u+ n
  19.             self.data[addr + i] = buf[i]2 O, ]2 G9 Z6 Y2 |4 m7 z; o0 q
  20. & T, T" ~# b) G+ j! a
  21.     def ioctl(self, op, arg):2 k9 a1 n$ H7 r/ k7 u+ F* Q
  22.         if op == 4: # block count
    2 o" c* B2 a3 c) B% \& A
  23.             return len(self.data) // self.block_size
    , @/ Y: |) x( g. y. i
  24.         if op == 5: # block size
    ( o. n" C# F1 y9 l9 w9 N3 [6 y9 @
  25.             return self.block_size( T, Z$ S% z- j5 u5 l
  26.         if op == 6: # block erase
    # \' f* [/ V: a0 b8 T" K, n
  27.             return 0
复制代码
( w: @. k& T/ ]" \+ t
$ i9 }5 s4 J% }$ z, |
' D. X. ~! j+ ?" w3 I! ?, [4 B/ I

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

  1. import os5 @% {& O6 E. d. n6 P& i# ?
  2. & _* Y( n, ^0 ~! f! v- Q! x7 H
  3. bdev = RAMBlockDev(512, 50)
    " s- W7 A  x. r
  4. os.VfsLfs2.mkfs(bdev)' L* ?4 ]3 ~, n6 U
  5. os.mount(bdev, '/ramdisk')
复制代码
* O, ]! n9 v2 V/ m

$ q8 O* `) l0 J, c2 Q8 L- e, ?8 @8 j+ ~4 I7 K5 u, [

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

  1. with open('/ramdisk/hello.txt', 'w') as f:' z* c9 e$ o" I* v; y! P
  2.     f.write('Hello world')( B! r! S9 R, B# i- p$ [
  3. print(open('/ramdisk/hello.txt').read())
复制代码
* i1 _. x9 w; v! H9 }! i# @

0 y' G/ b. U* Y! [" y' W, Z/ z
& Y8 m! g; }5 E9 d  ]# d
7 J6 H1 N$ [# T3 Q: H7 {

! X! s5 U( R2 [* s" }) e0 T文件系统

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

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

6 g% P3 G/ o3 X8 D
FAT

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

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

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

  1. # ESP8266 and ESP32
    2 b# u# B! _# {
  2. import os/ i8 w# b$ Z( E0 Q6 l6 `7 J# @
  3. os.umount('/')7 [  B& n( G0 e" d" |1 h7 I
  4. os.VfsFat.mkfs(bdev)
    & k. u" g" A% p! C$ c  k: F6 ~6 }
  5. os.mount(bdev, '/')
    9 R( e' B# v4 ]' b' n. F  P
  6. - c" j1 t5 J/ }& l( q4 I* g
  7. # STM324 e$ p( S3 X1 m# p8 U% x( [
  8. import os, pyb
    . p0 _' {. o& _1 r- I& t' _) X. f
  9. os.umount('/flash'), O) N9 ^7 i, A9 R! z6 c8 y# N
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    9 z3 [2 o7 {) u: @& Z& X
  11. os.mount(pyb.Flash(start=0), '/flash')
    % Z( y3 _7 m. U  a2 A0 V
  12. os.chdir('/flash')
复制代码
' B3 s, x) L3 H( n3 ]  U7 \+ ]$ [
, P" B: A" N) J2 w% F
' a% |! B! [5 j+ q1 t8 h) X/ l$ P& p/ S
7 k$ }* U2 l+ b$ F4 `3 X: I
Littlefs

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

笔记

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

4 g# v! V9 c9 R( S9 h, S% X

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

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

  1. # ESP8266 and ESP321 g0 k8 d/ z3 C* `; y3 t3 M) |
  2. import os6 L; P/ a) \6 n9 Y  j
  3. os.umount('/')
    ( I; I# Z* H/ o& M- q' j$ E7 ^
  4. os.VfsLfs2.mkfs(bdev)- g$ Z, `- J3 D1 N
  5. os.mount(bdev, '/')  Q# i/ q3 O6 T3 j: e

  6. 6 {( @: \. Q% f3 @$ v7 r# x5 e
  7. # STM32
    ( C5 h0 ?/ A5 {9 A* b$ [; ?! h
  8. import os, pyb( V! {9 Y' \" p
  9. os.umount('/flash')( o- r* F" i% J7 G' }
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    3 m& r5 c# _% W
  11. os.mount(pyb.Flash(start=0), '/flash')
    ) }3 m4 }% o9 m. G3 ?4 B% \
  12. os.chdir('/flash')
复制代码
) m" j$ c# u) N6 R# ]- z

" P% f: C+ O! e
& j/ _9 s. v, _! y1 s, a* I: _4 G# s- D+ }! M# R, I! r6 k  B
混合 (STM32)

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

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

  1. import os, pyb
    7 e, T# X) E. W* K6 ~/ K5 n
  2. os.umount('/flash')+ U5 R, S. h' x' n. M
  3. p1 = pyb.Flash(start=0, len=256*1024), J- l1 D0 T& p4 y& e
  4. p2 = pyb.Flash(start=256*1024)
    % j: p- G  a( V) g/ o( |
  5. os.VfsFat.mkfs(p1)/ g/ d7 W% K  d$ P* F' ?, f3 r" \, ]
  6. os.VfsLfs2.mkfs(p2). E9 L9 m4 O0 H" V5 D
  7. os.mount(p1, '/flash')1 H, G) {, l' y# g
  8. os.mount(p2, '/data')' {  i9 k* C! s% f- v# U! K0 z
  9. os.chdir('/flash')
复制代码
2 g6 f# Z' M) w( T! @0 Z
* Q% D& w1 H9 l; o5 r

& Y. `6 V3 h3 E3 e! c

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

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

  1. import os, pyb0 p1 w5 J! C# C/ P! X
  2. p2 = pyb.Flash(start=256*1024)2 Q5 Z! p7 [( C: U
  3. os.mount(p2, '/data')
复制代码

$ b. N/ T5 c+ q; k$ P( X1 i* y
% w0 d' j: \/ G- l9 V9 _) L8 m3 T0 }9 J

来 boot.py挂载数据分区。

; y2 l7 w) f! {7 u7 z: g: N4 a$ V
混合动力(ESP32)

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

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

  1. import esp32, os0 }3 D2 S$ e" m' {
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')0 r  o5 s& T* L6 S1 O" N* W
  3. os.mount(p, '/foo')
复制代码
5 K$ a: Z( Q+ z6 X9 }

0 \6 m% ~/ b0 B1 N1 Q6 _/ E: ?: }
) D9 _4 K- W/ E
0 T  s, Z3 O& R

0 J7 ~! ]) D; b! @# `3 ?6 `3 T" I/ J' A4 r0 S! Z  y. M

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-5-17 09:22 , Processed in 0.109200 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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