micropython编程爱好网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 201422|回复: 2

使用文件系统

[复制链接]

24

主题

24

帖子

3334

积分

管理员

Rank: 9Rank: 9Rank: 9

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

内容


0 d# Y- q5 `* e! _3 w' c

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


4 K6 T( t7 q! x' W9 N块设备

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

内置块设备

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

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

STM32 / Pyboard

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

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

  Q2 [9 s$ ~) x  a
ESP8266

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

9 |/ x# r# J% s/ U
ESP32

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

" y) I* s& u* h( ]1 Z! A4 ]/ _3 y

1 S, D) ]* u3 b& p自定义块设备

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

  1. class RAMBlockDev:
    ! _/ a" E$ d6 ^& g+ E- A
  2.     def __init__(self, block_size, num_blocks):  ~$ w+ g. ~) n4 S( l' P( d6 h
  3.         self.block_size = block_size; z4 `- j: W6 q5 q3 H
  4.         self.data = bytearray(block_size * num_blocks); e  i# @! X4 p; H, q1 p' `3 X8 R

  5.   e& S8 v- Q' M  s
  6.     def readblocks(self, block_num, buf):
    2 i; V2 @: P' v' ?$ z" u4 {% ~; M
  7.         for i in range(len(buf)):* [/ G( a* @5 j, V' B
  8.             buf[i] = self.data[block_num * self.block_size + i]
    4 r9 D9 e$ T+ y  }5 @* c

  9. ; r+ k9 I9 F3 b, J+ j3 B5 J
  10.     def writeblocks(self, block_num, buf):
    0 w: \3 Z9 |. d! [
  11.         for i in range(len(buf)):
    3 b& r. y$ w4 C% A5 a. J
  12.             self.data[block_num * self.block_size + i] = buf[i]0 J5 _$ b) D5 U; S) w+ W
  13. $ |* J9 h' C* o7 R( z8 T4 c
  14.     def ioctl(self, op, arg):
    ) |9 ^0 ~5 _* W0 Z, [: i
  15.         if op == 4: # get number of blocks
    2 @6 }! ?, J5 y* q! J6 W
  16.             return len(self.data) // self.block_size) D3 H6 h& _+ Z, T& Z( I5 n* k& p
  17.         if op == 5: # get block size
    0 A3 M! @' [. M1 q2 R. J# }
  18.             return self.block_size
复制代码
6 P# I% L* {5 f  I2 F

: R9 R. n% _9 I+ @3 H
& m' }& u# i# `" Y; {1 v

