micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 188522|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3276

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

# Z2 R: [' T; L" S

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


  N& e( [0 @1 Z块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


# U$ d. S& `, `9 m, RESP8266

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


/ a6 [! a' z6 h0 T' LESP32

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


: Y1 V* ~  g. h: \! m, t. Z
" ?3 G7 z2 n3 E0 O自定义块设备

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

  1. class RAMBlockDev:$ q) U: v4 g# |( A# c* O4 A1 K3 @
  2.     def __init__(self, block_size, num_blocks):: N0 d. R- k- ^+ n$ o& n2 \
  3.         self.block_size = block_size3 W$ x1 ?8 y; Z
  4.         self.data = bytearray(block_size * num_blocks)- b. e9 Y" O- J

  5.   I; H2 ^. u5 g- y$ z4 G
  6.     def readblocks(self, block_num, buf):+ o) [/ Y. F3 s
  7.         for i in range(len(buf)):4 m3 ^6 W2 ^3 G
  8.             buf[i] = self.data[block_num * self.block_size + i]
    3 U8 d5 y) v/ a$ u: _, m" C
  9. ; T* Z/ M6 [' C2 Q, x' N" L; b& u) \
  10.     def writeblocks(self, block_num, buf):
    % H. c& w3 d7 s: ~9 t* d) F9 f% p
  11.         for i in range(len(buf)):/ j- F6 A2 c7 f: Q5 J
  12.             self.data[block_num * self.block_size + i] = buf[i]5 g5 P# M# F7 ^: H9 r

  13. + K( c$ K  B  O9 I! ]7 f7 z) u# M0 W( A
  14.     def ioctl(self, op, arg):: m+ S3 [; \2 g3 ^5 Z) ]; V' ]( h
  15.         if op == 4: # get number of blocks' t, z0 u: R) q6 c* T0 P+ W
  16.             return len(self.data) // self.block_size
    ! o1 v, ^% C2 P6 ^% \
  17.         if op == 5: # get block size
    # G/ h  b8 P* h8 R' v% Z% C
  18.             return self.block_size
复制代码
$ q/ I1 z8 e9 Y# _0 y7 l
9 ?% v) Q* c1 c# z& @1 x
  ^' ]. P3 O6 L! ], ^) z7 \+ n5 _" f& P

