micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 178147|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3238

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


/ [* T* H3 O8 f, t% y0 u  u6 m& 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 上,主文件系统挂载在 /。


) e5 l! \0 W% |) l* u( O- |8 S块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


- A: t, L2 ~' }' a# Y" {ESP8266

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


) _( W( m0 R1 d3 }, g, I' [0 x# L1 WESP32

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

! [5 y1 Z7 o1 ~0 O8 D& n( T

# L" h2 ]+ ?$ G# _自定义块设备

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

  1. class RAMBlockDev:
    7 E7 \& M; r) `8 O. ?
  2.     def __init__(self, block_size, num_blocks):" p  I7 Q7 S2 l
  3.         self.block_size = block_size
    ! W# v& W" g- p4 ]7 n0 W# u9 s) R' B
  4.         self.data = bytearray(block_size * num_blocks)5 K$ o0 _) O% H
  5. 6 y; }! z0 A, `& J: W0 o1 R! a
  6.     def readblocks(self, block_num, buf):
    , ?1 `1 ?" J  B
  7.         for i in range(len(buf)):
    6 M8 N) U/ l0 I( z# y
  8.             buf[i] = self.data[block_num * self.block_size + i]
    # D1 @# W' B+ ^

  9. 0 R- b( F$ a7 c/ ~0 u& \  E* u) Z
  10.     def writeblocks(self, block_num, buf):: i5 v& [3 E( p. p2 F5 q
  11.         for i in range(len(buf)):7 M7 {8 z0 c! v7 W0 m( o' s- h
  12.             self.data[block_num * self.block_size + i] = buf[i]
    8 w8 r2 b4 Z2 A& V
  13. 8 S  n% z4 j# R2 `
  14.     def ioctl(self, op, arg):
    2 _, K5 E2 [' _( g3 @$ n( }+ S
  15.         if op == 4: # get number of blocks
    + p% z6 g) f; E7 Z- u# v9 N
  16.             return len(self.data) // self.block_size6 V0 }2 V3 G; d% r. t+ O
  17.         if op == 5: # get block size
      ^& q& o5 c# h) D* i2 V/ z
  18.             return self.block_size
复制代码
) q7 \; ^  W1 M9 j

- I- Q; ~0 g3 L9 {
& _' A' _0 }8 o* z4 i# l

它可以按如下方式使用:

  1. import os) A7 \: }; K$ s& @

  2. ( u3 q7 d6 |( s2 P
  3. bdev = RAMBlockDev(512, 50)
    , L& j0 x) A7 X0 F' |+ X4 L
  4. os.VfsFat.mkfs(bdev)8 O* Y1 V- D. ^
  5. os.mount(bdev, '/ramdisk')
复制代码
4 N% d- Z( Z5 G+ B

! G3 F0 y: L4 z) F( T
9 Z& {1 R/ o; {: \9 v' M) b; R

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

  1. class RAMBlockDev:
      M% E. O7 h# S1 \
  2.     def __init__(self, block_size, num_blocks):) l) Z, M) a/ D% I% f
  3.         self.block_size = block_size' x1 z+ m9 F) R! N( G! R
  4.         self.data = bytearray(block_size * num_blocks)
      q# L9 B8 `& B

  5. 7 g# U( t6 k7 V
  6.     def readblocks(self, block_num, buf, offset=0):
    / }6 Z  |+ U, S7 i/ i
  7.         addr = block_num * self.block_size + offset$ @/ P: e% k7 @; i: `
  8.         for i in range(len(buf)):, H% }" ?4 w( d. I6 D
  9.             buf[i] = self.data[addr + i]* j4 Z- m$ f/ E7 Q# u
  10. 2 \4 B( J. R( [. a3 t6 U) E' j
  11.     def writeblocks(self, block_num, buf, offset=None):5 C: t5 a+ d1 }' v
  12.         if offset is None:; j' N3 s# A, D
  13.             # do erase, then write
    $ m( C9 v- ]; N" N
  14.             for i in range(len(buf) // self.block_size):
    3 [3 p6 {3 p# ]
  15.                 self.ioctl(6, block_num + i)
      U. ?" c' N  V3 Y$ @. h
  16.             offset = 0% z3 ?. Z/ B$ ?! i
  17.         addr = block_num * self.block_size + offset
    - T$ ^1 J& k6 L
  18.         for i in range(len(buf)):% @; ^0 `4 v+ H
  19.             self.data[addr + i] = buf[i]3 i4 g; f- W. j0 u5 H/ H3 O

  20. 3 v+ e1 p; U0 g$ j
  21.     def ioctl(self, op, arg):
    + ~/ \) H0 a+ ^; g
  22.         if op == 4: # block count4 A5 e& W" _/ V% F* n* v5 F: ]
  23.             return len(self.data) // self.block_size$ e: t' p& U- j6 G
  24.         if op == 5: # block size' d' k5 B: j$ B( f  u2 }3 }$ p
  25.             return self.block_size% \. s# b% C/ Z/ ^
  26.         if op == 6: # block erase/ C& z* e$ R: h* ~& u
  27.             return 0
