micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 181709|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3244

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

% b7 k% J# B: ?1 a3 Q

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

; \2 ~5 a5 c# |) i
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

' X1 U' s( \# X  {% F9 [" F. u, s- B
ESP8266

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

' x/ U. B' y: m5 W
ESP32

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


  q2 e9 F: s* _/ H) T- I/ c9 F5 T" V; b  N3 E! A8 }9 W9 D& |
自定义块设备

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

  1. class RAMBlockDev:; E9 Z# l6 m& c: ^$ k0 V2 G
  2.     def __init__(self, block_size, num_blocks):  Z0 x$ F( X' I5 N1 T& b3 ]/ z
  3.         self.block_size = block_size0 }/ \) ?: ]. J$ c% [
  4.         self.data = bytearray(block_size * num_blocks)4 r9 n9 ^* ^; F

  5. ' ~" @1 x/ e& y; y) o* h4 b. f$ o+ p% a
  6.     def readblocks(self, block_num, buf):& b: i5 T  K# |" k2 \
  7.         for i in range(len(buf)):; O( U* R3 _/ b! s9 \, M
  8.             buf[i] = self.data[block_num * self.block_size + i]% G4 {8 c. ]8 s# y& r
  9. 1 o" O* u* k1 Z7 `/ Z
  10.     def writeblocks(self, block_num, buf):
    9 |1 ~  r7 S% y* w
  11.         for i in range(len(buf)):
    , [( Q' e# c3 E" \) _
  12.             self.data[block_num * self.block_size + i] = buf[i]9 I! _% @  [8 ~! j$ a# P8 J4 U

  13. - l& a! ~$ Y2 e! Y9 U5 r- y
  14.     def ioctl(self, op, arg):
    3 A( ^2 h! [& N# C5 F) ?# z
  15.         if op == 4: # get number of blocks& W; `- H/ w7 z" r0 G
  16.             return len(self.data) // self.block_size4 j! ]( S# b% _& p0 \& Q3 }
  17.         if op == 5: # get block size
    4 X) S% Q3 \: d1 G8 m6 e
  18.             return self.block_size
复制代码
  W: C. f2 C' E7 d. g9 y# O
0 u% w) W: w" U

: m; z/ l* S$ s" g+ j$ K. [

它可以按如下方式使用:

  1. import os
    0 d8 L3 u8 O1 o, e3 s
  2. + d3 c7 N, n6 d+ _$ e
  3. bdev = RAMBlockDev(512, 50)
    , }* @( {4 {1 x4 F/ t
  4. os.VfsFat.mkfs(bdev)/ [8 M& _" T2 O0 G
  5. os.mount(bdev, '/ramdisk')
复制代码

2 q" N& @! b; B) a& U* ?
, E8 |9 j/ }0 K5 g6 [. R9 _  _0 Z8 I- e# f0 t  |

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

  1. class RAMBlockDev:
    # ?- V6 \& }! ?. u, A
  2.     def __init__(self, block_size, num_blocks):9 x) ?4 K- X% C5 ^: J
  3.         self.block_size = block_size7 q0 v4 R# _( K; j. X6 e
  4.         self.data = bytearray(block_size * num_blocks)
    3 s+ B! {( }7 h3 u7 h! T
  5. 6 h. T$ D8 @, b, R, y* Q
  6.     def readblocks(self, block_num, buf, offset=0):9 o# D2 d$ B' U; B4 d
  7.         addr = block_num * self.block_size + offset
    ! ^8 Y$ W4 ?$ D, T* G6 ^' c
  8.         for i in range(len(buf)):$ ~5 @& u! k: ?. U$ H* B0 Z
  9.             buf[i] = self.data[addr + i]( n+ Z% s0 y4 u) }! s- h" x  ^' h
  10. * ?" m( q: ]) p# c) v
  11.     def writeblocks(self, block_num, buf, offset=None):
    5 C/ |7 U* P' @$ ]9 L! o
  12.         if offset is None:
    - B3 ]+ _" C6 T
  13.             # do erase, then write
    4 b# M" W  ~6 X1 {3 M8 X
  14.             for i in range(len(buf) // self.block_size):
    : l: x1 U; V1 s
  15.                 self.ioctl(6, block_num + i)- V1 {  N8 A1 g4 c* D
  16.             offset = 0' Q0 e0 N* S) z
  17.         addr = block_num * self.block_size + offset, @0 W( @. }# i
  18.         for i in range(len(buf)):% b  ]4 O- }' D* {6 _; W! |# j
  19.             self.data[addr + i] = buf[i]. ~9 e" f& ?. A. j$ D% v
  20. ! i/ `6 V$ I, u2 s: e- s. x
  21.     def ioctl(self, op, arg):
    + q& E  O1 Q1 u% f; r# N$ N0 T
  22.         if op == 4: # block count
    2 x* S$ o* }5 R/ R
  23.             return len(self.data) // self.block_size4 c# M) Y% f0 B# ?. ~
  24.         if op == 5: # block size+ V- J4 F9 b' ^4 v1 y
  25.             return self.block_size4 I) _5 [$ m/ U( f
  26.         if op == 6: # block erase
    3 D8 _5 _* |: w
  27.             return 0
复制代码

4 ]7 @$ P+ D: P, o  U7 s2 k  o; I8 i' U5 d; R. M. S: L

& g3 q8 Z" X# B9 c3 f

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

  1. import os+ V7 r- @% ~( g- g
  2. ' d9 O- I: \' C2 v; x- k
  3. bdev = RAMBlockDev(512, 50)! t9 e( ^3 v- ]2 W
  4. os.VfsLfs2.mkfs(bdev)7 S" \; I2 w' J5 ^( k
  5. os.mount(bdev, '/ramdisk')
复制代码

4 m8 \3 s$ ]! x5 K, c
, {6 u8 H4 T1 E  L
* r* o3 e, A% w; C# l/ S+ t) t

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

  1. with open('/ramdisk/hello.txt', 'w') as f:5 ?6 C0 ^/ X- g5 Z3 }
  2.     f.write('Hello world')2 G. G( I! g$ [/ S! |/ e# g0 U
  3. print(open('/ramdisk/hello.txt').read())
复制代码
- ^. o% b1 \3 b5 ]: }; _

, q% |% D& F" [$ A! z, Y- V( V% x1 z6 e$ e# D* u

; `5 I9 @# I7 h7 @

& l* w1 D  H% q/ k. h& I2 R$ O( e+ w文件系统

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

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


, L& m; [9 l) `2 kFAT

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

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

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

  1. # ESP8266 and ESP32
    6 \6 o+ u: z. a' @3 q% W
  2. import os1 o) \, V; o7 o5 l5 b6 b
  3. os.umount('/')1 L/ d, r. L" Z
  4. os.VfsFat.mkfs(bdev)
    4 ^7 `9 M* j. h0 m- {+ h& P+ A
  5. os.mount(bdev, '/')$ q7 A" g' d! f2 W

  6. ) |" Q" f" R# N8 f$ Y; Q6 |2 l
  7. # STM32' o& `- Z2 k5 ~/ l% d2 i+ o
  8. import os, pyb" n- f) }! x, n$ X8 m( `( `# |4 A
  9. os.umount('/flash')8 ^5 A+ i% o0 P! W
  10. os.VfsFat.mkfs(pyb.Flash(start=0))
    ( v: ^$ R: `* C$ x8 o
  11. os.mount(pyb.Flash(start=0), '/flash')0 w9 B' E+ H) p. a2 Y) ^
  12. os.chdir('/flash')
复制代码

! z/ Z% V# p! G! D8 L, K; V2 ?! D/ x  r4 g" m# L
2 M( n* ^+ t! L; D, F! K( v
" G& G% O. H9 ?" ?; m
Littlefs

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

笔记

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


2 l' k& K; O1 M. c

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

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

  1. # ESP8266 and ESP324 n3 ^" |6 w- ~; B. [6 b
  2. import os( T- B7 {* ~: j/ j  D0 B) K
  3. os.umount('/')
    4 w$ k5 J( r/ ~* {. N, C# K6 w
  4. os.VfsLfs2.mkfs(bdev)
    ( t* r: v8 C/ W$ X4 f& @& N
  5. os.mount(bdev, '/')7 [6 f. N3 u0 {* w
  6. 3 [; X) B: @/ o6 w) q
  7. # STM32
    : w9 b+ ]. v6 [1 y! O0 `' X* _2 v9 e
  8. import os, pyb$ h8 g  T( V: H: s, {
  9. os.umount('/flash')- {& j% q& E' x% ^% @. Y* Q
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))0 b  A* ^' ~8 D6 N' {
  11. os.mount(pyb.Flash(start=0), '/flash')
    & l$ j' e( W: s. B& U
  12. os.chdir('/flash')
复制代码

2 m0 R5 j: ]' c! u% r3 e5 B
$ G& |3 [2 w6 g. n9 Q0 m2 I6 w" b: [! x, i

* |2 t, a# s: K* M' n: H' L混合 (STM32)

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

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

  1. import os, pyb  l1 K. N8 C+ e3 @9 Q! s
  2. os.umount('/flash'). B2 @3 p+ V1 m( e8 Q. r
  3. p1 = pyb.Flash(start=0, len=256*1024)
    % l% E" m  L  c7 C0 X
  4. p2 = pyb.Flash(start=256*1024)( r  o3 W7 _* f9 n" `! n
  5. os.VfsFat.mkfs(p1)1 k) S& B2 `5 T) ^
  6. os.VfsLfs2.mkfs(p2)' ?; [. {2 {; S
  7. os.mount(p1, '/flash')
    6 n. }" C" J+ t6 B3 P& M$ ]
  8. os.mount(p2, '/data')
    2 v6 w1 |  ?. X% q+ ~" Z
  9. os.chdir('/flash')
复制代码
0 W" C' H6 H2 ^: Y
7 j4 v* b1 U3 i8 u" u

8 j6 K: V9 ^2 s9 H3 G- D5 s4 n

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

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

  1. import os, pyb9 g6 i4 M) e$ O& C2 W& `
  2. p2 = pyb.Flash(start=256*1024)
    1 `- y% \' A' n4 ?1 v
  3. os.mount(p2, '/data')
复制代码

8 ~# C1 a8 r0 X2 L- w: C& J* t% K+ |# ?1 v

9 }" K* [. \* u; ]( F, S

来 boot.py挂载数据分区。

7 F1 f' Q$ H% X- L0 |4 e
混合动力(ESP32)

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

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

  1. import esp32, os
    3 H5 A; a1 X1 O5 z* c+ [6 n! f
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')( C; H9 Q' d. W4 b2 V
  3. os.mount(p, '/foo')
复制代码

2 n2 g. I" Q2 b, E) A
  }) K2 y" q9 C$ {6 D- s6 h% r
4 L& ]  ~/ Y: r! V/ A+ _5 l; M$ s& w! J

! f/ E& w: j' e/ {
8 D& O* |; F( \( E* I3 ]

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-6 08:06 , Processed in 0.187201 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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