micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 180361|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3244

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


9 e# Q9 H: R# N9 _

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


* R9 d( `6 y& \; O% y+ P$ H' h块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


4 K$ j3 y' b* a' r2 \8 O2 ?ESP8266

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


+ l: q( u: |; ^2 Z: yESP32

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

- r# P: D. N3 Y, N& D& A% j+ \

' g5 \; M' Q6 i" @# F4 l" h自定义块设备

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

  1. class RAMBlockDev:* H0 H. x2 W* z, J, s
  2.     def __init__(self, block_size, num_blocks):
    ; @  R, q5 T& I- L9 p
  3.         self.block_size = block_size; d- J# J& b* X  b$ F4 X6 E
  4.         self.data = bytearray(block_size * num_blocks)
    ! e+ t4 H- h+ C7 T: J- W0 k' s! f

  5. * U5 v4 G3 e# u: F% D& h" L( V9 i2 `
  6.     def readblocks(self, block_num, buf):
    / A8 k+ l# I. M! C3 j; D& k
  7.         for i in range(len(buf)):" I* T# U4 x0 q9 U/ A0 G1 C! ^3 {
  8.             buf[i] = self.data[block_num * self.block_size + i]
    4 S/ u; s. g9 f5 T6 i5 ?! p3 U1 ~
  9.   K4 H: S4 _; q1 r1 j
  10.     def writeblocks(self, block_num, buf):+ B& o  \% R; Q
  11.         for i in range(len(buf)):# I& U7 K, K3 a, T; ]5 ~3 K
  12.             self.data[block_num * self.block_size + i] = buf[i]. p5 }% G$ e  B" G0 s

  13. + h3 s9 Z$ Q  H5 b9 }2 G
  14.     def ioctl(self, op, arg):: z8 l* e& ^; R
  15.         if op == 4: # get number of blocks
    1 X9 V: P* Q2 e1 k! }
  16.             return len(self.data) // self.block_size
    % d4 X( P; q4 k4 `
  17.         if op == 5: # get block size
    3 `& m: R" h, T) }
  18.             return self.block_size
复制代码
$ E: e. |* t) [( ~7 Y7 ^2 P. z
9 M1 \% k0 j- G5 z

# I/ h$ I# c4 i# I" t) `) ]) m

它可以按如下方式使用:

  1. import os' l" \4 q- g7 U* G, A% O8 j; L
  2. # t9 W0 f+ Q, w  v
  3. bdev = RAMBlockDev(512, 50)
    ' \( r# C" G1 G. j, A7 K  N
  4. os.VfsFat.mkfs(bdev)
    $ k# @: u7 ?/ y* b) I
  5. os.mount(bdev, '/ramdisk')
复制代码
  v* d. u( p1 F

! O" t$ e) a# {. m, I8 T6 @: C2 ^" W& \4 s) G  f1 R( L# K

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

  1. class RAMBlockDev:
    * T5 M: u; x# o$ z: h6 u& m
  2.     def __init__(self, block_size, num_blocks):5 a7 h: c0 W' p, i9 Q
  3.         self.block_size = block_size
    ' P$ e6 S9 x7 }! c4 }; J0 S
  4.         self.data = bytearray(block_size * num_blocks)
    : c4 m6 c/ E' ~6 C
  5. : A& l0 s, H) u5 l, I# D  B+ k
  6.     def readblocks(self, block_num, buf, offset=0):
    % E1 l( C7 Y! W/ Q  |
  7.         addr = block_num * self.block_size + offset
    " A- u5 W5 w5 m: f' s& i; J& a6 q
  8.         for i in range(len(buf)):
    7 q) `4 n1 R9 s9 A' k( `8 ^
  9.             buf[i] = self.data[addr + i]
    0 H  Y& j' w; O, i4 o( p
  10. 3 O- }; b( f/ j7 j9 r. k
  11.     def writeblocks(self, block_num, buf, offset=None):
    5 Q3 z& A6 Z. z
  12.         if offset is None:" L# C" o) r, J8 @1 g2 T1 Y
  13.             # do erase, then write& b+ S( r1 n# }5 f
  14.             for i in range(len(buf) // self.block_size):
    8 o' Q) f1 W" C" m
  15.                 self.ioctl(6, block_num + i)
    ! W$ D: }' ~+ E! x" J) \
  16.             offset = 0' i! Q( d% L" `0 g5 p: l0 s9 W' d
  17.         addr = block_num * self.block_size + offset
    6 e1 n& n; m: `* `2 y
  18.         for i in range(len(buf)):5 t. x! H, K' z" M4 `! G/ Y" j
  19.             self.data[addr + i] = buf[i]
    ! a9 Y4 W! p: D6 K! @
  20. 6 m: t# m- {, Z* X. Q; ]" W
  21.     def ioctl(self, op, arg):
    , i) Z& u! g3 J2 x3 ^* j9 V; F
  22.         if op == 4: # block count
    4 a& ~0 c6 X4 B! S3 R4 I
  23.             return len(self.data) // self.block_size
    7 n) g. B% l5 @9 R6 p8 B+ r
  24.         if op == 5: # block size4 z7 N' M, ^% n3 n4 ?3 i) e) Z- W
  25.             return self.block_size
    4 e) J$ T) R3 j5 M" @
  26.         if op == 6: # block erase# d& `+ ]) F9 L+ m! u
  27.             return 0
复制代码

0 ?4 O( H4 i9 n/ E  ]4 l, n5 C6 y3 N& y9 O/ q! X3 _  h

