micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 173360|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3236

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


) _0 U+ j5 A! j* @

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

/ F5 I* G, ]; v* P
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

- l, u+ k  M+ ]4 A
ESP8266

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

/ w! q7 U- R7 y) c/ R5 C. T/ b6 q
ESP32

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

' f+ Q8 t( ^  L' z! e4 y) F
" H, \4 v5 z. b
自定义块设备

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

  1. class RAMBlockDev:6 J8 z' j/ e( z( S$ k- U" q
  2.     def __init__(self, block_size, num_blocks):/ R/ K. |4 ^# }, d; [
  3.         self.block_size = block_size5 Q1 u; l( f) Z% y& F* F
  4.         self.data = bytearray(block_size * num_blocks)
    5 R  ], o: d0 v" a5 \3 Z
  5. $ A, @7 e# B" \" P. h
  6.     def readblocks(self, block_num, buf):+ l, R+ m$ ~6 o, O' ~! c' |7 I
  7.         for i in range(len(buf)):
    , |7 l+ e1 U9 `( g
  8.             buf[i] = self.data[block_num * self.block_size + i]
    : I/ x8 m- M8 G, z$ A

  9. + i$ a( N7 l3 j$ _1 o4 b
  10.     def writeblocks(self, block_num, buf):' \  d1 [" @  a! C: `) j8 Y
  11.         for i in range(len(buf)):  O; h$ S! c* _7 U5 w# x" H
  12.             self.data[block_num * self.block_size + i] = buf[i]
    1 {+ v% {8 E" f. N

  13. $ U0 S; I# ]2 q7 _4 u9 Q
  14.     def ioctl(self, op, arg):
    3 ]0 @+ M: p4 n' S& T( l( D
  15.         if op == 4: # get number of blocks
    + d) I- K8 h' S% z# M) S4 v4 u
  16.             return len(self.data) // self.block_size
    2 Z7 a- ]4 i) [7 T7 w
  17.         if op == 5: # get block size
    - w( T6 S6 Q! t0 [7 d/ u, G
  18.             return self.block_size
复制代码
+ @( f0 Q, S0 w8 U9 i6 }* V

5 v) ]! ]6 \1 v3 v  u3 w) Y$ n0 Q% H9 c1 u/ g! q( Q0 S0 N

它可以按如下方式使用:

  1. import os9 \1 L( ^, `* k% R. l/ N" L

  2. 3 X: |+ ?' P3 F8 q9 a3 O8 W+ m4 D
  3. bdev = RAMBlockDev(512, 50): j; o  ~$ \8 g
  4. os.VfsFat.mkfs(bdev)
    9 E' {* {1 {# |' }7 [+ Q! m
  5. os.mount(bdev, '/ramdisk')
复制代码
& ^1 o3 E* c4 L9 s/ c2 d9 O- p  C2 P
$ g0 c5 K# ~( w4 E5 s
7 X- z& ^. c+ [% s$ ]+ K/ D% _9 {

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

  1. class RAMBlockDev:9 S: @9 ~" N3 T  N% g
  2.     def __init__(self, block_size, num_blocks):
    % o/ y& N; S( E3 x& @
  3.         self.block_size = block_size
    2 y' L3 X9 p$ s9 f# h
  4.         self.data = bytearray(block_size * num_blocks). H0 L9 v% j- f) T

  5. 7 z$ P" E( E6 S9 f- L
  6.     def readblocks(self, block_num, buf, offset=0):
    1 d* K0 c) n4 `" G9 l; O
  7.         addr = block_num * self.block_size + offset, s/ T2 ?7 [! S, A! b, ]
  8.         for i in range(len(buf)):6 s# v. a: U! |; E3 D' I, d7 h
  9.             buf[i] = self.data[addr + i]
    5 e6 J$ y. I1 M

  10. ; Y9 {! u: K% [! d% c" ]3 [
  11.     def writeblocks(self, block_num, buf, offset=None):
    2 Z( W& w+ j" y4 t& ~
  12.         if offset is None:
    / f2 _4 n" `. Y/ a& X( ^
  13.             # do erase, then write7 B/ g; B0 i, R5 I- Q0 b
  14.             for i in range(len(buf) // self.block_size):
    2 W, w. W1 n$ D2 N( V
  15.                 self.ioctl(6, block_num + i)+ W8 o; V3 D, O
  16.             offset = 0
    ) W" d8 v. \0 K  U# q
  17.         addr = block_num * self.block_size + offset
    ! m4 _' B5 Q+ ]
  18.         for i in range(len(buf)):5 B/ N1 L4 Z5 r7 R! d% T
  19.             self.data[addr + i] = buf[i]7 O! {4 A# y9 l& I3 F

  20. + y; `7 F9 \" _; S/ ?
  21.     def ioctl(self, op, arg):
    ) _# A9 c/ Q1 b0 K
  22.         if op == 4: # block count
    3 d' t; Q# `4 S/ c) Q9 D0 ~
  23.             return len(self.data) // self.block_size
    9 i3 r- J/ w" p0 J$ _' A3 E
  24.         if op == 5: # block size9 \( H) \% n) Z5 R7 |7 r8 e
  25.             return self.block_size
    8 S  D7 p  K' L9 y5 m: c( H
  26.         if op == 6: # block erase
    5 H( H, l5 E  v7 o
  27.             return 0
复制代码
9 l; Q0 j4 K1 M' l! l6 g" V1 o

1 d2 [$ e+ @6 @% C6 k2 M% U, F0 O# ^8 x& Q/ ?( w! y

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

  1. import os4 L, ^9 t8 E/ E/ i
  2. * ^$ O  ~8 J2 r
  3. bdev = RAMBlockDev(512, 50)4 g- E6 Z6 O; J7 y# r
  4. os.VfsLfs2.mkfs(bdev)
    . e5 X' U) x7 S7 ^
  5. os.mount(bdev, '/ramdisk')
复制代码
# q2 J# H2 u1 \# K! q

0 z* ^2 r8 C: T$ @" |% v3 w. o/ K& f! D4 E# g6 `* q1 ]

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    ) y( J% p$ q3 T; y
  2.     f.write('Hello world')
    5 }& a% ?( E' q! @
  3. print(open('/ramdisk/hello.txt').read())
复制代码
$ ^$ M  Y  x5 H- p: X
& @' p' g* M2 _
7 X3 Y. V7 @/ m
3 ?+ ?- [( {# Q* P+ x* J
6 R( u; v8 B- e1 E3 u! F8 l2 Y, Q/ n2 ^
文件系统

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

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


$ ]% n' |6 f4 v. {2 |FAT

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

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

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

  1. # ESP8266 and ESP320 _5 J6 }+ \) }( h: N
  2. import os5 q. a. c- {: e% g% x! V& s9 |  ?9 M3 Z
  3. os.umount('/')
    8 r+ y9 e% v  W  S
  4. os.VfsFat.mkfs(bdev)& h4 F+ c. V4 ~: H! X' Z  {
  5. os.mount(bdev, '/')
    ) D- R1 E5 Y5 r1 G9 a  n$ s0 y
  6. 2 y$ t4 I0 s7 |8 y+ Z8 l8 S3 M1 p
  7. # STM32
    4 h& p. S; Y% G  i
  8. import os, pyb$ |- P3 l5 Y; O3 U1 U2 w- v
  9. os.umount('/flash'). C) ]) i1 H: C1 g
  10. os.VfsFat.mkfs(pyb.Flash(start=0))8 R7 ?* k0 G0 J
  11. os.mount(pyb.Flash(start=0), '/flash')
    - P+ S- l4 W# _9 @& z
  12. os.chdir('/flash')
复制代码
+ ~$ I/ o0 a5 A6 _* o: Q
- H' S8 E" t( P. A# R
- ?8 ~* l' E' @8 N

0 \$ Z% \$ ]& z) V0 `' rLittlefs

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

笔记

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


' i& t& U+ R% r

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

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

  1. # ESP8266 and ESP32
    ; L& b- y( u& ?6 r% v
  2. import os
    ' \; r& k" l2 f7 b1 e7 o& \& ^
  3. os.umount('/')0 Z0 D2 ?" }; t. @) S3 g
  4. os.VfsLfs2.mkfs(bdev), A5 E+ Y$ C: b* O0 q' N
  5. os.mount(bdev, '/')
    # [" `2 A! N! d" b$ T

  6. $ u4 w) p2 m: s
  7. # STM32
    6 S4 m* H' Q8 b+ ~+ f5 D- r
  8. import os, pyb
    1 f  C# [/ c5 G$ N6 S
  9. os.umount('/flash')
    & H4 e( `0 V% i2 j' f0 a
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))2 f, X4 }5 H4 K9 |
  11. os.mount(pyb.Flash(start=0), '/flash')# H7 {$ h" B3 v. a
  12. os.chdir('/flash')
复制代码
. S1 N; H. Z+ a- L# q
. j# z/ V% Z( }0 N
* s2 _% b: W) \- x7 z4 z1 Y

2 A. i+ L8 C3 Z: p/ b% P, L- E混合 (STM32)

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

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

  1. import os, pyb
    / |' q- q, V$ \& j8 q% {2 T' J
  2. os.umount('/flash')+ {4 W6 ~% {2 U  G/ E; D. R
  3. p1 = pyb.Flash(start=0, len=256*1024)
    5 Q/ X; K  X) T( m/ @, f: P
  4. p2 = pyb.Flash(start=256*1024)
    - Z# Y3 I6 U& B+ P0 d. |8 n0 T' B
  5. os.VfsFat.mkfs(p1)
    . G* N) F! A4 c( q9 N
  6. os.VfsLfs2.mkfs(p2)  `; F- X& |! \" q7 m. Q
  7. os.mount(p1, '/flash')
    # {! Q/ D8 i7 Z. f1 z+ ]: }" x
  8. os.mount(p2, '/data')
    1 M' f* n8 ^# a1 m1 X
  9. os.chdir('/flash')
复制代码

! U! y$ \) ^# Y7 D$ \$ l: T% L1 T1 d; D
7 x& @' K4 g% o! {

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

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

  1. import os, pyb5 h8 h& E: V% r: \+ W
  2. p2 = pyb.Flash(start=256*1024)
    ' _$ u9 Z4 U4 V9 Y; ], v) Z4 H
  3. os.mount(p2, '/data')
复制代码

5 ]1 t3 Z' F' a5 i& I7 X+ }. x, z8 D2 T6 B: _5 ?* D- j
( D+ i$ h* k7 r' R, b$ a& T1 t, `

来 boot.py挂载数据分区。

) }6 j" _- }% v3 t1 V8 R* B5 w9 ]
混合动力(ESP32)

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

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

  1. import esp32, os, l; K# N- }; h1 ?5 Z
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo'), w" D+ X! o. T& n- ~" k
  3. os.mount(p, '/foo')
复制代码

7 X. U# f; c2 C  [) n; N5 I" I( P+ _& E4 q
8 N8 Y, f$ y7 ]. Y

1 l; Q, y) Q6 _: A' `
3 }! \5 q) N' S! L, g
- n, Q, _) H' H+ G, _

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-6-27 23:01 , Processed in 0.124800 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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