micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 201720|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3334

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


6 z" ^  u& @' `* m3 u" s- u  L

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


+ u. L' `, d/ l. S块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


" V6 B4 B  O, v6 n  |( c  V4 H1 UESP8266

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


4 L1 R& S: Q/ j, K  lESP32

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

! j, {1 I9 {- o

7 \7 r( g+ K0 ?1 i* w$ B. g自定义块设备

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

  1. class RAMBlockDev:
    ; S1 j" R$ W( K: Z, j, |
  2.     def __init__(self, block_size, num_blocks):* d! Y2 k; c% v% ]! |
  3.         self.block_size = block_size; D; G' \3 @+ M( L0 j( T" x
  4.         self.data = bytearray(block_size * num_blocks)" {# c& _5 E; p. R0 Q/ \3 f# E2 m+ A
  5. & ?  R+ }4 {( w0 p& _
  6.     def readblocks(self, block_num, buf):
      U; }8 ^3 e+ G
  7.         for i in range(len(buf)):
    & Y) r! |- n+ U3 |) k+ H, Q
  8.             buf[i] = self.data[block_num * self.block_size + i]
    1 r& B! ~& b$ M( \1 I9 |: i

  9. 8 Q3 y8 r3 P/ X9 l* i2 L
  10.     def writeblocks(self, block_num, buf):7 Q2 F. F, j3 e/ {
  11.         for i in range(len(buf)):
    ( Q7 V( q+ b% A6 R8 a0 N. P# ]
  12.             self.data[block_num * self.block_size + i] = buf[i]: _, I7 p; |' M+ W
  13. " Q1 S+ v5 f: u  n# ~
  14.     def ioctl(self, op, arg):
    6 t9 ?* D2 W$ f
  15.         if op == 4: # get number of blocks
    : @9 w# O2 |& q% ~- z7 v- j
  16.             return len(self.data) // self.block_size
    ) A# n8 Y0 f  M7 D
  17.         if op == 5: # get block size# N% t$ t  d; d( k7 z+ c) O
  18.             return self.block_size
复制代码

8 o& B9 o& b: a; {. _1 m2 M/ K- O1 f6 o# Q  }# C  `

( n1 n! {1 V7 P

它可以按如下方式使用:

  1. import os
    7 ^7 F% v$ [5 \7 n7 S

  2. / ]! U) w- @, \( p* x& g
  3. bdev = RAMBlockDev(512, 50)
    ) Z& |9 a' k( [0 N7 w1 V7 T
  4. os.VfsFat.mkfs(bdev)
    ; ?& R6 Z1 f, m4 F8 J
  5. os.mount(bdev, '/ramdisk')
复制代码

/ S2 K5 Q3 [9 ^# W* n/ ]5 p8 C  }3 M2 b

) z( p4 Y1 o9 i0 G4 T; ]

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

  1. class RAMBlockDev:+ v1 N! w& ]8 a4 ^9 A% n! a  |" S
  2.     def __init__(self, block_size, num_blocks):
    ' q# ^# K# i2 z# @. z
  3.         self.block_size = block_size
    2 p  s3 k2 g, u4 w
  4.         self.data = bytearray(block_size * num_blocks)+ j% o0 E9 H4 |% C

  5. ( P& R& ~$ y; ?# Y: Q
  6.     def readblocks(self, block_num, buf, offset=0):
    & B* b! A6 ^: w% g
  7.         addr = block_num * self.block_size + offset
    0 s" T2 m; O: P
  8.         for i in range(len(buf)):
    ! _  L' G( X: ]0 q# l0 l- A! Z
  9.             buf[i] = self.data[addr + i]. V9 ^. M, k2 J' v! }
  10. ! o6 U" L- U5 L% l9 l: h$ ^  I
  11.     def writeblocks(self, block_num, buf, offset=None):
    / H3 a! M9 C' `! B. y7 z
  12.         if offset is None:# n1 b9 b+ U% [
  13.             # do erase, then write& ~9 N- I5 w6 O. w# p* B/ [" x; g6 b
  14.             for i in range(len(buf) // self.block_size):$ ?' \- a, k7 z$ Y
  15.                 self.ioctl(6, block_num + i)# A% b5 K/ l3 E/ y0 X* s
  16.             offset = 0
    3 C+ k# B' h( j
  17.         addr = block_num * self.block_size + offset
    3 b5 R: q* p- H
  18.         for i in range(len(buf)):
    # ?1 j% F& n" z5 C2 E
  19.             self.data[addr + i] = buf[i]
    % D% G4 e/ g& h6 a/ F# w6 G

  20. 0 X% T( N2 B, f
  21.     def ioctl(self, op, arg):. l. ~+ U4 L: a9 p+ Y
  22.         if op == 4: # block count9 W5 w* k+ N, O; _/ n& w
  23.             return len(self.data) // self.block_size8 N3 S9 u2 A* K
  24.         if op == 5: # block size' J% ]2 q9 w, N: G
  25.             return self.block_size8 R* [) ^8 V$ S
  26.         if op == 6: # block erase1 `) \+ m$ @; T1 M0 I" z
  27.             return 0
复制代码

9 _/ K1 B; |) o9 \
/ p' I: K# c. z
  m6 [! y2 V9 \6 H: S4 i" O' a

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

  1. import os
    4 e, M$ S& v4 s! X2 n6 e9 f+ v

  2. ! g9 A. E6 y, Z/ O7 z4 N; A
  3. bdev = RAMBlockDev(512, 50)8 Z0 ~  l9 c6 i
  4. os.VfsLfs2.mkfs(bdev)
    # T  y: t' ~$ q  Q
  5. os.mount(bdev, '/ramdisk')
复制代码
+ n! h% _6 \% z  n4 v! W

% y9 A; e" u' \- K
, @& y; u9 J5 r( ~3 A/ f

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

  1. with open('/ramdisk/hello.txt', 'w') as f:5 K% t9 O8 P" a3 u
  2.     f.write('Hello world')- V; I% Q# p# W" ~  Y
  3. print(open('/ramdisk/hello.txt').read())
复制代码
( v. p6 S( H8 R6 v' N: f+ g3 W

, u: Q" d, w' |, I+ L) Y( [& X  O; J3 {$ h0 ~/ l
- ?$ U% T9 F& o8 G# c# B# l# ]+ |
) G0 ?0 d& t" Q& o8 t3 C
文件系统

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

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

) N+ \1 t- ^3 w  S
FAT

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

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

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

  1. # ESP8266 and ESP32
    9 b' R* f& n' {( r) I7 Q4 P: L- U
  2. import os
    4 I. {: q# S/ S, _
  3. os.umount('/'), r/ t+ j" z. U. {. t% e4 Z. i6 M
  4. os.VfsFat.mkfs(bdev): j) [" c: q8 T3 t- Y
  5. os.mount(bdev, '/')
    4 n# l0 w, B0 C" ^! S

  6. ; a) H" l9 W4 N  m0 B. K! Z1 u
  7. # STM32
    . x" m2 t" P2 o6 t) L
  8. import os, pyb! W& B3 Y% Q, S- V% M9 V2 t/ t
  9. os.umount('/flash'), ~" O/ f# c" @4 v
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    8 V$ U, V$ }( y' L8 P2 ?$ {5 j
  11. os.mount(pyb.Flash(start=0), '/flash')% n: H& l! P" I0 n
  12. os.chdir('/flash')
复制代码
$ C+ \- {4 \" H0 G& |! Q  q

6 R! I7 R' Q% [9 w. ]5 G
7 O# \0 i+ J* h* [/ A9 Y6 a. g: ]3 V. @' A5 D% Z
Littlefs

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

笔记

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


9 O( l1 Y& x1 O( \0 C: d

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

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

  1. # ESP8266 and ESP321 s  g0 q/ t# }& C' h
  2. import os3 l+ r& e1 T/ B
  3. os.umount('/')
    : v- ]8 t" N% \# y3 Y" |
  4. os.VfsLfs2.mkfs(bdev)& G2 `0 O* {* K* @2 Y
  5. os.mount(bdev, '/')2 ?: H5 |8 F* l1 P: z; _! m
  6. 1 f" z8 e5 `4 B; y' g1 c
  7. # STM32& l4 B, m4 S3 `$ x9 M  d
  8. import os, pyb
    $ A: A9 G2 V3 `
  9. os.umount('/flash')
    2 G! R: W" P4 X3 o! F
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0)). Y$ @6 D9 S- S+ c0 K
  11. os.mount(pyb.Flash(start=0), '/flash')
    2 }: X6 u  V8 _
  12. os.chdir('/flash')
复制代码
' w- ]" Z2 B: o0 T, ]+ J
$ V( o& Y$ }4 G& e5 f
) N1 X7 `1 U( K7 O4 V- e
. u# R' {/ Y* {. Z/ M
混合 (STM32)

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

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

  1. import os, pyb4 j5 I& M  k8 u8 U$ t) \) b
  2. os.umount('/flash')
    # N# Q- _, _+ G/ O, a
  3. p1 = pyb.Flash(start=0, len=256*1024)
    6 j# J" A3 D& v
  4. p2 = pyb.Flash(start=256*1024), B  m+ W. Q% y
  5. os.VfsFat.mkfs(p1)
    : s, G. M( ]1 w9 K9 C7 }
  6. os.VfsLfs2.mkfs(p2)& ?# G; P, Q7 @, b' {9 x2 \% @
  7. os.mount(p1, '/flash')
    7 d6 V3 A: Q2 o$ |9 O" m0 ]6 ~* h
  8. os.mount(p2, '/data')8 G+ {. F) ?+ u( L. O* ~
  9. os.chdir('/flash')
复制代码

' g' c' k% p) Y  K# x& e' y9 h9 T+ Z. r1 c

* C1 B0 W9 f! P, z# n

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

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

  1. import os, pyb
    ' Z0 {8 o- h! P3 L
  2. p2 = pyb.Flash(start=256*1024)1 E5 }" Q1 W# r3 j6 ?( R
  3. os.mount(p2, '/data')
复制代码

; h. j; B4 Z0 I5 r. I6 m/ s9 I: R3 h: D
' I3 K' o$ O* d0 {

来 boot.py挂载数据分区。

9 Z5 [5 C1 ~% O6 Y' j: l
混合动力(ESP32)

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

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

  1. import esp32, os
    % s  O% ^2 }0 {" y) O
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo'), D5 f: |, m3 |6 }) Q: Y! ?% t. q
  3. os.mount(p, '/foo')
复制代码

$ _( G6 z* O$ ?! t9 Z, w7 F1 L5 s+ Y7 j" K( `) w! I5 f' D" m
+ [9 _: S$ S* A8 c" P% y, K8 M

" O/ a4 G. c/ D5 Y, n& b- V! S
6 a5 r+ t8 x6 b. L; i/ D# G' O1 E

( J2 Z2 l" c5 i

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-9-19 05:36 , Processed in 0.202801 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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