micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 178535|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3238

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

0 ^. k6 `5 A* _+ {; X* v' V; e0 `

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

; E! R% R3 l9 S- R1 q5 |1 V
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


; P1 z6 A: D0 h  HESP8266

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

' }9 H! w, v0 M7 q3 E# ~& ?7 @. o
ESP32

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

6 g5 `, _' k- k* v+ {- w
) e* t4 J# I/ w- `; p) y7 e
自定义块设备

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

  1. class RAMBlockDev:& R$ V# m: ]  F
  2.     def __init__(self, block_size, num_blocks):
    ( Y/ H' g: W+ d3 R( L
  3.         self.block_size = block_size
    , |! R( e9 g/ k6 ?* E( T
  4.         self.data = bytearray(block_size * num_blocks)) H# g  p% E5 e* N# k, z" r: l
  5. , \9 n, t& s/ f. J' |
  6.     def readblocks(self, block_num, buf):
    / _8 [/ k7 q6 `- W2 [
  7.         for i in range(len(buf)):
    " O  d; T# t/ W2 Y5 a0 R  b
  8.             buf[i] = self.data[block_num * self.block_size + i]7 b9 _; g2 C" a! u5 g2 ^- T1 j! u

  9. ! _/ a' S% E+ g9 O
  10.     def writeblocks(self, block_num, buf):
    6 U# g9 C/ {' t3 ^) L
  11.         for i in range(len(buf)):
    & f9 a0 j6 R: V. W' `
  12.             self.data[block_num * self.block_size + i] = buf[i]
    ( t/ I  h1 o4 M% k
  13. ' S3 e4 E& Z' y2 J! i3 {  P
  14.     def ioctl(self, op, arg):7 b- Y7 Q$ E0 ~# C* S* y9 c
  15.         if op == 4: # get number of blocks+ R3 y0 U! P5 {. ]+ ?
  16.             return len(self.data) // self.block_size  v9 |/ K+ ^6 G4 A# g- N
  17.         if op == 5: # get block size9 Q7 X' s) d- i, |' o* l
  18.             return self.block_size
复制代码
& `; C' z6 D  _9 j5 I: w% A9 b

5 `( |' ?% b4 e1 u5 q3 H; a& v0 y# u1 D& y" n. p

它可以按如下方式使用:

  1. import os
    % C7 [. R6 D8 U: m7 E. \! Y. h0 n

  2. ' D& x" }' ~7 F  N9 D& J7 }: u- o
  3. bdev = RAMBlockDev(512, 50)
    ; E/ x# \. }$ M) i) V: v: H' C. b
  4. os.VfsFat.mkfs(bdev)  L( U: \' ]+ Q# ]  @
  5. os.mount(bdev, '/ramdisk')
复制代码

1 }  J. [6 u: q0 K+ Z) k  J9 _9 ~0 z2 U! [) t. C5 Y0 R! {. `
$ u% \7 t8 B# U$ ~1 `5 U, @

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

  1. class RAMBlockDev:+ H- [7 I1 o! W
  2.     def __init__(self, block_size, num_blocks):
    ) ]' p# U7 i1 Y/ b9 \
  3.         self.block_size = block_size
    ; z3 q7 P4 K  j! g% u' h) ~" l0 `
  4.         self.data = bytearray(block_size * num_blocks)
    6 v& p. Y( \7 B1 |/ r; G

  5. 7 P) z+ ?+ T8 v/ y# `" x
  6.     def readblocks(self, block_num, buf, offset=0):. V& Z4 f8 @6 @5 t
  7.         addr = block_num * self.block_size + offset
    " p3 G. G' _2 C9 A+ y% [8 n6 m
  8.         for i in range(len(buf)):9 h) C# b9 m5 b4 j
  9.             buf[i] = self.data[addr + i]# Q7 {, a% e; J) x, P: t3 J
  10. 6 L1 Z0 B) w. K. b
  11.     def writeblocks(self, block_num, buf, offset=None):
    $ }5 S9 u4 o" P9 }. B9 D- d
  12.         if offset is None:
    & i9 {8 Q- m- b4 p
  13.             # do erase, then write
    # q, M7 K( e/ b% A6 L: h
  14.             for i in range(len(buf) // self.block_size):1 j1 z1 G* _3 c* k* J4 [! j5 r
  15.                 self.ioctl(6, block_num + i)
    / z5 v( \" J- J; V, q) t  }
  16.             offset = 09 _7 d" J" l% n: h- l/ d
  17.         addr = block_num * self.block_size + offset
    ( f0 }3 @) y! d2 j9 G5 [
  18.         for i in range(len(buf)):
    ; q9 r( j7 P& a4 H; x' a
  19.             self.data[addr + i] = buf[i]
    / f8 M$ @  ^# E6 r: `, z

  20. " ?# }: b) `' x* p+ S" X
  21.     def ioctl(self, op, arg):" p/ \! M( v" h. }
  22.         if op == 4: # block count8 z/ H$ g) i5 q( x
  23.             return len(self.data) // self.block_size7 j6 T$ G+ F/ M. Z5 G( ]- K! L
  24.         if op == 5: # block size
    , s* N: @: p- G( d2 y0 d
  25.             return self.block_size$ J2 {: p8 r7 \" `( ?
  26.         if op == 6: # block erase
    6 q! A* M1 z+ M( o: X. d: x2 w2 E
  27.             return 0
复制代码

; u2 v! a! z  m6 i/ |$ D. p7 x* S' l# u6 ]5 |. T

2 i* u) y+ k& S" W. F2 P4 H

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

  1. import os) s( T& _9 Q5 M# I5 q* T: `- r
  2. # x9 Y$ u  Y# F2 z; {7 d
  3. bdev = RAMBlockDev(512, 50)( r0 u# D$ h2 z* g
  4. os.VfsLfs2.mkfs(bdev)
    + i) }3 T" d3 l1 \, V1 z
  5. os.mount(bdev, '/ramdisk')
复制代码

* g5 X+ Y. @+ L6 o. @! w8 L$ l1 r4 c
% P0 B% ~+ S- {# M: Z  X8 B
8 q: t0 X" q  W4 \) M$ I+ s9 U

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    * K/ c" G0 a" J$ }
  2.     f.write('Hello world')( \1 T4 ?. Z$ H1 ?+ m
  3. print(open('/ramdisk/hello.txt').read())
复制代码

6 E+ p- g. O6 r" F& i
2 S5 ]$ `/ e* q$ j% ?3 T
, n5 Z0 h) k' r; v$ {: [2 F) ~' P0 z) Q( Z% Z/ i4 L

3 U4 {3 E) |; m: d文件系统

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

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


9 [/ c8 ?2 Y3 y+ F& a0 g, KFAT

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

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

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

  1. # ESP8266 and ESP32
    ' o/ `" |* U. F- f, L
  2. import os
    ( e4 \6 `0 P6 A" d" n; g4 J
  3. os.umount('/')
    ! p# @! p. D& `
  4. os.VfsFat.mkfs(bdev)) H& W9 i; S' i" _6 l
  5. os.mount(bdev, '/')
    6 @2 T3 A7 L" D

  6. 6 z3 i! y9 k3 G; A
  7. # STM32) K" y" o) o- T; V4 i
  8. import os, pyb) o  [- J* `7 d* [0 M# S7 o5 ~4 `
  9. os.umount('/flash')
    , @, J+ [" U# v# V1 k
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    " R! M& X5 |" }+ ~4 N( w) D1 @
  11. os.mount(pyb.Flash(start=0), '/flash')
    4 {8 u* m8 I/ s) |( v% z5 q1 U
  12. os.chdir('/flash')
复制代码
; R! L/ i& l) {3 d% |
$ R- w2 a' V  s+ V, s
: _6 B0 Q; f5 ]# F

2 }1 @# Y2 K5 g& i9 p2 ^3 pLittlefs

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

笔记

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


: @8 c, ?: I4 Q+ l6 s9 w

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

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

  1. # ESP8266 and ESP325 O4 B: X7 q& T3 O# c' x3 l
  2. import os
    2 S0 T9 j+ V; _; R% E" p0 z/ c% g4 ?
  3. os.umount('/'), v* H- o7 h2 D3 ?* I0 U: I
  4. os.VfsLfs2.mkfs(bdev)
    1 j% Z- n, A) n& w( \* S% ~$ }
  5. os.mount(bdev, '/')
    & R1 L/ i! e& m. h

  6.   e4 \6 Z1 E& s3 V, ~6 t
  7. # STM323 }' z/ f# B8 p
  8. import os, pyb
    7 c: z2 {; v9 d' K
  9. os.umount('/flash')4 I3 F; Q* g; B& j5 t
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))  r: b' {/ R4 Y" N- J4 ?
  11. os.mount(pyb.Flash(start=0), '/flash')
    1 ]* Y. }0 E7 O! w
  12. os.chdir('/flash')
复制代码
* A* C8 Z/ y% P& o( T: I

( Q% I) r( O0 P
7 x7 @% l8 j6 n. T- n# ^0 p7 k2 |+ n% K7 c
5 u  e. h7 M& ^7 f混合 (STM32)

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

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

  1. import os, pyb9 o: O* w3 H" a3 J  T1 B$ x6 f
  2. os.umount('/flash')
    " t4 T5 {& `  ?1 y- g: ]1 d
  3. p1 = pyb.Flash(start=0, len=256*1024): @9 u" v) B: `! }4 x; f  W) d
  4. p2 = pyb.Flash(start=256*1024)
    # s9 Q' M% B7 l# Y' X( A
  5. os.VfsFat.mkfs(p1)6 M0 W) ]/ D# ]+ V
  6. os.VfsLfs2.mkfs(p2)/ A3 n4 w, d# p, K; P
  7. os.mount(p1, '/flash')
    ' I; n+ |$ K2 f/ n1 Y7 @+ N: p& |
  8. os.mount(p2, '/data')
    3 S5 A6 a5 }9 N! n3 B8 f
  9. os.chdir('/flash')
复制代码
. B% g1 v, z" H8 o7 ~) m! n6 {
* f6 S0 N0 D, J

& [' g' W% d% h- e  H: Y9 }

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

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

  1. import os, pyb, ^2 n: k1 G! D( l0 P4 K5 Z) U
  2. p2 = pyb.Flash(start=256*1024)
    - T8 c, r  s; ?
  3. os.mount(p2, '/data')
复制代码

0 F# e( A% M1 m
& n# o; T0 m( i5 A" s) P) Y' C4 j" a. @5 T

来 boot.py挂载数据分区。


. g. s3 x4 p! W" n混合动力(ESP32)

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

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

  1. import esp32, os
    + ^- r9 ]  P; `& I3 u: V
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
    ! A! g+ S! U' [. s4 T
  3. os.mount(p, '/foo')
复制代码
4 `; Q( @! R4 F: w) n# z

& B. C3 h% K% c& B2 b! r9 b4 u( {8 J! F9 ^# X0 A+ `
+ ~* g5 C' t; l3 E
7 T  V/ [; D8 u
8 ]) f, A! Y1 d$ A% g

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-2 05:31 , Processed in 0.140400 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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