micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 178310|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3238

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容

, f4 W( h- q1 H/ T

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

) n4 I5 W7 f/ Q6 `+ N
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


3 {. k, t. G5 Z( O( }' S* @ESP8266

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

3 E* \' v  D" r* Z
ESP32

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


4 U2 ~4 x) d8 B* X4 b2 [: n
! M5 S" ?$ J. h, A' Q* f自定义块设备

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

  1. class RAMBlockDev:  L& a/ u' z5 {
  2.     def __init__(self, block_size, num_blocks):
    7 @7 D6 f0 ?9 i" i) y8 n8 B3 S
  3.         self.block_size = block_size
    & D. V1 ~- f( W% e7 Z& b- {
  4.         self.data = bytearray(block_size * num_blocks)( S+ K; ~( c1 p4 V

  5. ( `  G& ]# |" w9 h0 c; `: Y
  6.     def readblocks(self, block_num, buf):3 u. O6 D" O% d9 s5 q
  7.         for i in range(len(buf)):
    3 H, g+ z7 H. g; _7 m+ f& w
  8.             buf[i] = self.data[block_num * self.block_size + i]) p) x3 T! o) G( C$ R

  9. # p# @4 M& C# y0 ~, K& K/ o
  10.     def writeblocks(self, block_num, buf):: p0 I7 D( @" S- r, T
  11.         for i in range(len(buf)):+ ]% F' f" U8 |6 f8 i9 k. U
  12.             self.data[block_num * self.block_size + i] = buf[i]9 @( t: [& c+ F0 @

  13. 8 `9 Z( ^, X$ ^2 f
  14.     def ioctl(self, op, arg):. z# j5 ~1 ?2 f, q5 U+ f
  15.         if op == 4: # get number of blocks
    : H# ?1 T* Q# u( J# [6 t
  16.             return len(self.data) // self.block_size
    ! S' D0 d: ^; S0 K) o4 F: G
  17.         if op == 5: # get block size
    & I+ C# b, d) M
  18.             return self.block_size
复制代码
' }* p2 Q; ^$ e2 o0 L! Y5 J1 j: t
; f8 A+ \" P8 e% r  @2 Z. p
/ K' M+ a9 e0 c) o

它可以按如下方式使用:

  1. import os' H  b3 Q1 }% ?% B; D/ x
  2. . u! R- g: p9 @1 K$ o" T2 z7 T
  3. bdev = RAMBlockDev(512, 50)
    " q) C! l, Q. Q7 I! F" L) q. w
  4. os.VfsFat.mkfs(bdev)8 ~! l4 I* B) \6 M$ p; @5 n. h) ^
  5. os.mount(bdev, '/ramdisk')
复制代码

+ X6 I- K! W; h6 C3 p4 e
+ r/ m( M. ^8 L4 a& s6 \+ Z' [
$ }( O9 ~2 ^  i7 T

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

  1. class RAMBlockDev:
    2 G  N1 V4 r  m$ i/ h. R
  2.     def __init__(self, block_size, num_blocks):
    - o/ j1 F. C3 C3 p* q- d+ e) x
  3.         self.block_size = block_size
    8 V! I( u: a" O" N3 G0 v- C/ o
  4.         self.data = bytearray(block_size * num_blocks)- S, H3 R  @) B3 R' q8 R
  5. 9 V/ _" t# U% o  |, h  L+ _/ @
  6.     def readblocks(self, block_num, buf, offset=0):
    + Y* x1 L% D' G" K; V- M* e& `% u3 `
  7.         addr = block_num * self.block_size + offset4 \; _8 \% c' q  T! c
  8.         for i in range(len(buf)):
    " \) i4 t; }* q& `5 r/ j$ W
  9.             buf[i] = self.data[addr + i]
    * }% o. P6 j6 z1 j! \2 b

  10. 3 m! c' U* n2 c7 e0 @8 Q/ n# f4 n' R
  11.     def writeblocks(self, block_num, buf, offset=None):0 B" t7 M2 u! |& @7 O5 a4 A
  12.         if offset is None:
    / K4 F, `# L( [! v
  13.             # do erase, then write, F! C1 E  `: h* o$ O
  14.             for i in range(len(buf) // self.block_size):- ]7 k) U% ?$ g( ]* q' T- Y
  15.                 self.ioctl(6, block_num + i)
    9 z) V8 l3 W7 x3 B3 {& l. d
  16.             offset = 0
    2 m6 {) v" `4 S( a. T8 N' P
  17.         addr = block_num * self.block_size + offset! z4 y" `+ F# A* R
  18.         for i in range(len(buf)):4 L- w4 |" O8 _# `
  19.             self.data[addr + i] = buf[i]. U2 ~( R2 s% Y1 ~, b4 K) f/ [7 c7 @

  20. % g9 n5 ]! f$ y9 r3 X0 K9 F
  21.     def ioctl(self, op, arg):9 W2 N# M6 i# A! P
  22.         if op == 4: # block count+ @# S% Z, k0 y9 I7 X
  23.             return len(self.data) // self.block_size; M4 H- R% s5 n, C
  24.         if op == 5: # block size/ V& W. h* K9 ~  u+ {
  25.             return self.block_size
    $ u+ P! W* ^- N& I" v. g
  26.         if op == 6: # block erase1 _8 l- @5 Q  e5 ?0 N
  27.             return 0
复制代码

: T: {5 H7 ?% S* I( i! K1 @; G! c/ ^+ B5 u

- X% m7 e* C1 Q8 q( |" n

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

  1. import os0 B2 S; e6 f7 z- T5 E

  2. : k% r* L* C+ C3 m! g
  3. bdev = RAMBlockDev(512, 50)$ V$ V4 W( [" x6 K" j, D, f
  4. os.VfsLfs2.mkfs(bdev)2 s/ s3 @; Y. ?2 |, |
  5. os.mount(bdev, '/ramdisk')
复制代码

2 f; \9 o2 P) C- g6 v( @" M
# A* R* o* n( P/ }, D- z/ W# U" w" U- ]

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    ) G$ k/ |/ f1 p: z/ [: b# E0 z) ~1 E
  2.     f.write('Hello world')
    6 R$ Q$ J! V" f% ~, I: M
  3. print(open('/ramdisk/hello.txt').read())
复制代码

9 T$ r; {8 J$ o1 M) N- H! E
1 k  u, ]4 L  W3 K3 M
2 B9 d0 [" I+ j* V* b7 Y! S; a$ e4 n* V! h4 q0 W" s( F! n! c
: E7 P0 w6 c6 D+ `
文件系统

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

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

9 ?/ g. B( i/ ], Q: v
FAT

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

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

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

  1. # ESP8266 and ESP32+ x/ w5 P5 \: `+ V
  2. import os
    ( M! U) O4 w1 r& _. x* }
  3. os.umount('/')
    ( w; h; l: [! L4 e: W# W+ V$ B
  4. os.VfsFat.mkfs(bdev)
    . @: D; u7 s! h9 R6 m/ p0 v
  5. os.mount(bdev, '/')
    2 b7 p, E$ T" h* j) [. S  g

  6. ; }! |* ~# O# o- I1 |
  7. # STM32
    0 H5 n& J2 B  M4 G
  8. import os, pyb, p1 p6 d3 @# v
  9. os.umount('/flash')
    7 z! A/ j0 X3 G' v2 s
  10. os.VfsFat.mkfs(pyb.Flash(start=0))9 _8 c& j5 O* {& s: n1 h
  11. os.mount(pyb.Flash(start=0), '/flash')
    $ w, _3 P0 F/ N7 ^& V8 G1 g
  12. os.chdir('/flash')
复制代码

' I+ V' o2 G" ?$ @
" p1 {& j+ V( Y- J+ _. b1 t! N. ^$ X5 ?2 K& Q  S

: g4 H3 g: S7 ?Littlefs

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

笔记

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

: Q  z: W" T% ?3 P4 |% Z' v

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

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

  1. # ESP8266 and ESP323 U) d6 L% B) G3 t: d, x
  2. import os! C% z3 j4 J, N4 ^9 o. w+ M
  3. os.umount('/')
    5 y  @0 |6 k5 V0 h1 d  k1 |
  4. os.VfsLfs2.mkfs(bdev)
    3 ]- t/ }/ q9 d, K2 h% P$ K
  5. os.mount(bdev, '/')
    ; Z/ x7 h. f9 D0 m2 o( R4 j/ |

  6. ( J9 ?; W( c2 I& a! K; z
  7. # STM32
    * h! \! f0 I4 |, Y9 O
  8. import os, pyb7 P. _' N3 r! ~% X
  9. os.umount('/flash')' D  j- `3 X. K" j) o4 K5 x
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))
    5 k3 L' w( G  Z  j; ?+ B: V
  11. os.mount(pyb.Flash(start=0), '/flash')
    2 B6 h( {  Z! h: K/ A& p: E: ]
  12. os.chdir('/flash')
复制代码

9 d7 D  v+ d8 Y) c; y
- K2 q4 U  q+ D7 J+ z7 ^: q6 `
! I. T- a! J# j! C4 Q8 x; `2 M. E6 }7 a) f2 L
混合 (STM32)

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

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

  1. import os, pyb
    1 A, g: ~8 f- f, F/ ], \3 B  L4 f
  2. os.umount('/flash')" V* m) E5 H& b: {; _4 Y1 a
  3. p1 = pyb.Flash(start=0, len=256*1024)  V& Y1 m1 |) {0 `6 w- e4 D4 X3 ^% E
  4. p2 = pyb.Flash(start=256*1024)
    / b  v% N( G/ h" ]7 `( N7 t
  5. os.VfsFat.mkfs(p1)* B6 v' p# Y- B) }
  6. os.VfsLfs2.mkfs(p2)
    , |+ G4 b  C2 M! r/ ~! q
  7. os.mount(p1, '/flash')
    3 X* N1 Y* |- y# u9 b
  8. os.mount(p2, '/data')) W8 N" v( N/ R* S: }6 C& V
  9. os.chdir('/flash')
复制代码

* y# i2 l/ g5 Z+ P5 d4 R: _2 N  G, d7 {2 c

2 f5 I& v4 A) Y2 B+ k$ x* ~- V4 N

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

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

  1. import os, pyb
    3 I0 Y3 f2 ?3 W) p* j0 U
  2. p2 = pyb.Flash(start=256*1024)2 W6 f& ^0 ^# Y
  3. os.mount(p2, '/data')
复制代码
8 T+ L3 a# a& ~0 v
( V  ?9 G0 x* Y) e$ e; K- m

/ \$ R. {% ^. X3 G8 ^& g) [; O4 S

来 boot.py挂载数据分区。

* Z! k+ O0 x& v3 q  X) `0 C
混合动力(ESP32)

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

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

  1. import esp32, os
      m- ]! q" A- k
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo'): i% a! ]* a- ?3 r, g! V: S# n
  3. os.mount(p, '/foo')
复制代码

" `% A) j  Y" e: o7 y/ @2 |1 W3 Q# W2 ~1 P- d& T) r& z

2 U9 b) K) Z3 S
4 C8 D6 ?" @0 A: y! y$ c! M, V

2 b; @" I+ P8 g, U5 J3 M' W/ ?4 Z0 v0 M7 d3 p8 H

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-1 23:40 , Processed in 0.187200 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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