micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 142048|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3156

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


1 s# L. e! C, f# p

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

! \+ j$ P( I$ K' o, e' E7 p
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

* p8 P  {$ n& v) A! ^4 R+ d
ESP8266

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


* L0 b4 F( A1 _5 n. w4 |ESP32

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


; a2 i! b% j  m# @
0 S! P7 L5 b) ~  I( @自定义块设备

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

  1. class RAMBlockDev:
    9 T1 K0 K) w# l: Z9 i
  2.     def __init__(self, block_size, num_blocks):
    0 ^" ?1 Q2 y) B; G; Y
  3.         self.block_size = block_size
    9 G; h! Q1 c- @6 B' G: h
  4.         self.data = bytearray(block_size * num_blocks)
    $ l6 g9 Q9 j6 b; ]. n
  5. 0 w# b$ |% X  p3 I& o; e) B
  6.     def readblocks(self, block_num, buf):
    8 |! q: C3 E6 g/ q( N
  7.         for i in range(len(buf)):% i) ~7 x" g( e
  8.             buf[i] = self.data[block_num * self.block_size + i]
    ) a% Q. y/ V5 F8 Z, r7 t

  9. ; f7 L4 E( ^% @1 H
  10.     def writeblocks(self, block_num, buf):2 Z3 t/ ^6 N( n
  11.         for i in range(len(buf)):
    7 d9 ~. e3 k& B
  12.             self.data[block_num * self.block_size + i] = buf[i]/ W; |) c* s1 w" f

  13. 3 }8 y6 Z' S$ p$ w& P9 ^; u; k
  14.     def ioctl(self, op, arg):* |9 X# S# F# `8 Q6 V
  15.         if op == 4: # get number of blocks' y: f3 ^) V" J( g) A; e# d2 a
  16.             return len(self.data) // self.block_size
    - y' y  s0 }& {) S
  17.         if op == 5: # get block size' {3 a4 {7 f2 ^5 O
  18.             return self.block_size
复制代码
. F- d2 R1 C6 q$ y