它可以按如下方式使用:

  1. import os
    $ J- g- t$ c1 P( N5 l* ~+ {

  2. 7 Q  M4 Z( g% q8 R2 u' d
  3. bdev = RAMBlockDev(512, 50)  S5 ^1 x. ?  j8 b% g( e2 Q
  4. os.VfsFat.mkfs(bdev)
    5 p3 p  ]6 W/ ]. D' B
  5. os.mount(bdev, '/ramdisk')
复制代码

% y4 ?  R' `* D7 D0 P/ @) h7 k8 J; o4 `+ A+ g4 H
  T" t- J- Z' ]; W

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

  1. class RAMBlockDev:
      X7 b3 @6 {8 M7 J5 ?7 [0 ^
  2.     def __init__(self, block_size, num_blocks):% l) k2 x* q8 o8 b
  3.         self.block_size = block_size- g9 X* X+ y- [& A) h
  4.         self.data = bytearray(block_size * num_blocks)
    ) @: p9 k4 t1 c1 V8 ^1 b
  5. 9 E2 ]) i' D* @9 E
  6.     def readblocks(self, block_num, buf, offset=0):
    - y' \4 Z' S  F- G( v% O
  7.         addr = block_num * self.block_size + offset
      R0 Q" q/ `4 S1 D) d
  8.         for i in range(len(buf)):
    / ~  R/ s$ a$ M+ H- Q0 _4 ~& f& f
  9.             buf[i] = self.data[addr + i]! ?3 U6 X2 P7 N4 G0 y  v% R0 c4 h
  10. , t) q6 g! B& {2 h
  11.     def writeblocks(self, block_num, buf, offset=None):& b7 I% k6 F2 O1 ~  f2 o, u
  12.         if offset is None:" ^1 I6 Q  B/ w, g& C
  13.             # do erase, then write. ]; m" X9 w5 c" n6 H. L
  14.             for i in range(len(buf) // self.block_size):
    " h8 e- Y# q) \( k; H9 k0 D
  15.                 self.ioctl(6, block_num + i)
    7 U* E* G% j3 B: |
  16.             offset = 0
    # Y/ y) S. E! B1 \0 p
  17.         addr = block_num * self.block_size + offset' m* m2 `% ^( n. g9 O
  18.         for i in range(len(buf)):2 I( }4 X; O/ v2 J3 G) a
  19.             self.data[addr + i] = buf[i]$ u+ P; J; B8 B/ [, e6 x1 u
  20. 7 Q" T( r8 G- W+ T$ N" S+ w! p
  21.     def ioctl(self, op, arg):5 a  L( q$ I/ j" I4 x: X. u
  22.         if op == 4: # block count
    / s9 L; F/ M' r/ I* ^# C5 A
  23.             return len(self.data) // self.block_size
    # r* {! B" d/ Y& S0 R) |
  24.         if op == 5: # block size; J6 Y& Y0 }- K5 H
  25.             return self.block_size( s" W+ U/ I2 m: d* F. {
  26.         if op == 6: # block erase
    / i) v& E7 ^, W+ {! Y
  27.             return 0
复制代码

, e; u! H7 \5 S/ I. \, V" K5 `1 W
) t- G7 U9 h* u( {1 h* u$ O

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

  1. import os
    * ~, \+ K: o3 b$ Z" B& K) a2 |2 n
  2. - A- W2 M% k" d0 W8 K: D# [
  3. bdev = RAMBlockDev(512, 50)
    % `, P" y4 @* X3 f
  4. os.VfsLfs2.mkfs(bdev)
    % N; E4 ]9 a" [
  5. os.mount(bdev, '/ramdisk')
复制代码

4 I& j8 y, {0 q- S5 `" I8 k9 i  |( \! t5 b' M, D( S

2 ?9 n  E: z& N# z: }

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

  1. with open('/ramdisk/hello.txt', 'w') as f:9 M: J4 d) l. A- S9 ^
  2.     f.write('Hello world'). `5 Y9 `/ o+ Z$ p( y1 s
  3. print(open('/ramdisk/hello.txt').read())
复制代码
7 z# v( C/ ~: {0 S1 s8 }. Z

7 n. i7 l" E3 V* ^1 \$ h/ r8 A" Y  \, _# v

" t; Z" |: r5 k# s' E* |

% q' C- c, b8 K7 R1 f! ]3 @1 r文件系统

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

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

* s, G- Z0 p# i4 e, C  X
FAT

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

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

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

  1. # ESP8266 and ESP321 C3 S" W& c2 g* d$ w
  2. import os6 r) {& F. B( y) U' v3 j& B
  3. os.umount('/')0 [5 ]1 r: g4 V4 R
  4. os.VfsFat.mkfs(bdev)) z- [- |, U4 R  ]
  5. os.mount(bdev, '/')
    . B& T5 j' \5 a

  6. / x# `( ]. i- _) z/ P& d3 T0 h7 x& C4 c
  7. # STM32+ Y/ E! {: n* A8 }
  8. import os, pyb, O* ]: N' C! p
  9. os.umount('/flash'), x' |( \( v7 l$ m5 N. J: X
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    % V) ]+ Q. u+ J3 _. i
  11. os.mount(pyb.Flash(start=0), '/flash')
    8 F& ~4 R6 }( @0 s3 J, [) {' M
  12. os.chdir('/flash')
复制代码

6 @- @! r  U0 F; h7 v$ n8 s' z4 Y& j$ [2 V( K0 R
; F. c' y7 b$ I& v

2 n: D' l& V+ t% t- fLittlefs

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

笔记

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

& X4 V- P% ]9 V1 I1 o

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

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

  1. # ESP8266 and ESP326 z; V5 J- R; Y0 j- U" y0 |7 y) n
  2. import os
    5 C! ]8 `8 _0 G$ U2 R/ H; F$ U1 O
  3. os.umount('/')0 Z9 Q, u4 B4 K/ x7 E. v
  4. os.VfsLfs2.mkfs(bdev)/ u! v0 Q: [  S; |' Z' [
  5. os.mount(bdev, '/')3 l( U# T2 f5 I1 }7 B
  6. % ^* C, Y* R. ]! E
  7. # STM32
    " o& Q1 V1 L" p
  8. import os, pyb
      i% {. \/ p1 x
  9. os.umount('/flash')" H/ {; \8 z, N1 q% l4 f7 a' R6 Z
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    / E& S3 L' Y: a
  11. os.mount(pyb.Flash(start=0), '/flash')$ ?. {6 Z8 r( O) _& U2 ^: E  s
  12. os.chdir('/flash')
复制代码
* J+ Y9 P& J: W1 h+ w

* S, H, m0 \* F' U$ [0 P8 u& C/ d5 `

% t1 B2 k8 q8 H* i. x混合 (STM32)

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

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

  1. import os, pyb
    1 @" T. m, _6 Q
  2. os.umount('/flash')& e# a6 }: ?5 y+ ]
  3. p1 = pyb.Flash(start=0, len=256*1024)$ p! \- o* X0 ^9 C/ K
  4. p2 = pyb.Flash(start=256*1024); f: d. Y) P8 T4 T8 M# \
  5. os.VfsFat.mkfs(p1)3 v$ i8 v6 _% g/ c/ ?: b
  6. os.VfsLfs2.mkfs(p2)( t% f8 z" o4 b  C
  7. os.mount(p1, '/flash')0 y( j$ E" F& I2 c9 Y! O
  8. os.mount(p2, '/data')
    " i, G8 P7 b3 r6 U: t- \5 J
  9. os.chdir('/flash')
复制代码
! }2 Q' G5 Z2 V4 d  Y0 _" o

2 C. n' i+ ]! |# X4 b' Q% x* c: k0 e0 j& z

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

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

  1. import os, pyb
    . f% R0 G" Z  q4 R$ V" E) c
  2. p2 = pyb.Flash(start=256*1024)4 h2 j& S5 a! ?8 }, \2 G" p
  3. os.mount(p2, '/data')
复制代码

  S/ S8 R( @4 [1 b: A/ T* |
) a$ H% S. B  ~, T* |6 o. ~! S6 w% _

来 boot.py挂载数据分区。


2 t. Q! P( r. s+ w+ T! E" T& h混合动力(ESP32)

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

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

  1. import esp32, os
    ! ]3 C% X+ _/ g, E6 X% A6 G
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')! l/ \- C4 N: E4 _# B0 v
  3. os.mount(p, '/foo')
复制代码

/ M" j: D) v9 v, Y; M  E3 P9 v: ^0 M' Z+ i/ @  O

8 J' Q& H# V' t& O
4 g% Q& i# W! b7 s
! ~5 y8 z! g+ S1 H
- B* t7 t! S& \6 `: ?% G& q9 H2 e

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-19 08:33 , Processed in 0.187200 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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