micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 180391|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3244

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

, I5 {9 F, Q2 w' @  E! ^1 e6 z! J

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


3 ?2 y0 d& h+ R' g. `. z) @/ |块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

! b* _4 ~) x: J0 v: S
ESP8266

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

0 i: ~  Z" G% q* p3 }. e  D" f
ESP32

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

' q0 ?+ W0 W4 h; E
' n! k& ~* V" A: G
自定义块设备

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

  1. class RAMBlockDev:
    - T/ {0 X& v( s) c, j
  2.     def __init__(self, block_size, num_blocks):
    $ Y2 j4 G) P+ B1 v3 k' |2 z
  3.         self.block_size = block_size% X/ v% i* d/ s& T, h& |  i. u
  4.         self.data = bytearray(block_size * num_blocks)1 h' G! h0 a: X8 p- |* A9 z

  5. : z7 h  \; G8 H. y6 o" W' u! w
  6.     def readblocks(self, block_num, buf):
    8 T$ A) E; |3 y4 g
  7.         for i in range(len(buf)):: y" W) T9 K) d% Y" V
  8.             buf[i] = self.data[block_num * self.block_size + i]% @. y8 D$ y, J% ]5 Q* ^/ O7 n6 \
  9. , j  Y4 e3 c0 a- P1 M4 M
  10.     def writeblocks(self, block_num, buf):
    ' B2 U7 E% W6 O# ]7 j' [. d. P
  11.         for i in range(len(buf)):
    ( g8 \3 e7 u) p1 ]8 b7 {" W
  12.             self.data[block_num * self.block_size + i] = buf[i]9 J  g, v3 D7 `$ _

  13. : A1 O5 _! E- T, y" M' k* C
  14.     def ioctl(self, op, arg):! n' C: L& C" ^& a
  15.         if op == 4: # get number of blocks2 o7 ?5 h3 f: U8 N5 M5 {; p5 @1 g
  16.             return len(self.data) // self.block_size
    " d$ B4 r" W. v' I/ E" X: F
  17.         if op == 5: # get block size
    # D6 @* K1 J1 }1 \$ I. K! x
  18.             return self.block_size
复制代码

9 C# L  G$ e  ?1 W9 u1 g: v) C/ W

! g" b( B- [  y

它可以按如下方式使用:

  1. import os
    ' A$ R; L) j8 n/ S# r. d8 W

  2. ; y2 a* ?- H5 K1 w2 X) b, x" F
  3. bdev = RAMBlockDev(512, 50)
    4 f6 {" z; E+ ~  y6 S
  4. os.VfsFat.mkfs(bdev)  S0 d; T7 j2 w; L% z
  5. os.mount(bdev, '/ramdisk')
复制代码
' e+ V; Z0 K6 N& k2 H
* v4 z4 j+ S" L$ o! ^
, P0 t% H+ e4 A3 F. W0 }1 E- h6 C

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

  1. class RAMBlockDev:
    4 ^# ]5 x& G3 [% D. v# \/ h" o/ W
  2.     def __init__(self, block_size, num_blocks):
    - f. N! P  B0 D8 g2 ]
  3.         self.block_size = block_size
      M6 h% |& R5 c- X
  4.         self.data = bytearray(block_size * num_blocks)
    * H3 u2 H, ?5 K, l& A! c  K1 M- z
  5. 7 f& |" y0 `  i$ Q) o
  6.     def readblocks(self, block_num, buf, offset=0):: [7 E7 J: O  S1 ^- A/ X- q8 D
  7.         addr = block_num * self.block_size + offset
    , {$ N2 [* b4 r% Z& F
  8.         for i in range(len(buf)):
    ' r2 r6 r. k& u( T" X
  9.             buf[i] = self.data[addr + i]
    9 Z( f$ z  \) P( L* v- |: e' \
  10. 1 L8 f% X' i) H3 R3 H( B
  11.     def writeblocks(self, block_num, buf, offset=None):
    1 ]! T" R) \( @0 u
  12.         if offset is None:$ q' I3 ~! r  g
  13.             # do erase, then write% ~9 j1 S' v4 m. l  V
  14.             for i in range(len(buf) // self.block_size):$ y, {8 ?) d3 G! Q
  15.                 self.ioctl(6, block_num + i)( o5 ?& i, l% {6 D0 B8 F( J
  16.             offset = 0
    , S& L" w+ \$ N) O
  17.         addr = block_num * self.block_size + offset
    . M3 F$ b7 G" }8 z
  18.         for i in range(len(buf)):: b* f" q/ F" D: d+ Z; {
  19.             self.data[addr + i] = buf[i]
    3 l3 Q" m" y- m. E! S

  20. . G! n0 ^' W' ?* y% U( G6 V6 e
  21.     def ioctl(self, op, arg):) V) C; ~+ _& w( R$ ?3 t
  22.         if op == 4: # block count
    : U$ E" e3 R1 P2 Q' i+ p
  23.             return len(self.data) // self.block_size$ Y5 s' b: N+ l$ B+ C0 c
  24.         if op == 5: # block size
      R" l3 b9 L, U
  25.             return self.block_size) p# y. W7 h4 l
  26.         if op == 6: # block erase. p) m7 l/ g! F7 t
  27.             return 0
复制代码
7 T8 t/ S5 e, b
. I/ T& i) f: r) {% J$ r0 ^
5 I8 C( r1 @6 A" L( \

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

  1. import os
    % t" ?7 T1 S! x

  2. : K- v5 S4 i9 r' b  {
  3. bdev = RAMBlockDev(512, 50)4 t+ O( u4 L8 X6 `- E
  4. os.VfsLfs2.mkfs(bdev)
    ; d" c2 L- Q) f  q! C
  5. os.mount(bdev, '/ramdisk')
复制代码
5 Y9 G  }' ?' R
5 A7 c4 O" C% d: H/ ?# v8 ~) ?
3 V8 b4 V7 a7 P5 o' W6 Z

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    6 s" n+ J$ c. {- b  O* M
  2.     f.write('Hello world')' F( O! V( N' @8 _: J4 M
  3. print(open('/ramdisk/hello.txt').read())
复制代码

# v' o' W8 \8 e5 ^$ I
, |' |! g! r. T2 g- x; y- S* M/ H' K, l# }* P" P) R) U' R6 k& A
5 j8 {& m! ]* s& y  s

/ c, i$ t( y) \1 e) E+ y7 s5 n1 K' ^文件系统

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

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

' `2 d1 B0 ]- ?4 l1 e
FAT

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

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

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

  1. # ESP8266 and ESP32$ B" F, h6 ?& x- X1 t+ d# J
  2. import os) v' |, t7 W' D4 `9 A
  3. os.umount('/')  k8 U! A! R% F$ U* |
  4. os.VfsFat.mkfs(bdev)( v0 F" X& L( j: o8 s3 N
  5. os.mount(bdev, '/')( Y+ Z' h: n# E( B9 k
  6. + L* T0 k$ o8 p1 f
  7. # STM32
    ) d" @$ i: ], Q2 c% i  d
  8. import os, pyb
    9 p9 r% a( f  a# r, t' w9 t
  9. os.umount('/flash')6 V' h5 u# z% @' a
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    ) n, t* ?8 S; ?" y$ S0 j
  11. os.mount(pyb.Flash(start=0), '/flash')
    ' k$ h: d, L& V, j& t! H
  12. os.chdir('/flash')
复制代码

! J" J) m0 S8 k  k, |" z$ a( a6 F1 J+ J! v/ `( Z) \

5 `" D2 V; v) z& F/ g( f* b* T! [8 F+ e  e, r
Littlefs

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

笔记

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

! Z& Z7 n% t" }- _  l; _# ^$ J( r

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

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

  1. # ESP8266 and ESP32
    4 x7 l7 i- u, W0 z* [. ^
  2. import os: E" U0 W9 f, q6 _1 u( s9 H8 a0 e1 E
  3. os.umount('/')" e( ]) C/ s0 n6 R4 p: F! A, {
  4. os.VfsLfs2.mkfs(bdev)  \6 K; ~4 i' ]" Z- l" G# W
  5. os.mount(bdev, '/')
    / ^0 @+ \# a6 r2 e
  6. : A+ @8 B* ^0 ]  [
  7. # STM32
    ( U* o2 c+ W' H6 I2 O
  8. import os, pyb
    & J- ?; B6 p4 Z9 b( k
  9. os.umount('/flash')
    8 C- K5 l6 w6 i/ V9 F; d
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))$ h# ]' I) F& _6 h6 M: P' \# a1 M1 q
  11. os.mount(pyb.Flash(start=0), '/flash')
    3 }( h/ B) |+ C! r+ N" c( C+ w
  12. os.chdir('/flash')
复制代码

* i0 B6 G5 l1 o; }
) M: y" y* D' b1 _- Q9 U5 X; W" x( B7 N# T: Y
# X. s& R3 K/ K8 F; t! W9 L
混合 (STM32)

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

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

  1. import os, pyb3 h" H: f( ^: J
  2. os.umount('/flash')
    6 T1 f! a6 a/ H: g: q
  3. p1 = pyb.Flash(start=0, len=256*1024)
    * I# f; n' ~- ~- l7 ^, n  e
  4. p2 = pyb.Flash(start=256*1024)8 r8 Q* h$ _7 Q4 x, {
  5. os.VfsFat.mkfs(p1)3 ^, R: b. x/ @' R/ O& {
  6. os.VfsLfs2.mkfs(p2)0 u3 E3 q) h- a
  7. os.mount(p1, '/flash')9 D- i( g6 V$ i! f6 Z: }
  8. os.mount(p2, '/data')% q4 e6 I/ \7 g* Y" T) o' _) }8 W( D
  9. os.chdir('/flash')
复制代码

" W3 d. p6 l. t
; Q1 T4 t7 T2 j# K: W
2 K* J1 b( J' {. {

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

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

  1. import os, pyb
    0 T7 S. [$ b% ?7 Y3 }2 G3 h. M
  2. p2 = pyb.Flash(start=256*1024)% i7 t6 `, V: C
  3. os.mount(p2, '/data')
复制代码

/ R$ h' D7 s  |+ U4 y; C4 {( @
& o9 ?% I% t* r) M9 Y* v. N
: @" J& w) P& S; f  w* S8 F$ a

来 boot.py挂载数据分区。


& K2 g6 d" H7 y, Y! J混合动力(ESP32)

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

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

  1. import esp32, os
    : p9 V5 z6 S& e# K" v$ [
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')0 j$ }6 H% V) l/ P* ]: L
  3. os.mount(p, '/foo')
复制代码

2 W* J$ B( A8 F/ _( {. P: @- Z2 G: s% }5 u

4 L% J  A, j# r( j5 S  i# y7 M
' b7 U: e2 b# L4 U

& b9 X+ P" S: j& [' j4 L8 r/ _4 ?( q' _: m  i7 Z  M

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-4 12:21 , Processed in 0.171600 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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