micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 77390|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

2698

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

2 o8 z6 _# G* r5 ^- O  ]

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

% r/ |; C  q3 c! }
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

! {5 e. ?/ a+ A* a2 `2 Z4 j% r
ESP8266

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


: d6 b; t5 }" r% H/ DESP32

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


9 \- Y& i3 Z9 l! q' L
! e: z) `" ~' J5 f' [5 r自定义块设备

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

  1. class RAMBlockDev:
    8 S' I' I- |5 b0 K# y6 k# C, N, q# P
  2.     def __init__(self, block_size, num_blocks):
    & c9 q/ e3 r& v5 e+ K- W8 X5 \
  3.         self.block_size = block_size
    1 E1 j$ w* Y! e; `4 Z
  4.         self.data = bytearray(block_size * num_blocks)5 C' `8 ^) U" i/ G# `

  5. ' t- ]2 m. }! U; e
  6.     def readblocks(self, block_num, buf):
    ) J, ?# _; s- i7 k7 a' r2 s( C
  7.         for i in range(len(buf)):' p) {0 S# t9 F0 K& x0 w7 o
  8.             buf[i] = self.data[block_num * self.block_size + i]
    " I4 w# l4 O5 |/ f

  9. ' U' Z! N8 _( Y0 |% i
  10.     def writeblocks(self, block_num, buf):& m7 u& a- s5 x" c* ~
  11.         for i in range(len(buf)):& V% @0 a" ?1 Y+ _+ x% u
  12.             self.data[block_num * self.block_size + i] = buf[i]. k8 a! ?) m8 c6 H/ K, p5 v" r' L8 z

  13. 0 C+ w: K. L& B# g
  14.     def ioctl(self, op, arg):
    ) h" E& |; {6 W0 R% U6 g9 \
  15.         if op == 4: # get number of blocks4 f( B" n" I, H' _- k) J7 ^
  16.             return len(self.data) // self.block_size' J) q; i1 t5 s& R
  17.         if op == 5: # get block size
    + _1 j5 }, H2 C1 @0 [
  18.             return self.block_size
复制代码

3 C, Z2 {% p/ a
: C' x+ Y" d- k0 D5 K1 u& n  A& U3 p5 q5 E

它可以按如下方式使用:

  1. import os6 Y) z) X$ H" M# n7 S: M
  2. 9 P6 z& k* E1 k( N
  3. bdev = RAMBlockDev(512, 50)/ H5 ]! w; J% T; \2 t. I$ v: w
  4. os.VfsFat.mkfs(bdev)
    2 S; i& u( u' A9 T' v: Z
  5. os.mount(bdev, '/ramdisk')
复制代码
8 d- M: d' H: C3 z% ?4 j5 ^

$ l' D. D* d( b: r8 i- ?+ h0 I) K' f, M& P

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

  1. class RAMBlockDev:
    - S6 d  N6 q( z4 T/ P* G
  2.     def __init__(self, block_size, num_blocks):
    ( E  ]0 h0 v4 s& v
  3.         self.block_size = block_size
    7 W+ S8 D6 r, R8 s( O0 d
  4.         self.data = bytearray(block_size * num_blocks)
    0 ^$ _, T1 K6 q0 c+ x; p* G
  5. 6 j/ D6 X: e5 }, S6 r
  6.     def readblocks(self, block_num, buf, offset=0):
    " s) P0 I; d0 {& P; `  v
  7.         addr = block_num * self.block_size + offset
    . @- c8 g& j# `0 g- v; n& M1 U
  8.         for i in range(len(buf)):2 H- E! @. G9 r4 b5 b! J0 Z$ R
  9.             buf[i] = self.data[addr + i]% [8 f( \0 b7 j- z
  10. + ?& C7 F7 x1 J0 v, ~; q1 a
  11.     def writeblocks(self, block_num, buf, offset=None):$ x5 o/ L$ [! T3 O2 n
  12.         if offset is None:' n9 A3 f1 r( [% N; o) n
  13.             # do erase, then write
    ; ^$ a' O; c- U5 X
  14.             for i in range(len(buf) // self.block_size):3 J' M( u* v+ v" l; J' A& J6 N% @
  15.                 self.ioctl(6, block_num + i)/ z/ w. s; G; o0 K
  16.             offset = 0
    6 T2 M# P& }7 p
  17.         addr = block_num * self.block_size + offset
    0 l( {9 c3 s2 B1 o
  18.         for i in range(len(buf)):
    - l, a' M' P5 U1 J
  19.             self.data[addr + i] = buf[i]
    / o- [) Q6 H- A5 c

  20. % R" B& k; G$ e. Y2 z
  21.     def ioctl(self, op, arg):9 d' q" T6 G4 ^7 a. p
  22.         if op == 4: # block count" t+ Y; X  j8 J+ C/ \2 w, \
  23.             return len(self.data) // self.block_size$ e, v5 |9 v- D, _* u8 x* h
  24.         if op == 5: # block size
    7 d: M# ^4 |! I" B& `4 F/ P/ N
  25.             return self.block_size
    ! G; ?5 b4 D: a4 g
  26.         if op == 6: # block erase+ W" N9 x, [: M8 R0 y
  27.             return 0
复制代码

& M/ H+ ]8 B7 |9 J2 @+ b: d9 o' n$ p$ T

# c/ X8 o4 J9 Y5 c

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

  1. import os
    + n& ~, N* }* i# @2 T2 l- K

  2. $ e1 v4 u  d+ S( N9 U0 D
  3. bdev = RAMBlockDev(512, 50)
    8 z6 ]  g& X4 b9 h
  4. os.VfsLfs2.mkfs(bdev)
    ! j: [( E" `+ \) ~1 R
  5. os.mount(bdev, '/ramdisk')
复制代码

2 J0 [9 w1 k% Y8 J& }9 E$ f7 C2 z% q$ ?9 B. w2 r: \

7 A9 N& o4 Z) o, s/ {

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

  1. with open('/ramdisk/hello.txt', 'w') as f:; Y7 {$ c% D9 c1 A$ P: D( y
  2.     f.write('Hello world')
    - K1 B2 z9 L8 h
  3. print(open('/ramdisk/hello.txt').read())
复制代码

2 c2 }! d4 D* K* E6 K
9 ^! c$ m. v, ]' i+ j% C; U* e. u& m& N; i+ G
2 W/ p+ R( @; I2 l/ p

  {  }/ w% b" `1 P& l3 r2 Q/ C4 s文件系统

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

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


! @' n- ~* T7 D5 ZFAT

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

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

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

  1. # ESP8266 and ESP32  T- E* L7 C* G: U, Q* V" P' K
  2. import os% p& ?+ Y9 h3 H  [) K
  3. os.umount('/')
    2 T; A# I$ D- Z- q; a) R$ A. g  i
  4. os.VfsFat.mkfs(bdev)
    ) n( K  A0 i6 v' Q
  5. os.mount(bdev, '/')
    - x. J3 A% y" }# M2 ~

  6. ) W7 Q4 F1 x0 b( o% Y( p$ T8 l% v
  7. # STM32
    " z. @5 ]) l( B1 c
  8. import os, pyb
    7 J2 }9 Q4 n- K/ @5 n  B% P- Y& a3 U
  9. os.umount('/flash')
    7 Y0 O/ i: h- W( J/ X" n& `
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    ! T3 W  t6 T6 U5 m" k( C
  11. os.mount(pyb.Flash(start=0), '/flash')6 I3 y: _1 m4 }# k3 P. x
  12. os.chdir('/flash')
复制代码

* X; V/ o. b; g3 W/ D
: j7 N1 r" g$ J: O/ i
' _" x& {, Y# @1 |2 ~6 f0 M
& ^  y+ A8 z" ?7 L. a4 K/ s4 K9 jLittlefs

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

笔记

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

! D' P+ D2 J( A: n

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

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

  1. # ESP8266 and ESP325 o" ~5 [9 Q4 m0 ^
  2. import os
    % [* X; O* J( n! S* d* q  h
  3. os.umount('/')( g! b/ p4 W8 Y" j! y( p' h* R3 E* V
  4. os.VfsLfs2.mkfs(bdev)
    % h1 z& w+ c' I: q5 d; p, D
  5. os.mount(bdev, '/')
    & }1 N2 L- a% Q  A- v, {0 T
  6. 3 ~$ K( N3 R  W
  7. # STM32
    / A3 w  W) g6 C, S+ a9 a
  8. import os, pyb
    & ~7 @/ W0 M& D2 l2 Y
  9. os.umount('/flash')
    ; s; a) C8 i! Y
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    ! _, k, W9 l: n' N* q/ r( l7 t
  11. os.mount(pyb.Flash(start=0), '/flash')
    ! w1 m: `  N/ L3 f4 M
  12. os.chdir('/flash')
复制代码
; x' |) p3 Y/ A1 g* }) I- [
2 q/ J$ h) j$ \! M% Y
, Q; l0 ]: T2 K9 d' V
: Z) C# m8 l; x% U2 H5 Y# c
混合 (STM32)

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

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

  1. import os, pyb# k! L! o. T# j2 [, B9 d
  2. os.umount('/flash')( @2 g; e" A  i+ b
  3. p1 = pyb.Flash(start=0, len=256*1024)* N, U; h4 C9 @8 g' x; c
  4. p2 = pyb.Flash(start=256*1024)
    : a& r* K% }' F2 ]% ~. t% `
  5. os.VfsFat.mkfs(p1)6 R% ?- E* x; e# x
  6. os.VfsLfs2.mkfs(p2)7 f- G1 C( ?' Z+ e- y$ G% Z6 I
  7. os.mount(p1, '/flash')
    , ~0 i: H- k. w- h5 x
  8. os.mount(p2, '/data')
    5 H9 N9 h+ v6 `) r
  9. os.chdir('/flash')
复制代码

+ d( _$ [( P7 C# d( [+ R' B, p# s0 m5 ], U, D# y
4 x5 s0 b% h6 L( o# o7 n& @9 J3 ~

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

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

  1. import os, pyb
    % P8 z( K, R- w
  2. p2 = pyb.Flash(start=256*1024)/ U& Z% h& G0 s" x2 |6 Z
  3. os.mount(p2, '/data')
复制代码

0 i) h+ Y3 E- a0 [
5 z, j+ b! X1 b( Y/ r" Y6 k' Y+ a
- `0 k2 K- k5 O& X1 r3 q

来 boot.py挂载数据分区。

. ~6 V: ]  c  h0 @
混合动力(ESP32)

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

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

  1. import esp32, os
    % d! U/ u# W3 W6 v
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')/ u1 {7 P& a/ w( Q  Z
  3. os.mount(p, '/foo')
复制代码

! N3 s6 ~/ Z4 h7 h) K+ y$ I! U9 p# E; H" P5 [- M' G5 c

0 e3 N; @/ i( B& Q8 |  u, A7 L7 j! r

! I9 G, O  K, A+ P& Z
0 J  Q( C% U7 S* K* Q) A4 s

本帖子中包含更多资源

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

x
回复

使用道具 举报

0

主题

2

帖子

4600

积分

新手上路

Rank: 1

积分
4600
发表于 2024-11-25 10:14:39 | 显示全部楼层
回复

使用道具 举报

0

主题

2

帖子

4600

积分

新手上路

Rank: 1

积分
4600
发表于 2024-11-25 10:16:31 | 显示全部楼层
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2024-12-4 16:59 , Processed in 0.202800 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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