复制代码
5 b2 c0 n; }: `5 V8 r
- y3 B4 N0 G: Y, o. [

) L/ J- i+ A( M3 T2 W1 e

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

  1. import os
    1 P7 |+ Y3 _% |4 V

  2. 2 g" ?( k6 b7 C
  3. bdev = RAMBlockDev(512, 50)+ R7 n, D8 ^) R4 [. G
  4. os.VfsLfs2.mkfs(bdev)$ @! y+ ]' g* J* y
  5. os.mount(bdev, '/ramdisk')
复制代码
& S) R" E+ [' ~; d: p. C6 ^

, m$ x. }3 K  k- m; P
- N1 |2 f: y( @9 |% T% h5 N

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    , j# ^* ~. u4 w% F8 X
  2.     f.write('Hello world')
    1 k, ^5 w) [7 ^! h4 O; `- @# K
  3. print(open('/ramdisk/hello.txt').read())
复制代码

8 R- m7 M5 d1 E+ @. E& I1 O  r, L4 N) |

' d% r, ?4 t9 V, t9 ?" W% I9 Y, f* `7 a4 U! N/ W+ }- s

! e, _* I2 ]4 C# V文件系统

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

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

7 \! M% ]/ C) u' H! @' a
FAT

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

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

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

  1. # ESP8266 and ESP32
    # A" D: D' [8 ]+ f! P. a
  2. import os
    8 Z, y( d' c0 p. I
  3. os.umount('/')( S$ P/ X+ [5 Y7 U, x2 c: k
  4. os.VfsFat.mkfs(bdev)* i. l6 d/ D" X5 u2 i7 g
  5. os.mount(bdev, '/'). f0 l, C% _$ \2 E
  6. ! f+ h% h7 j4 |) W8 y1 v
  7. # STM32; U- ]! h  q. l
  8. import os, pyb, o$ y! W" ?/ D0 p5 q  w
  9. os.umount('/flash')
    * E/ K1 T3 z- E+ ^6 m
  10. os.VfsFat.mkfs(pyb.Flash(start=0)); q% c# z, q; z2 A2 b3 y* d
  11. os.mount(pyb.Flash(start=0), '/flash')
    % a8 S9 y1 z) n1 N: H9 m, a# z
  12. os.chdir('/flash')
复制代码
; t/ b4 U& A& v& S9 ^. |& \& I( _
8 d9 B, V7 s1 Y

9 R' i- T( d& D- C
" l# q0 c+ @1 o% z- z2 {Littlefs

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

笔记

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


8 t2 V$ Y4 Y6 t! \' b! |& j9 C( [

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

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

  1. # ESP8266 and ESP326 f! T( J& s4 a' Z4 K7 s1 y5 u
  2. import os
    / D. g$ a+ U6 ?0 F- O' V& p5 ~
  3. os.umount('/')
    2 D" X' w" }9 w! ~
  4. os.VfsLfs2.mkfs(bdev)
    / x% J- Q9 R- Y7 W# j
  5. os.mount(bdev, '/')* e0 R) D* K3 D

  6. 3 L2 B; |* Y+ a  v2 m- t9 x5 J
  7. # STM32
    & k* y+ C; U% a8 F8 j) u
  8. import os, pyb
      w9 m: ]8 \5 {9 Z0 v0 ^2 [* \. d
  9. os.umount('/flash')
    9 D3 y0 l. H* K' M8 M' B
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    , K  c& K  x# d, R1 s0 ~  G
  11. os.mount(pyb.Flash(start=0), '/flash')
    % M) m; s4 u' k) A5 C
  12. os.chdir('/flash')
复制代码

, y% }) W& V7 X% r# |) p) w. h! h
, S, B0 i6 i3 V' U; D8 \% y1 E
2 m0 F0 k2 ^" ?/ ^% F; C6 V4 b  U& Y( G: u, t
混合 (STM32)

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

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

  1. import os, pyb) |$ ]2 \# k% z/ M
  2. os.umount('/flash')) |0 _* ~8 p7 R2 s: b
  3. p1 = pyb.Flash(start=0, len=256*1024)# K1 Y9 o1 a% r! {/ v/ s
  4. p2 = pyb.Flash(start=256*1024)
      e$ J8 c' `0 M
  5. os.VfsFat.mkfs(p1): a( j$ E2 y" d- \/ L
  6. os.VfsLfs2.mkfs(p2)
    4 x7 c+ b$ x2 e" b4 |3 I
  7. os.mount(p1, '/flash')
    0 b( A2 t; T7 |* n5 A
  8. os.mount(p2, '/data')* ^& H* x& l2 c  f
  9. os.chdir('/flash')
复制代码
6 _0 a; ?2 C5 n6 p; u3 I# n+ r
5 v8 W* i- m9 S+ w& b
/ ~! Z5 m$ v% ~8 W8 w% G$ J

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

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

  1. import os, pyb
    1 _0 I4 x! L8 h3 x- Y; u) O
  2. p2 = pyb.Flash(start=256*1024)! J# c/ Z- F3 K6 V% v4 h$ y8 U  Q
  3. os.mount(p2, '/data')
复制代码

1 S* z: _- ^% i1 e
* A; _# s. f* d( C; z- n2 Q! f  p2 u, k* c" L

来 boot.py挂载数据分区。

+ Z1 F% Z9 c; ?
混合动力(ESP32)

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

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

  1. import esp32, os
    . i  z5 _- d( [3 t- N! q6 o
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    : I1 R' k) e8 W0 m$ {  K
  3. os.mount(p, '/foo')
复制代码
" X& p5 R/ {9 v6 Q6 i) w6 u- l

% C: w, B& z( K: W8 J* t- {
7 v. `( }8 E" G8 T
" R0 [9 z, ~- }7 o! ?" h

4 q! |7 L  q3 w9 y% ^; j2 F8 r6 w3 o5 p' C

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-1 18:52 , Processed in 0.171600 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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