micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 183523|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3256

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

# f/ }3 m9 b* c/ u- N0 o3 v

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


8 @0 h: Z( p+ v. ^  `4 w, h块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

3 |2 m/ Y$ ]# v7 ]/ ?4 ]0 x+ Y1 {% P
ESP8266

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

% ?! ^' R0 s# N5 K7 U  J
ESP32

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

2 H. _6 T, C: P9 ^7 [: ]
% N) ?+ l4 {: d' y
自定义块设备

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

  1. class RAMBlockDev:' I0 T6 p& P- t  l5 X: }, G- P
  2.     def __init__(self, block_size, num_blocks):, K: s& K+ l3 R6 I
  3.         self.block_size = block_size
    3 U2 [6 y- K# W  r
  4.         self.data = bytearray(block_size * num_blocks)# p1 o7 G( q- m0 a0 G

  5. % a: A! `) I7 R+ G, \6 m
  6.     def readblocks(self, block_num, buf):
    # g4 I# r9 ~4 |
  7.         for i in range(len(buf)):& `. p' @" C5 [9 q0 |% Y9 l
  8.             buf[i] = self.data[block_num * self.block_size + i]) p3 Q( u0 Y# ^7 O1 G7 }
  9. + ?3 {5 l3 F+ u: s) q8 m3 h, V( \
  10.     def writeblocks(self, block_num, buf):7 _% z5 T) U; Z7 ^0 ^+ C
  11.         for i in range(len(buf)):0 f& L; z* R, E. t: \& W
  12.             self.data[block_num * self.block_size + i] = buf[i]( z# L: x) Y- j1 }& }2 E4 H+ E7 X

  13. % C/ o. u% f5 ?2 w$ _8 @4 Y
  14.     def ioctl(self, op, arg):& u! I+ _/ s  |
  15.         if op == 4: # get number of blocks
    0 g0 K; K, Q  g" O8 V
  16.             return len(self.data) // self.block_size
    - u2 Y- |6 M2 u) f
  17.         if op == 5: # get block size
    ) m! O! y: A1 u6 |- p& d$ T7 `
  18.             return self.block_size
复制代码
$ O& O$ q# L) ~% a  O1 S

  z# o* S0 V. S* q5 w
* s# O5 z7 h0 m: w8 R( j