0 D% c7 g0 [5 n' b0 ~; [

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

  1. import os
    : c' V+ z4 M# S, Y
  2. 1 i+ q. R) O( B
  3. bdev = RAMBlockDev(512, 50)
    * f* c/ U- N) G% v/ v
  4. os.VfsLfs2.mkfs(bdev)7 n4 z, y0 S+ x
  5. os.mount(bdev, '/ramdisk')
复制代码
+ E# z0 |  I/ R* i" K$ }& E
  h. J+ I/ N8 M1 F9 _2 U7 P4 Z
5 Y5 A! w4 U+ y6 R; F1 h

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    2 u/ l# u3 q2 a" s# `7 s( O
  2.     f.write('Hello world')
    7 f9 V  {8 h1 X! v
  3. print(open('/ramdisk/hello.txt').read())
复制代码
; f0 l% N7 g  W5 V" p# @
2 N; b/ g7 d+ [* H/ g9 \; }
  r4 L( t9 W, h! \4 y" z, n

( E# E4 m' j3 ^
# X4 P$ t1 X. w) F7 c9 f1 Q! i
文件系统

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

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


$ _, I  s" X- u4 XFAT

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

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

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

  1. # ESP8266 and ESP32& o7 H; _0 v6 q
  2. import os) }9 G2 B% I- A
  3. os.umount('/')3 W. o4 R4 D1 I% u; g9 Y( K, I
  4. os.VfsFat.mkfs(bdev)
    8 k+ y% p/ y. i" r; y& h
  5. os.mount(bdev, '/')( p* i3 D& {: K
  6. ! o* {) k( b/ s
  7. # STM32( K( M$ r4 c" i) p0 m
  8. import os, pyb9 \4 c- x5 E& K$ i
  9. os.umount('/flash')$ N. ?. `" ]& N" V
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    $ f* [6 c  \  ^0 S
  11. os.mount(pyb.Flash(start=0), '/flash')! S8 P0 Q/ `/ E/ B+ m" D
  12. os.chdir('/flash')
复制代码

4 x  V, r$ @/ Y( h$ P9 s2 ]: Q1 e5 Z) B

" y: d  G. ?* P# j. v9 y- d( g; {+ a) l& V, n; s& _- B! `
Littlefs

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

笔记

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

- \4 K% {7 M- e. _5 |

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

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

  1. # ESP8266 and ESP325 e- A5 y2 I. p% `' R5 U! W' _# \
  2. import os
    0 L* S  K) N2 n6 B: |
  3. os.umount('/')
    + K3 ]1 d2 V. t8 ?/ i
  4. os.VfsLfs2.mkfs(bdev)
    9 ^! `  u! X5 d& V. e& L
  5. os.mount(bdev, '/')
    ! O3 J! T; g  v$ s. ?5 W! t6 e5 @

  6. . {/ h; d3 T& K& `
  7. # STM32
      j9 R1 q( F  r/ u0 ]
  8. import os, pyb( o# y0 R+ O; _) j* ]" Q3 l
  9. os.umount('/flash')3 C5 E3 C6 S; Y. ]" ^1 R% {
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    8 _( O6 W* Q; K3 K9 H! G* r
  11. os.mount(pyb.Flash(start=0), '/flash')$ V0 t0 q9 |1 p  O+ l3 Y9 |
  12. os.chdir('/flash')
复制代码
9 Q2 T" j1 H, x; q! X- |
0 C8 N( r- t5 b$ R3 A

! x6 U* l6 {4 c* }
3 Z1 F3 H, x& A5 i) N" y  i混合 (STM32)

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

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

  1. import os, pyb
    3 o  f& r; @1 g7 S. q6 }1 v& y
  2. os.umount('/flash')3 @+ \; g1 h; G" }0 j% v) ~) L
  3. p1 = pyb.Flash(start=0, len=256*1024)
    # a& x5 s% ]( X9 U' I- i4 I5 b- J
  4. p2 = pyb.Flash(start=256*1024)" L" }, S2 a0 U! d! P1 ?$ J
  5. os.VfsFat.mkfs(p1)' S! L) W6 E8 A
  6. os.VfsLfs2.mkfs(p2)
    * h! F9 V: v1 s1 n, R
  7. os.mount(p1, '/flash')  M" E2 K) z. l: ]! L  j
  8. os.mount(p2, '/data')
    3 P* \2 \( Y9 `5 M# f% X
  9. os.chdir('/flash')
复制代码
7 a- c& A$ \1 ~  q5 A. {% u& W

* |* C" y- g# g" y& a0 T& \
& v, G9 H" H2 M

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

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

  1. import os, pyb$ _; |+ D) i" Q
  2. p2 = pyb.Flash(start=256*1024)
      t9 [9 T5 z1 l2 S( S
  3. os.mount(p2, '/data')
复制代码

; A+ H5 h) q4 X& U
% }" G' n, ?1 B% x: [0 s6 |1 c) s: p
$ R' g  m3 Q) Y/ X2 p' |

来 boot.py挂载数据分区。

4 I3 f. q% @2 i; {  l! |* G3 T( K
混合动力(ESP32)

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

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

  1. import esp32, os/ m; m/ ]0 c9 R8 v: k! s- F) Q
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')& b* V5 u% u2 B! ]/ z: t' Z1 D
  3. os.mount(p, '/foo')
复制代码
% ~2 ~. [$ W  l
8 P$ [  t2 a5 _

3 k% e9 }) G- W1 d- A* e' M
2 \; ]+ Z. V: M
5 G+ e" e- H% A- O/ j/ f0 e
( h2 i* T) d) O1 _

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-4 11:18 , Processed in 0.187201 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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