micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 200273|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3322

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


! k( J. N8 z& ?' e3 h) s2 p: h

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

: |' l$ l0 P3 Z1 H
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

" i9 V  L7 q5 G3 [0 p; t" S" O. S
ESP8266

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


! w# A  N! f/ [4 j: iESP32

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

& Y* A6 g" x; i. u0 k

: t( {- k: o  c% G4 S自定义块设备

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

  1. class RAMBlockDev:
    0 G& ^0 c) T/ |0 ?# p+ H* i4 a
  2.     def __init__(self, block_size, num_blocks):  ^6 e0 ?3 \0 [1 Y% i" S
  3.         self.block_size = block_size0 {" H( B4 B' [
  4.         self.data = bytearray(block_size * num_blocks)
    # g5 K  k  x. @+ a( D
  5. 8 x# D$ f: f$ F' |  m1 x7 u, y
  6.     def readblocks(self, block_num, buf):" ^' {# G3 M2 P) j. Q$ ]' w
  7.         for i in range(len(buf)):/ N) z6 y# \+ }  q. {2 j9 E
  8.             buf[i] = self.data[block_num * self.block_size + i]2 Q5 z' A" L! C) g' y3 z* f

  9. * i( q! N4 P3 k& X* @' [* l) l; u; m
  10.     def writeblocks(self, block_num, buf):( [$ l  R4 ~6 Q5 g: X$ w; _. @
  11.         for i in range(len(buf)):
    ) K  X- C' w: K+ F. `
  12.             self.data[block_num * self.block_size + i] = buf[i]" L' W3 K- u+ Q5 a: ?
  13. 3 g8 u  N, g, K# A2 |7 ]
  14.     def ioctl(self, op, arg):
    + K" s) q5 k# \& f. |( T8 n
  15.         if op == 4: # get number of blocks, v2 i- A/ X4 j# y$ R7 R. i% k1 N
  16.             return len(self.data) // self.block_size4 G) q! K- @0 f; ~0 p
  17.         if op == 5: # get block size' j+ c4 u# x. S* P8 g) Q: W9 ^$ c
  18.             return self.block_size
复制代码
! r7 G7 Q8 @3 A2 m& {* d
9 |: [1 \8 o; A& M5 ~) \

* n8 D0 M* Q5 [/ G. A% I

它可以按如下方式使用:

  1. import os7 e) B/ Q: D3 w. F. W- D* o
  2. , l2 a$ y1 \) Y: ?3 W1 A2 z; B
  3. bdev = RAMBlockDev(512, 50)
    * k$ Z7 x) [4 n# Q/ B" R, j
  4. os.VfsFat.mkfs(bdev)
    ; f* q7 E1 C' n! X5 l9 d( O
  5. os.mount(bdev, '/ramdisk')
复制代码

/ z! T+ \- r! k1 o0 e# X/ X/ E: B: H& w5 i6 P8 A

" r4 {* u6 h& h# r5 H1 W* N$ O

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

  1. class RAMBlockDev:* c6 {2 e3 R) X  t% r. q0 I
  2.     def __init__(self, block_size, num_blocks):
    : h+ O- C# f  O* P* |5 r
  3.         self.block_size = block_size) R. X3 Q( k+ g. u1 m
  4.         self.data = bytearray(block_size * num_blocks)) Z4 y4 W8 g1 s' |" z3 N
  5. 6 F0 N" r6 Q) S+ Y
  6.     def readblocks(self, block_num, buf, offset=0):+ c8 f8 y/ c" T0 J' }; `
  7.         addr = block_num * self.block_size + offset
    8 `- h- @" z% n' d1 \* y7 ~
  8.         for i in range(len(buf)):( |$ Z9 P" h4 `; S! b6 O7 {
  9.             buf[i] = self.data[addr + i]/ j6 O5 G% v- l
  10. ! g& d/ p& j  o7 r2 ]
  11.     def writeblocks(self, block_num, buf, offset=None):
    ; \% s) e  |9 z; w* x! u1 q1 E  Z
  12.         if offset is None:: d" p3 @8 _4 @" E% L+ D& F4 d7 B
  13.             # do erase, then write$ F3 H9 m9 r: H& q% I9 J% T# N
  14.             for i in range(len(buf) // self.block_size):
    ; r: s3 X! _$ g% m8 l$ a- Y' }
  15.                 self.ioctl(6, block_num + i)
    # V. [. S5 r& \  H9 `
  16.             offset = 0# N! H5 i3 o1 W2 d1 z5 c
  17.         addr = block_num * self.block_size + offset+ w6 p$ x2 q* y8 U% ^" u4 t0 {( ^7 p
  18.         for i in range(len(buf)):  o; e3 T% i3 j# y
  19.             self.data[addr + i] = buf[i]
    9 ]0 q1 K  E+ b8 e. S; |
  20. + Y9 p+ C% ?  D- h, l! q3 l! ]
  21.     def ioctl(self, op, arg):
    ' w5 p" r6 v: g. O/ m) Q
  22.         if op == 4: # block count
    8 R% ^. K6 C" a7 Y
  23.             return len(self.data) // self.block_size( e- k% ]; y/ p2 \$ [9 |* B# [
  24.         if op == 5: # block size
    / z6 H; ~6 n+ \8 q% E
  25.             return self.block_size
    4 P1 N! S7 R+ @4 q" r; P' W. F
  26.         if op == 6: # block erase+ c) I2 b; K0 |. {! m
  27.             return 0
复制代码

- f; Z1 @: c0 z( L% g9 Q, _1 M2 z1 _6 w8 B
5 j: v# ?/ a$ h" z( N  G- H# S

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

  1. import os
    - g, O, D4 o% X- N+ I

  2. 0 u0 Z# p2 n& N
  3. bdev = RAMBlockDev(512, 50)
    ! Q1 K1 R- q1 {. |9 E( I& P
  4. os.VfsLfs2.mkfs(bdev)
    4 ^$ Q" h9 Q) ]  K  `
  5. os.mount(bdev, '/ramdisk')
复制代码
+ i4 F& S5 A) f7 q

6 T' s' {# A) q3 c. {( [
! u6 P7 q2 r2 T6 [5 l# X3 N# l

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    ' _7 @, T# o& e4 ~: ]8 P
  2.     f.write('Hello world')# L! `) n  Y8 m" K/ W) |5 D( b
  3. print(open('/ramdisk/hello.txt').read())
复制代码
, Y( }6 K: T8 E" Y2 a) Y
0 l" W: t' K& a/ H' P% C" f
' [9 z# m& }6 X+ Q6 k! P

. }3 M5 f  j2 ]7 U9 }

3 V4 G4 T) `4 v文件系统

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

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

/ G0 V5 |3 z7 o3 D
FAT

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

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

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

  1. # ESP8266 and ESP32
    : _% }+ k2 S( D/ L9 M2 l  T4 d, c
  2. import os. x- i' }" y  ^: p3 h/ o
  3. os.umount('/')4 n9 \1 G# e3 F
  4. os.VfsFat.mkfs(bdev)$ T; F' C' A8 d! u
  5. os.mount(bdev, '/')" U' H( w4 a, o7 K+ e; v
  6. , w5 V; U7 h) J6 e$ {* o! H
  7. # STM32$ B/ \- T+ D1 |7 Y3 y2 ^5 N4 b
  8. import os, pyb
    9 {( n, q% ^9 X6 z, ?( }0 _1 h
  9. os.umount('/flash')' X( K. Q" J2 ~4 m9 R: {
  10. os.VfsFat.mkfs(pyb.Flash(start=0))% ]. c: a7 C( D" s+ O& a/ R
  11. os.mount(pyb.Flash(start=0), '/flash')
    ( r+ O+ C- J3 K6 ?- I+ O! V' D9 N4 ?# D
  12. os.chdir('/flash')
复制代码
( _% E) J: k1 p3 H4 X
7 d9 A6 @3 M1 M4 u& O5 ?. w, D

: U! H4 x  x4 r4 d& R' x8 V7 l6 y. m
7 i: _  z" i* }5 |# Q5 I7 oLittlefs

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

笔记

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


" e) O; X0 }) y+ J6 f% `& I% s$ e* f

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

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

  1. # ESP8266 and ESP32
    $ ^, D' R6 s' ?" u
  2. import os
    # A& a) M: [6 M
  3. os.umount('/')
    2 u) B! T" A& O# g) h
  4. os.VfsLfs2.mkfs(bdev)1 X9 _" Z' x' S7 W( m
  5. os.mount(bdev, '/')/ l% l+ _+ U* C2 O" _

  6. * E+ S/ X, |9 a* j
  7. # STM32
    % Q) V' V0 O6 w
  8. import os, pyb. n! E% s4 M, j0 @0 O- k8 ^+ t# k* R
  9. os.umount('/flash')
    2 r: [. H. j$ u1 v% R" {
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))9 N7 W2 a) S# p; ~4 D, s" B% v
  11. os.mount(pyb.Flash(start=0), '/flash')
    5 s! }6 r; c7 b
  12. os.chdir('/flash')