它可以按如下方式使用:

  1. import os" L$ W5 F& j5 e: w2 J! k
  2. ( r$ k# Z6 ~5 A1 Q7 F- D
  3. bdev = RAMBlockDev(512, 50)
    / @+ v7 Q7 J8 u( ^) P' @
  4. os.VfsFat.mkfs(bdev). a8 M& t! F% E$ \; c$ R
  5. os.mount(bdev, '/ramdisk')
复制代码
9 ^& g' l7 ]3 ~

. I' h* y' c# @) e3 T
" v0 E* ~: K3 ^+ d* T/ h/ t# I

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

  1. class RAMBlockDev:
      {2 W* G: c- S
  2.     def __init__(self, block_size, num_blocks):9 a0 D1 D8 l9 _
  3.         self.block_size = block_size
    5 n7 u5 G/ Y. W( ~
  4.         self.data = bytearray(block_size * num_blocks)$ N( ]2 a& X3 ?) ~6 i$ G* _
  5. , w; G8 k% f9 `) l, I
  6.     def readblocks(self, block_num, buf, offset=0):" k2 ~6 s1 a& T" g; q
  7.         addr = block_num * self.block_size + offset
    & N& U3 K" ^; B& ^" i0 }
  8.         for i in range(len(buf)):3 u3 \- E& ~# N/ E
  9.             buf[i] = self.data[addr + i]2 l7 ^5 w6 R/ Q! n1 Y
  10. & m4 t# J; }& k5 [% \, k7 @
  11.     def writeblocks(self, block_num, buf, offset=None):
    8 }% a9 v$ ?$ K0 y, q8 G
  12.         if offset is None:
    % }- r( I$ d+ |  c' g; b. l0 R( T
  13.             # do erase, then write; O% v0 q- s( j! T
  14.             for i in range(len(buf) // self.block_size):
    % c# n' E2 I% B% }/ B! g
  15.                 self.ioctl(6, block_num + i)1 V/ {' I2 Z4 t# o! Q$ R6 H' y) s
  16.             offset = 0
    8 U5 f' Q! @& R3 l% ?6 i
  17.         addr = block_num * self.block_size + offset
    ) T5 m. z  L' B0 l; c/ x* b/ N
  18.         for i in range(len(buf)):( {+ x% h# x3 A
  19.             self.data[addr + i] = buf[i]  a; G' n: G8 g* s; Q

  20. 8 L1 w7 ?3 j' T
  21.     def ioctl(self, op, arg):
    : L7 ^# Z' o( [) r) Q
  22.         if op == 4: # block count
    ! q, G) G1 a$ r/ B- W6 b
  23.             return len(self.data) // self.block_size( W- e  _/ z' |8 z, r
  24.         if op == 5: # block size. \& s: V; m5 V2 W
  25.             return self.block_size
    5 M7 A& {2 W, w) D$ Z5 o
  26.         if op == 6: # block erase
    : ?' c+ n8 a; ^8 j: y
  27.             return 0
复制代码
1 h/ W) j! I8 Y$ {% P4 g( O
4 U0 {, _) U5 d4 ^- y8 D& |" v

2 y% h6 {+ I7 d! [; F6 Q

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

  1. import os
    : H: W/ Q0 z3 i1 I" K" K

  2. - t6 ^% }" O3 d; c$ y* H# b. V
  3. bdev = RAMBlockDev(512, 50)
    6 j/ p5 K5 `% L0 w6 q5 \, T
  4. os.VfsLfs2.mkfs(bdev)
    6 t$ R& a$ U% ^% D/ w& e+ Z
  5. os.mount(bdev, '/ramdisk')
复制代码
. M' S+ C5 a  k7 H4 ]
. I# Q7 H" I9 d' N! S  y/ \

6 q. |- T, j! O# P8 a

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

  1. with open('/ramdisk/hello.txt', 'w') as f:% H/ G( _  g$ I, O! X0 w% d$ X
  2.     f.write('Hello world')
    & d; ~( g9 Z3 a) C- C6 b7 P
  3. print(open('/ramdisk/hello.txt').read())
复制代码

0 Q7 W! e6 [/ S8 w" B. P7 k' H
. @+ l& {. C9 \  M8 {3 H; C1 P* |% {3 t+ l
' v( |3 m& R: g# g
9 U0 v- w8 I2 [4 I
文件系统

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

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

. j' u" n( s* i
FAT

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

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

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

  1. # ESP8266 and ESP323 }2 ?$ p) G2 {7 y9 Q. p
  2. import os
    5 I- F1 g9 z& l. l
  3. os.umount('/')
    - O, O3 ]5 C5 {/ B/ X9 S9 w* s5 B* A
  4. os.VfsFat.mkfs(bdev)
    ; \2 C# w3 Z3 i* t0 |( b1 L
  5. os.mount(bdev, '/')4 [$ b0 |! U7 |7 B5 r' W

  6. 6 v/ Q* R' H# M! ~: d  ]
  7. # STM32
    : B, x: v8 F* U8 {$ v" X* \, k3 H
  8. import os, pyb" x3 P; C6 n$ V6 Y  n
  9. os.umount('/flash')
    3 [6 l" D8 n+ R# W5 Y0 g
  10. os.VfsFat.mkfs(pyb.Flash(start=0)), N5 n9 F% Q# k" ]0 u( q7 e
  11. os.mount(pyb.Flash(start=0), '/flash')
    3 `" ]0 @0 F; q1 n1 B. ?5 w& h
  12. os.chdir('/flash')
复制代码

% g6 c3 e# F1 ^) Q9 ^: U- D& ?
/ l7 T, j( H0 f' U
& _! t0 [* H  j" h
+ n$ ]8 u" g* z% r( B. sLittlefs

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

笔记

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


- u9 I0 F5 t" `; P

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

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

  1. # ESP8266 and ESP327 u' N) L& h% |: N
  2. import os& w1 {5 f5 I2 G' T7 ]$ ~! P
  3. os.umount('/')
    + w5 M' ~6 @# ^: I' Y
  4. os.VfsLfs2.mkfs(bdev); r% N$ h- ~$ a# C- r
  5. os.mount(bdev, '/'). u5 |3 ]% {8 s9 L; `

  6. 5 X) Z! T- y3 q% a! K' @0 z7 g+ Q9 v
  7. # STM32
    , u8 X' p  X0 d& ^* y
  8. import os, pyb
    % `7 n( N" T5 c& B# g4 N
  9. os.umount('/flash')
    3 x% \* z2 g" E# A
  10. os.VfsLfs2.mkfs(pyb.Flash(start=0))/ ^+ D8 Z! w3 H+ ~/ q" H* l/ t
  11. os.mount(pyb.Flash(start=0), '/flash')
    0 \( K  a# l% w$ a
  12. os.chdir('/flash')
复制代码

; q$ [3 |+ v) e
6 d0 |! O. x' m/ u! n/ F0 Y4 V
$ H6 Y, C8 Z* N2 m
; J) o: y! y7 ^. c混合 (STM32)

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

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

  1. import os, pyb
    2 ]1 D2 c; A$ u8 {7 \6 k+ l5 ?
  2. os.umount('/flash')' J$ }) Z8 A" z8 e4 l+ T
  3. p1 = pyb.Flash(start=0, len=256*1024)" b. }$ I4 \# y7 x# d! G) _1 z
  4. p2 = pyb.Flash(start=256*1024)
    1 ~% l" o. ]7 a- a+ R8 K
  5. os.VfsFat.mkfs(p1)
    / E  l1 y8 R8 c2 |2 h0 z
  6. os.VfsLfs2.mkfs(p2); f* }: l6 z* j5 N+ n" \5 G
  7. os.mount(p1, '/flash')1 {9 h9 s% }! ]) _/ c! b
  8. os.mount(p2, '/data')
    1 Q" q) l# u7 {' ~$ ]  k. P) x
  9. os.chdir('/flash')
复制代码
2 W" y$ ]* M( U# P9 h
. ~" J1 T, ~9 T+ w" w

* a" c8 D8 @& j' g, K  \, U

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

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

  1. import os, pyb! V$ C; a! j6 V) t0 O- l
  2. p2 = pyb.Flash(start=256*1024)- ]6 u, m0 v) @8 @$ F  j& W- F. y- P
  3. os.mount(p2, '/data')
复制代码
+ D2 i' g- v/ M! t& m; j: g

* _2 {( g! d/ @% I# {
3 W( r9 G7 W4 [) d- t! L! j

来 boot.py挂载数据分区。

# c% ^" t7 P: I) @0 L
混合动力(ESP32)

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

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

  1. import esp32, os  T7 P" e7 k8 x8 E- i4 S+ Z, @, S7 I+ I
  2. p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')' A' a+ ~- i) w9 k; }0 x( z( d! [
  3. os.mount(p, '/foo')
复制代码

1 S" F: Z# I" w0 m* t3 Q+ Q- l/ n% U& `1 }8 i

! R, ]+ f- C1 i: o
8 `# R/ J; r" g3 n" B* A8 M
& A* d5 T& ^" l  y# [

6 @6 e9 f' f" x- b- E" u8 @8 @( u" I/ @

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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

粤公网安备 44030702001224号

GMT+8, 2025-9-18 11:02 , Processed in 0.156000 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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