它可以按如下方式使用:

  1. import os  s7 \$ }8 U. Q1 j5 V
  2. 9 |2 f( k& b9 [3 H+ |
  3. bdev = RAMBlockDev(512, 50)! i/ F0 r! Q' U0 o
  4. os.VfsFat.mkfs(bdev); K1 S$ ~! S  k& k
  5. os.mount(bdev, '/ramdisk')
复制代码
) R2 L8 I& G! ?
$ @; ?. q% X( d/ S$ `
  L+ z+ w6 p- ]5 v- P

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

  1. class RAMBlockDev:5 x/ C0 P/ j5 A4 f
  2.     def __init__(self, block_size, num_blocks):% Z; r9 Q: i2 `+ l
  3.         self.block_size = block_size0 _. W7 @- f. ^2 r; L/ _  \
  4.         self.data = bytearray(block_size * num_blocks)
    , |' j( k9 U/ @( j: J; q( b
  5. - g+ x; g, I3 G- Q
  6.     def readblocks(self, block_num, buf, offset=0):( a! {2 O4 W6 F1 J4 p4 `  r/ h% s
  7.         addr = block_num * self.block_size + offset
    ( g9 W+ ^9 X5 ?2 l* U
  8.         for i in range(len(buf)):
    / b* Q  U; d3 N1 F, Y
  9.             buf[i] = self.data[addr + i]9 \& p" i* J: J. \9 l$ u
  10. $ P# |$ |" i- E4 [0 t9 Y) e6 X. H
  11.     def writeblocks(self, block_num, buf, offset=None):
    2 V, M$ x1 o7 K# ~3 J
  12.         if offset is None:: u% p! P7 |6 @- ]! R/ [
  13.             # do erase, then write
    : I5 D2 d3 J/ E) Y5 l& d+ S
  14.             for i in range(len(buf) // self.block_size):7 D; |1 ?* r& v
  15.                 self.ioctl(6, block_num + i)" Y( B: N6 {2 c" l5 `" n
  16.             offset = 0
    2 b# t* a; d6 G' l7 _0 K0 Q
  17.         addr = block_num * self.block_size + offset5 i7 ^, i) M( q. I
  18.         for i in range(len(buf)):
    " e' T% i* u  I/ O" o1 T. ~
  19.             self.data[addr + i] = buf[i]2 W* H2 U5 G7 M5 R7 Q1 ^, H* q
  20. . F3 `4 t0 a# u: N. w
  21.     def ioctl(self, op, arg):
    7 d3 x8 r+ P8 j4 ^* @
  22.         if op == 4: # block count* l" J3 u7 A. W1 j- Z' A0 k1 Q
  23.             return len(self.data) // self.block_size7 h  m+ @! h% ~6 p# ?7 y( s
  24.         if op == 5: # block size: S3 p9 I( [7 G6 J
  25.             return self.block_size
    . P/ I3 J* X9 q& w% c% c
  26.         if op == 6: # block erase7 L; m$ k9 j) A+ w: ^. B
  27.             return 0
复制代码

5 R$ r  v; I! P5 F* T/ @3 E5 t2 k, g1 r! ~5 n

1 }6 l' w. @1 a( g. o

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

  1. import os7 y7 u4 z. g1 C: z: ]- H  ?

  2. 1 @- N9 q8 c: _( r
  3. bdev = RAMBlockDev(512, 50)+ u5 b9 w3 Z( H0 C
  4. os.VfsLfs2.mkfs(bdev)% D, u0 {1 |! m# ], U
  5. os.mount(bdev, '/ramdisk')
复制代码
! G; M. e, g" O+ p  c: m3 q; [/ U
5 E5 n% V$ T& z3 C/ {: _. i/ O5 }
7 A& x( m6 m/ l5 ~

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

  1. with open('/ramdisk/hello.txt', 'w') as f:6 W+ f7 g. r2 U7 r; V, Q, K( l
  2.     f.write('Hello world')
    4 N  S( Z, ^: S7 m
  3. print(open('/ramdisk/hello.txt').read())
复制代码
7 }* \/ \3 x7 ^+ Z+ J
' G( r( Q( r: S' h6 |, r+ j8 E

) `5 T3 H' x6 y: \" J: k
: M  h) m0 [" W) `( h

# U: O, D! l- i7 b  R8 B* t文件系统

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

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

' X) K; v$ k$ C: J; g9 W. k) d' c$ R
FAT

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

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

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

  1. # ESP8266 and ESP32
    9 x+ ?( S8 e3 Q6 U" y/ h1 B2 [
  2. import os3 Q8 q+ k: X1 h/ f3 [4 b
  3. os.umount('/')
    6 o5 v; M0 ?& ~% \; H& ]6 b
  4. os.VfsFat.mkfs(bdev)# G4 l- l. w# L* j3 b. }
  5. os.mount(bdev, '/')
    9 l" H- `9 |( ^7 q
  6. & V; c0 C+ ^  c" S
  7. # STM32* \( f3 S: t& Y3 f
  8. import os, pyb
    . B0 u2 t& ?) _' d& r# `
  9. os.umount('/flash')
    0 q# y  Q: S' P9 |/ ^# q
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    / Y+ ?1 O, A$ D% S
  11. os.mount(pyb.Flash(start=0), '/flash')
    / [% U" U* U1 C/ ]' K% l
  12. os.chdir('/flash')
复制代码
- ]: x2 n. w# d+ W& H% p% T

% Y/ G2 ~& f8 n- f! B; F" F& o' b
" h* c: j5 p; }, K  q
: M7 m, g1 E4 c) n  L3 LLittlefs

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

笔记

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

, Y9 V0 l0 x# ?# K" A  S% ]' L+ s/ Q$ f

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

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

  1. # ESP8266 and ESP32
    " ~# \2 k) q' g  h5 g3 F. v
  2. import os
    - Q0 u, z1 C- E, Y' z7 p, e6 ]
  3. os.umount('/')  I! @, k; v, y" E% D1 C7 b/ `
  4. os.VfsLfs2.mkfs(bdev)2 Y) r2 Y; Q' O$ p% c" k7 I$ |
  5. os.mount(bdev, '/')4 n) V  ?- c3 b9 q" i* Q
  6. 5 S- l! r7 E  k+ l! U
  7. # STM32' C- f4 b6 D& {4 R# H8 F( O0 N) L
  8. import os, pyb9 P4 v# O% w4 V3 r# `3 n4 Q
  9. os.umount('/flash')$ e" e$ q0 `! B# E( E: Q
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))- ?% {' p. H  W& s6 S) {$ v( ^; s
  11. os.mount(pyb.Flash(start=0), '/flash')
    ) R( `2 f. h% G* B' N- a9 D
  12. os.chdir('/flash')
复制代码
/ I/ s/ n( x* E/ h  p
3 D, b5 S( A0 F( K" \

$ ]' K) |1 k+ ?. a$ ]0 {  E$ a4 h( i7 ?+ {0 G% ~
混合 (STM32)

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

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

  1. import os, pyb
    4 R! ]; `. B- f
  2. os.umount('/flash')8 j2 _8 {3 k4 [9 T
  3. p1 = pyb.Flash(start=0, len=256*1024)
    ( t) g9 ~4 `  I+ o+ M! x7 h
  4. p2 = pyb.Flash(start=256*1024)
    / d. l: X: B' I2 j$ h1 E2 a1 Y% C
  5. os.VfsFat.mkfs(p1)+ b) m4 m4 l' j" P0 m4 N2 P
  6. os.VfsLfs2.mkfs(p2)
    , t0 P( [+ m8 Y2 d) n
  7. os.mount(p1, '/flash')1 _$ q0 p$ i$ z: o1 t
  8. os.mount(p2, '/data')
    / p* b! c2 \  J( U0 u
  9. os.chdir('/flash')
复制代码

- S4 |9 w9 ~: _% ]! n- o3 q; O, a1 h9 T: O7 J) x

  `2 k# k9 s6 g$ j: r; S5 y" U

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

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

  1. import os, pyb
    + e+ H6 X1 O; y. r7 P
  2. p2 = pyb.Flash(start=256*1024)2 ^7 |# f9 O) F7 D9 q6 Z
  3. os.mount(p2, '/data')
复制代码

, ~: V8 x- e- h2 n0 _" O1 \; V' b. k; R5 |* i; s' Y

- p4 b( I3 y8 @/ s

来 boot.py挂载数据分区。

/ V; r( J+ w' f, o4 m9 B% a0 o/ [0 X
混合动力(ESP32)

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

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

  1. import esp32, os, C' ]5 N/ E' W2 ~: \
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    , g9 ~+ [% i# n7 P3 X1 g
  3. os.mount(p, '/foo')
复制代码
4 l- C& h0 _' @' @5 e3 _3 s

7 m& Q/ c% |# J1 b5 q
* W) ]# d, y) N& w5 l
3 L; f" b  F3 B$ B: g' M
, h; s. r. \7 [3 `
1 x. B/ h" g4 w! J( c

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-8 15:31 , Processed in 0.218400 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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