复制代码

/ R1 B* x5 d  P0 U1 O) }
5 d* N: O1 ~/ d- Z# ~+ a
: g& ^; x3 H# }$ W# E& u+ d# i* B; @# I* S
混合 (STM32)

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

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

  1. import os, pyb
    4 w3 J" T! X$ f  c
  2. os.umount('/flash')
    " \* I5 o6 z9 x; ~9 T3 n6 n* o' e4 H
  3. p1 = pyb.Flash(start=0, len=256*1024)
    % f3 b7 W& z5 X* ]) \9 L! |  P
  4. p2 = pyb.Flash(start=256*1024)2 N0 {4 y" c! |1 m- |( T  l; J
  5. os.VfsFat.mkfs(p1)
    ' Z2 v1 N5 O1 G0 A7 p7 F  ^
  6. os.VfsLfs2.mkfs(p2)) R5 o: f6 e% J5 d9 b9 o( |8 s
  7. os.mount(p1, '/flash'); D, t8 v: W& a9 e0 J: e
  8. os.mount(p2, '/data')! f3 ~  u! E4 p6 Q7 ~7 c- N  y& P5 ]
  9. os.chdir('/flash')
复制代码
) F; N1 z% Z8 j! J3 }8 a

5 m9 s  z: w& v1 Y; y, m: ?$ e+ P  ?" D2 t7 b! o

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

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

  1. import os, pyb
    ' J# N3 t) O. l3 w! o, g( N* y& H- O
  2. p2 = pyb.Flash(start=256*1024)0 V# t# O- y5 V5 e
  3. os.mount(p2, '/data')
复制代码

7 x+ I3 h( Z4 Y" e# b+ ]0 P8 ^' |3 z" ?

2 t% S6 m' m& d

来 boot.py挂载数据分区。


. s! F" N$ ~; o混合动力(ESP32)

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

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

  1. import esp32, os
    - R! }# [6 P  i' |
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')4 P9 E" c5 c/ Y/ h4 k1 i. @6 V
  3. os.mount(p, '/foo')
复制代码
4 Y$ k1 o8 S" I+ Y

0 C- P( g# Z6 b8 D! [2 q/ _. V! n/ y# ~% b5 x9 e# e0 M: v& W
' a: Q; _+ v8 F. t. G4 o. c
( [2 I' ~) w  `

& _7 E9 B% i, w. H' S6 c

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-9-14 01:01 , Processed in 0.202800 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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