micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 100409|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3104

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

, g& o; b7 X4 X

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


7 D. o7 o- l# U. G% e" M  T* e9 W! d块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

- e# Y# E- g! E. D$ d
ESP8266

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

1 X. D/ P$ K3 z/ R3 T' w+ W
ESP32

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


& ~3 x4 W8 [0 Y5 Q+ ~4 g# g# B1 J3 Z5 @2 s* J
自定义块设备

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

  1. class RAMBlockDev:; ?7 F, R% t4 u- x" G* H$ _
  2.     def __init__(self, block_size, num_blocks):# M6 n- s5 O4 W) J
  3.         self.block_size = block_size' ^9 X% P% _: z: L
  4.         self.data = bytearray(block_size * num_blocks)- o3 q: O3 \. h3 A8 z
  5. % @5 ~8 g+ D( ?8 d1 [1 s7 f
  6.     def readblocks(self, block_num, buf):" y5 d% U) {' B) g/ T  F& O
  7.         for i in range(len(buf)):
    # T& _' d7 M  e/ J+ |. X$ v  N
  8.             buf[i] = self.data[block_num * self.block_size + i]
    3 h  o! Y1 j  T. v0 p, ?

  9. 1 Z: \9 `! H  d% m
  10.     def writeblocks(self, block_num, buf):( a& m% X7 z, Y$ P6 C: z& g- N
  11.         for i in range(len(buf)):. C: i+ Z+ k0 Y" A3 }5 b% O
  12.             self.data[block_num * self.block_size + i] = buf[i]3 [0 L7 o* y% e, f# }3 Y" W! o5 s% ~
  13.   f2 T( `" y) w2 [/ a, U/ ^. s! e
  14.     def ioctl(self, op, arg):+ x; }: _. k& h' e
  15.         if op == 4: # get number of blocks
    / q" Z* R6 J$ r4 H4 o% c
  16.             return len(self.data) // self.block_size
    2 w$ D+ {( ?& k; s( a9 E  P
  17.         if op == 5: # get block size8 k. r1 g4 c" w% ~
  18.             return self.block_size
复制代码

, N3 B7 Z7 M! s# e, n& V( Z8 S3 o% A  I
) z  A6 |+ v4 a  b: G

它可以按如下方式使用:

  1. import os
    & r  Z. ^) g1 _; }3 `
  2. 3 [2 n4 D& K8 n8 Q8 K% b3 z
  3. bdev = RAMBlockDev(512, 50)# n$ f* X* ]6 g2 F
  4. os.VfsFat.mkfs(bdev)+ l, Y; `, S4 u2 l1 L" P+ h" Y3 b
  5. os.mount(bdev, '/ramdisk')
复制代码
5 @+ b4 U. c0 q2 b3 z9 }
4 o" f, o# t2 |% f  O) }

: e2 k. k5 l' Y6 a

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

  1. class RAMBlockDev:0 y# i- P) `  I4 E& b0 _4 a. e
  2.     def __init__(self, block_size, num_blocks):
    - B* ^) t( q" M; h6 `( J& [
  3.         self.block_size = block_size
    0 Y' |8 x5 F$ S; V- f
  4.         self.data = bytearray(block_size * num_blocks)% O$ V) K$ e/ k8 [% M; d
  5. 5 Z" I$ W# t- h  z9 {
  6.     def readblocks(self, block_num, buf, offset=0):
    7 s. ~( V, L. q, H
  7.         addr = block_num * self.block_size + offset
    ; z8 Z0 U7 c  N7 H/ [
  8.         for i in range(len(buf)):
    + |( x+ m& G6 Y  T* _0 T
  9.             buf[i] = self.data[addr + i]3 \' d) d/ ?2 Z" _4 x0 s. o( s
  10. . ]4 i- K7 i, ]3 `; f
  11.     def writeblocks(self, block_num, buf, offset=None):
    3 `5 x$ ~  E* a6 M
  12.         if offset is None:
    : [9 O1 U7 z* _9 `
  13.             # do erase, then write
    ! f; I1 K; A) a3 n
  14.             for i in range(len(buf) // self.block_size):
    # M& P( t$ A: J& M; y4 n& j3 C+ f# M
  15.                 self.ioctl(6, block_num + i)
    - ^, j2 t) d4 k6 F! ~8 G- l
  16.             offset = 0
    " g# P- j$ }1 |
  17.         addr = block_num * self.block_size + offset* s) c1 L/ q0 E$ p! X& b
  18.         for i in range(len(buf)):
      I) e- k/ F+ A* ?8 R. M+ \
  19.             self.data[addr + i] = buf[i]# f* p, A8 V9 J' p8 Q0 |; q% D
  20. 9 P* E' s/ v" N9 f! X0 O0 k7 H
  21.     def ioctl(self, op, arg):
    6 o0 L( }3 j$ X. A9 O
  22.         if op == 4: # block count
    1 ]9 y- M7 w0 u! _8 {, o4 \& B+ f
  23.             return len(self.data) // self.block_size
    : P7 S% G( V9 ]0 T+ {
  24.         if op == 5: # block size
    1 C( z- K1 O3 t, l
  25.             return self.block_size
    6 @) T4 A0 Y8 |' J$ u/ g8 Y
  26.         if op == 6: # block erase! i6 f/ Q8 m+ O3 F' b
  27.             return 0
复制代码
* H5 A) w' {# A) M
" f; t: [* X4 _* Z6 O  Y5 A7 r
( S9 O+ e7 I/ R9 O

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

  1. import os
    9 y" q7 Q% B+ j# ^" N9 t, D) P
  2. 2 |6 ]& @; T! }, @
  3. bdev = RAMBlockDev(512, 50)9 e- N) ]6 l1 S, j2 j( x
  4. os.VfsLfs2.mkfs(bdev)
    6 D- w. V' K" U' U
  5. os.mount(bdev, '/ramdisk')
复制代码

9 i; ~0 X, x% n5 S9 r6 L" o1 v3 r1 L7 A% \
6 ]$ s) t5 W* r$ e$ ]& |

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

  1. with open('/ramdisk/hello.txt', 'w') as f:! V! H# p/ M+ Q' x7 r
  2.     f.write('Hello world')
    / D) ]0 H6 O! K" c5 ?. U! z
  3. print(open('/ramdisk/hello.txt').read())
复制代码
, x+ r. {5 S% V

- n$ j& s2 M' r, ^6 N/ G% @( ^( M2 z: Z) v# y

$ M, i1 o0 L( W5 W1 Z% z3 ?5 l

! Z: U; S0 ~" w- P, E% u& ~/ R文件系统

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

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

' k4 w! l' l# u& [& r$ Z! `; t, m
FAT

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

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

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

  1. # ESP8266 and ESP32
    7 q' k4 n: x9 S1 J& H
  2. import os
    5 [$ Z) r3 i3 N- |
  3. os.umount('/')) Q9 l& b5 |0 Z; W+ I* ?) `. u2 M- j
  4. os.VfsFat.mkfs(bdev)8 k+ g7 Q7 p3 J4 z& @
  5. os.mount(bdev, '/')! C) y: ~& |! K

  6. . p, }9 ^% u/ _1 l& L0 d/ J6 r
  7. # STM32
    ) ?" R, H5 l0 M, \+ v3 l8 W, ]
  8. import os, pyb
      S. e# u2 o. C! E# X
  9. os.umount('/flash')" u9 x! s9 g8 |3 j, y
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    & j1 z3 d7 a, [! J2 Q( W
  11. os.mount(pyb.Flash(start=0), '/flash')
    , a6 C9 \6 C3 v# X# Q7 B
  12. os.chdir('/flash')
复制代码
5 D" g' k3 \( P3 d  v
" }; Q- c& u  N/ I' @

) f  p+ B% q0 p  z" L7 ]. `/ ?+ T
7 r5 ^# c- c) {Littlefs

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

笔记

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

/ u/ Q4 M6 P9 J8 Z/ U' _+ {) C

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

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

  1. # ESP8266 and ESP320 {& l0 [3 P9 t' _4 i* C8 K2 w
  2. import os  A: q& |8 Q' z7 n5 d. V  |/ p
  3. os.umount('/')
    7 B) ~6 d2 O! M
  4. os.VfsLfs2.mkfs(bdev)$ ~, C. G9 @" p6 \  R
  5. os.mount(bdev, '/')
    ( W9 ~$ {" b) y+ t( e$ q8 |

  6. $ b( M% a0 ^  Q9 n& M
  7. # STM320 e0 V  T; B9 }
  8. import os, pyb3 q+ z, I# T2 U) a% |9 H! i
  9. os.umount('/flash'): O  E5 K1 b/ v. E! P" L
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))& k( e" v3 h+ L5 Y
  11. os.mount(pyb.Flash(start=0), '/flash'); {; G  @. u* ^' H9 x: ]
  12. os.chdir('/flash')
复制代码
7 I* U2 w! N) r2 R0 T; b& C
* i- ]( N1 f* \6 S5 q$ N
- ^- c& s& t5 s- j8 }+ Q

7 s) k* c' T! J混合 (STM32)

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

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

  1. import os, pyb2 ?. c1 f- j2 X6 J! O! e" i
  2. os.umount('/flash')
    # ~4 n5 W. U; K
  3. p1 = pyb.Flash(start=0, len=256*1024)' b$ q, m- `- h5 q
  4. p2 = pyb.Flash(start=256*1024)
    & c6 z( e- _# n( b& p8 s
  5. os.VfsFat.mkfs(p1)4 @0 L- o6 [8 `3 }' Q3 x5 n+ ?
  6. os.VfsLfs2.mkfs(p2)7 u; A5 |& G: [
  7. os.mount(p1, '/flash')  L; {& j( X, Z* U1 k
  8. os.mount(p2, '/data')
    - d8 C( \: ?* C) S+ v, ^  w' Q
  9. os.chdir('/flash')
复制代码
( z( f  W3 @/ F' E  r* o
7 [$ y# J6 U7 g8 N! d; u2 H
9 g$ F( d( U0 v0 M2 n3 t' E

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

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

  1. import os, pyb7 H$ v! _- D  n" u5 P4 P0 k
  2. p2 = pyb.Flash(start=256*1024)
    3 Z9 C0 g) u9 Q" S/ _
  3. os.mount(p2, '/data')
复制代码
2 b7 ~  U  Y3 U
5 K6 d6 C/ v9 L, J) I! L
, d* W& n9 @# J& Q/ C5 e( J

来 boot.py挂载数据分区。


* r$ C( {! y( h: p' V+ ~+ F8 o混合动力(ESP32)

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

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

  1. import esp32, os
    ' L( {( J- Q/ [2 h
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')% H; J* X0 s) A' l
  3. os.mount(p, '/foo')
复制代码
- f- y# X" i( F7 }2 J0 Y
' Q6 z" T1 x$ ?4 |, `' t5 P
- ~/ A9 f0 y6 ^. `4 w

$ d2 ]( ]; \! A/ F+ K6 X
- _& D( G4 e/ @& ]) r1 a1 j5 |) X$ n
/ L& B. s4 p$ |, ]( `

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-4-4 12:25 , Processed in 0.109201 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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