; |8 Z. {1 O+ n4 \, N+ S8 Y. R$ _/ ?

它可以按如下方式使用:

  1. import os5 e% s. e* L2 K( _7 h6 ^

  2. 2 _  B/ g; I' j8 U1 c( t3 c" V9 m$ K( r
  3. bdev = RAMBlockDev(512, 50)
    # n6 K' }) |: F3 V+ B/ g0 i
  4. os.VfsFat.mkfs(bdev)/ I# H- O8 c/ S
  5. os.mount(bdev, '/ramdisk')
复制代码
/ x$ S/ k2 h# }+ {

* s; p7 ]3 W* t. u5 @8 G- j9 r7 H
. V" z, X& Y4 _

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

  1. class RAMBlockDev:
    4 I9 `! \8 g& U& Q, c* e  A
  2.     def __init__(self, block_size, num_blocks):
    0 {$ i. {, |2 \
  3.         self.block_size = block_size# u+ U( A& w( @( {# a, G
  4.         self.data = bytearray(block_size * num_blocks)
    , @4 @* b  D& m6 M

  5. # d$ g" A* W  z# y
  6.     def readblocks(self, block_num, buf, offset=0):0 ?) ^$ P9 e: W8 z" W) }, W. U% s  d+ w
  7.         addr = block_num * self.block_size + offset  ]1 f1 b. p1 J  O' z" K
  8.         for i in range(len(buf)):
    ( N% r/ K8 h9 N/ t. K1 b1 E- H6 A
  9.             buf[i] = self.data[addr + i]$ w$ i/ T7 G1 U7 Z' t3 S
  10. 9 s; {0 I% k% n1 d' Y6 G( t+ X
  11.     def writeblocks(self, block_num, buf, offset=None):+ e* Z4 H# Q! D! l) E7 [# O
  12.         if offset is None:7 e. [. s* Y$ n7 E; v1 v% a# x- w# W5 C
  13.             # do erase, then write
    , x4 S  n7 v) c; w) Z9 K
  14.             for i in range(len(buf) // self.block_size):
    + f1 ], [" l( N- U! m. S6 l2 c
  15.                 self.ioctl(6, block_num + i)
    # `, s2 S6 m/ V, Q
  16.             offset = 00 `( C( ]) f2 P  Q
  17.         addr = block_num * self.block_size + offset/ @, X+ ^! S$ N+ n5 c5 _; m! ?
  18.         for i in range(len(buf)):2 g0 g8 z2 E  S/ ^- D
  19.             self.data[addr + i] = buf[i]! T: J5 ~; r$ g2 d  |" }

  20. ' s( x8 T; S/ W+ {! I+ S
  21.     def ioctl(self, op, arg):
    ; q: e$ |# D6 M- ?7 f: g7 K+ O5 }9 ~
  22.         if op == 4: # block count/ C: N" x8 I7 o& Q/ A/ c
  23.             return len(self.data) // self.block_size
    + i* q/ G5 i* r! A+ P
  24.         if op == 5: # block size$ H+ u# E) b' ?0 h
  25.             return self.block_size
    ! P& c$ o- A- q& L0 s. ?
  26.         if op == 6: # block erase
    1 J* ?' }7 I7 J+ G9 R
  27.             return 0
复制代码
9 H$ j1 s( L0 i3 Z! N

# P1 P/ b1 t: T. t
* h& F) \( ]0 e. f! a, y1 {0 W$ _

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

  1. import os
    ) S3 O- \/ I: U% m( k
  2. 5 x2 S: Q- x4 l  t2 g- o% l4 s
  3. bdev = RAMBlockDev(512, 50)
    - }  W: ~$ i1 [3 A. K# A
  4. os.VfsLfs2.mkfs(bdev)* B( u) g, b( h6 Q# n
  5. os.mount(bdev, '/ramdisk')
复制代码

  h: w$ K! ]8 c! a5 f
! a7 w+ \+ R2 g1 e/ `6 Y: {3 u1 r$ Z, g% G% f

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

  1. with open('/ramdisk/hello.txt', 'w') as f:
    * j9 U3 L; {/ X3 N/ T% }
  2.     f.write('Hello world')
    0 m5 n, W' m% I- A4 w6 r0 T, [
  3. print(open('/ramdisk/hello.txt').read())
复制代码
. \3 s/ \% {) o* `" n7 @0 k) ^

: N* V# k* D+ \* B7 s0 u; |2 F0 |8 r8 T

5 V- Q% ?7 L3 z+ _' P% Z2 T
$ [. {3 J7 a/ {* P) d! P8 c
文件系统

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

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

1 @6 ]* t5 j. L
FAT

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

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

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

  1. # ESP8266 and ESP32! Z$ C% M# C% x2 ^& b! j
  2. import os
    6 g# d3 @- w* |
  3. os.umount('/')/ m9 n# V2 N+ @, C  }  ]
  4. os.VfsFat.mkfs(bdev)
    0 L% \0 d  m& ~1 X. T: I0 `
  5. os.mount(bdev, '/')5 n# z" E: r7 R! D8 k

  6. ( a) T' `" Q% r2 Q
  7. # STM32: ~( ^; c+ P7 B3 i$ l1 c' T
  8. import os, pyb
    1 [; a7 _2 J$ Y% k  [; N2 r
  9. os.umount('/flash')! W) W! M; k/ U6 x
  10. os.VfsFat.mkfs(pyb.Flash(start=0))0 p" \' Y) s4 a7 M( y* b
  11. os.mount(pyb.Flash(start=0), '/flash'): c$ I7 M7 {3 i
  12. os.chdir('/flash')
复制代码
: `/ w: p) K/ }2 D8 T8 f3 e3 O6 _9 Y

; z* X! z& G: `1 H
* O2 y0 j* y. Q; E; g( D. ~& v& Q) n$ A3 G
Littlefs

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

笔记

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

% R% t. H5 h3 x

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

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

  1. # ESP8266 and ESP32: m$ j" V8 D1 n) S
  2. import os
    6 y% L5 T/ ]7 K
  3. os.umount('/')1 T- I! b" ^# s) v4 M$ y! {
  4. os.VfsLfs2.mkfs(bdev)/ r! K1 x# ]9 J9 w8 s
  5. os.mount(bdev, '/')$ J8 R2 P* j" o7 M7 J! |$ U2 k

  6.   M( i$ w$ ?& m- y1 {0 H% z
  7. # STM32
    6 o, [6 A! @/ Q* W# V
  8. import os, pyb
    0 R( v8 B3 E) Q& \6 n
  9. os.umount('/flash')# y7 H: ^! ~6 W  ]* F' K  S5 U5 }
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))  E4 d+ K  H  s/ k7 N  O% u2 S
  11. os.mount(pyb.Flash(start=0), '/flash')2 y* B* ^6 c+ {. }$ p. P8 @
  12. os.chdir('/flash')
复制代码
+ G* `0 M- `  Q# F( }
6 n+ Y- G3 o, X# n
4 p4 j  @$ s' m' ~3 C

/ h9 i1 Q. O7 i( N混合 (STM32)

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

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

  1. import os, pyb
    / g& s' s1 d5 e. v$ b( ^- N
  2. os.umount('/flash')9 q! S5 W, U* _! {8 u; ?' q
  3. p1 = pyb.Flash(start=0, len=256*1024)& b. [2 o* e2 l0 b7 ?" l
  4. p2 = pyb.Flash(start=256*1024)
    9 G. r! I7 p0 B- _8 x
  5. os.VfsFat.mkfs(p1)  }( U: N' y' j+ K3 k5 U6 k
  6. os.VfsLfs2.mkfs(p2)$ q* ~5 q: B2 b
  7. os.mount(p1, '/flash')% A# v0 R! O/ Y* \" h' @  W
  8. os.mount(p2, '/data')
    % G9 ^9 P2 B6 O
  9. os.chdir('/flash')
复制代码

- K7 x0 t* U4 v9 v
! e7 E# X# Q8 H6 P+ S
0 {' ?7 d: f, L. w% x* s

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

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

  1. import os, pyb
    4 ^! {- w! n$ Z/ ]( K( J2 K% z
  2. p2 = pyb.Flash(start=256*1024)9 a0 g' x1 o" r  T7 g9 f
  3. os.mount(p2, '/data')
复制代码

% ]) ]$ ~9 ^+ r  i9 z& u% J/ C  s& l# y8 p2 I' h

  r* b; o- y6 l: b

来 boot.py挂载数据分区。


+ D6 p& L9 B2 `混合动力(ESP32)

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

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

  1. import esp32, os& Z3 j( V9 B0 I- ^# w; G8 D% d6 s
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')$ x: R. o+ i. r, j. q$ Z# s. z4 J
  3. os.mount(p, '/foo')
复制代码

3 \" [2 B1 \- d; ^$ K6 X6 N  L  \
/ q5 r. P3 d* y) Y6 z1 t
- ]4 O5 g: v5 K) A

8 f: b7 E* w7 Q! b
5 l$ H: i1 Q( P& _. u& H- D4 R# y( i

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-5-17 13:27 , Processed in 0.140401 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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