micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 188505|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3276

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


( p' k% b6 \4 a0 a# k5 j  U0 b& 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 上,主文件系统挂载在 /。

) G* |6 i) @# r  `% I. H
块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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


5 X- K& F$ B: |, g; VESP8266

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

7 C; t0 b% Q* }! T
ESP32

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

3 `* E1 f& \  h+ w# n5 _

" W1 i( K. l( J- H自定义块设备

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

  1. class RAMBlockDev:' F# ^3 E  S# w8 `
  2.     def __init__(self, block_size, num_blocks):
    2 X) D1 i( b3 n# X4 m8 s  F
  3.         self.block_size = block_size* y8 A  E: Y% ]0 {5 B* V) e7 U
  4.         self.data = bytearray(block_size * num_blocks)7 n( e8 j" g+ Z/ W
  5. $ H, ^: q7 {6 c
  6.     def readblocks(self, block_num, buf):. C7 q: Z' r0 f% k
  7.         for i in range(len(buf)):9 Z* ^' }$ S% w( @
  8.             buf[i] = self.data[block_num * self.block_size + i]2 n0 K) e5 Y+ \* d# L

  9. + C5 a( ^: e0 r# x! g
  10.     def writeblocks(self, block_num, buf):% H; @2 C0 E6 w+ G3 A/ a2 e% x
  11.         for i in range(len(buf)):
    ) n4 y1 @  {* ~, m1 x& H
  12.             self.data[block_num * self.block_size + i] = buf[i]/ p3 J, O: {8 z% f7 r/ v" \5 }7 [

  13. 8 }$ w: q9 O. x3 t5 B1 w
  14.     def ioctl(self, op, arg):
    4 D2 l, g+ U: e) r: T5 |- W1 ?* Z
  15.         if op == 4: # get number of blocks
      A! l( t" H1 C' s- u) A
  16.             return len(self.data) // self.block_size" |% [3 T  q$ ^5 w: P
  17.         if op == 5: # get block size
    ; U/ P& N- I0 i& L5 |3 g) x! ^
  18.             return self.block_size
复制代码
; u8 H6 i! P0 j' X* R

: ^# |$ H0 d, d- ]+ H1 U$ y3 ~9 {/ B- w8 G

它可以按如下方式使用:

  1. import os
    1 u  C- ]( [+ }0 N* l
  2. * P/ L6 {  P) T, I; D
  3. bdev = RAMBlockDev(512, 50)
    1 Q4 w" v2 V, V$ z+ r9 j
  4. os.VfsFat.mkfs(bdev)
    " Z$ w  A) U* _& S
  5. os.mount(bdev, '/ramdisk')
复制代码
6 P: M5 \4 ]( c/ @5 X
- f9 v7 o  ~$ L+ a3 e

8 ^1 U6 N# S& t+ c+ O0 p; U

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

  1. class RAMBlockDev:
    ' H6 n) [- e: }9 @/ d
  2.     def __init__(self, block_size, num_blocks):
    # H+ S* w( K! W
  3.         self.block_size = block_size
    ' f4 i* u+ Z" g# l: @
  4.         self.data = bytearray(block_size * num_blocks)$ x0 U/ E8 F- a+ P: V

  5. 3 B+ e' p* c! c
  6.     def readblocks(self, block_num, buf, offset=0):
    3 `3 W" \/ u( F* L, P- U
  7.         addr = block_num * self.block_size + offset
    * ]  p" k6 |. a% B- s
  8.         for i in range(len(buf)):$ M4 o7 p5 x* B+ p' w$ R, d
  9.             buf[i] = self.data[addr + i], ?( {/ ?' p( ^7 P6 B
  10. % m" @! X: c. w( w7 \
  11.     def writeblocks(self, block_num, buf, offset=None):/ @3 H3 G" t8 V& l$ S* G
  12.         if offset is None:6 G, c# R+ s* P. l3 C0 x$ v7 B
  13.             # do erase, then write' T2 I, q+ X4 e# l. U
  14.             for i in range(len(buf) // self.block_size):
    ' m& x2 w) G4 h8 ~) L2 Y
  15.                 self.ioctl(6, block_num + i)- y% P5 H2 S5 D
  16.             offset = 0
    7 H" F' I. ?" w, n) q
  17.         addr = block_num * self.block_size + offset/ N$ l& n% }' s6 |7 W9 E8 _8 O) E
  18.         for i in range(len(buf)):
    7 s- U/ y7 X( B: q* f+ Z% Q6 n, l
  19.             self.data[addr + i] = buf[i]% e  Q2 Z" r: j; s$ r
  20. # {. M5 J2 m9 `' ~- U, ^
  21.     def ioctl(self, op, arg):3 A% x/ V* D8 r, e6 [
  22.         if op == 4: # block count
    % J: w& x! r- e! \2 ?+ ?9 d/ ^
  23.             return len(self.data) // self.block_size/ v3 N) S  G" o2 W/ D/ v" ?
  24.         if op == 5: # block size
    : _/ N9 H7 ~6 o$ `- J; D
  25.             return self.block_size
    " x4 X9 J% x" {9 H7 m4 ]" J, Q5 a
  26.         if op == 6: # block erase
    & f4 e: o% {$ A  j6 `
  27.             return 0
复制代码

; M* M5 u$ U3 i; N7 U3 X" I2 v. d3 T9 p6 }9 w' W! S

% M4 ~) S. j- h! L$ H

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

  1. import os. H' F5 }+ X8 e6 I0 P2 _

  2. ' A$ A1 U7 @7 O7 D- u; ]
  3. bdev = RAMBlockDev(512, 50)7 u3 y' @7 {" y1 q# G
  4. os.VfsLfs2.mkfs(bdev)- s$ m  f* u4 S/ e" e2 R
  5. os.mount(bdev, '/ramdisk')
复制代码
! y! N  |3 {- {% `9 y' v/ G( u

3 Z8 v- k6 A5 r6 ?5 m& U" }! J* P# l9 t; L' p  w+ X' |' U  z4 R

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

  1. with open('/ramdisk/hello.txt', 'w') as f:" m% T. a/ O5 C4 }( T9 C/ L; I$ H
  2.     f.write('Hello world')
    : S- o2 u0 b% @5 v/ S. m/ g1 V
  3. print(open('/ramdisk/hello.txt').read())
复制代码

7 Y1 y0 L+ ^9 A8 ~
8 k4 V3 {8 q$ c1 ]- m, ]0 A7 F2 g, J0 `+ E4 v0 f
% m  ~2 G2 c8 b/ a- O/ X3 F1 ?

. F8 r2 c! m* u3 ]  y/ C! J+ }8 _, {文件系统

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

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


/ ]$ Z' B& M4 n3 `' B5 KFAT

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

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

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

  1. # ESP8266 and ESP32; P, `  P" Q8 m9 L
  2. import os
    ; V. A' C/ l$ s$ V
  3. os.umount('/')
    " K' c: T- z/ E/ ]! O- I
  4. os.VfsFat.mkfs(bdev)! q! x( b: K' g5 r
  5. os.mount(bdev, '/')8 B* d1 }3 \9 a- A

  6. 6 G! b8 `* L& M- G  r+ R3 R
  7. # STM32
    + P! p: I$ Q. ^: {# D
  8. import os, pyb- R! d+ C4 F; p8 _* o2 \
  9. os.umount('/flash')
    " t7 U' p6 W0 C
  10. os.VfsFat.mkfs(pyb.Flash(start=0))# D8 n" \. f% U( \# }# ?- F
  11. os.mount(pyb.Flash(start=0), '/flash')  P1 U* \; n4 p% s/ `
  12. os.chdir('/flash')
复制代码

1 k9 p! W' v1 t0 E! p* D
! E/ t( M9 c" R2 P; ]
/ d/ ?1 S0 L6 j, q7 J  [) N, Z: H4 a, O& C& w
Littlefs

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

笔记

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


, `9 \1 q) ]& C" D8 Y+ b

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

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

  1. # ESP8266 and ESP328 P- p3 N  D1 Q: ?  p; S& u
  2. import os- P0 L. o. K- j- S6 K) B
  3. os.umount('/'), |/ v  \, Y7 N3 u4 m
  4. os.VfsLfs2.mkfs(bdev)8 ]% j% U$ ~, L( X* o
  5. os.mount(bdev, '/')- q: }8 F2 E4 H& N  h% [6 \$ V4 O+ c
  6. ( a3 Z' N; K4 O3 ]; `; P& O, f  I
  7. # STM32
    5 x' ?% q/ L. m9 C. H
  8. import os, pyb
    $ F' {  o& S0 l5 a( a% r
  9. os.umount('/flash')
    ; N/ ?+ s$ _! j
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))  P8 Z  x2 U( n
  11. os.mount(pyb.Flash(start=0), '/flash')
    ( s, ?6 K- q: Q
  12. os.chdir('/flash')
复制代码

$ z# ]' G, m; c" P7 j
9 o$ s# x* X  U# \7 X6 V* G
& ^' _. t; j' f+ W; B# [% w: B
1 N; I0 J" N$ V' m% s8 {2 V混合 (STM32)

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

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

  1. import os, pyb
      ?2 x. I: }6 L3 c! W* d9 I
  2. os.umount('/flash')- ]' C' [/ e: W
  3. p1 = pyb.Flash(start=0, len=256*1024)
    6 F/ @4 L6 h+ q
  4. p2 = pyb.Flash(start=256*1024)1 I3 C# }2 H4 b9 d9 |+ U
  5. os.VfsFat.mkfs(p1), J% M2 l$ I1 @
  6. os.VfsLfs2.mkfs(p2)
    ( s2 q% A$ y1 K" c3 M5 X
  7. os.mount(p1, '/flash')
    - g$ x* A& Q: n9 I
  8. os.mount(p2, '/data')
    ( r# A' v" Q' Y2 X: I/ ~$ K
  9. os.chdir('/flash')
复制代码

' [1 t4 Z. l$ W! o
+ h0 J: q$ I0 c: Q, S: `; O
/ _% y; n" R. G6 D. K) k1 V7 S

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

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

  1. import os, pyb
    7 v! v  Z# P# v" _- _
  2. p2 = pyb.Flash(start=256*1024)8 }" d0 {; Z2 _! ^8 F0 P
  3. os.mount(p2, '/data')
复制代码

; q% N/ y" W/ I1 H. l/ z
; \* P7 a2 r5 P' N
! c* Q0 `" F% r& G; A& s8 n2 R

来 boot.py挂载数据分区。


  ?( e; R5 }$ J混合动力(ESP32)

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

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

  1. import esp32, os+ V; n: a% g! H' S
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')5 C1 N3 o$ |/ v! B6 `
  3. os.mount(p, '/foo')
复制代码
% i; O* j; a4 b1 w, B) \2 v
9 c- p; X6 G3 F1 L

5 V+ u9 v$ W% Y# ]9 \3 E  M
1 e. G3 J7 d( W- V7 w' v

/ t1 o0 x9 I) z0 |0 @/ z1 V4 f0 g9 w8 Z, L# ~

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-7-19 07:50 , Processed in 0.187200 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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