MicroPython:Основы/Язык MicroPython и его реализация/Работа с файловыми системами: различия между версиями
Myagkij (обсуждение | вклад) |
Myagkij (обсуждение | вклад) Нет описания правки |
||
Строка 3: | Строка 3: | ||
{{Myagkij-редактор}} | {{Myagkij-редактор}} | ||
=Работа с файловыми системами<ref>[http://docs.micropython.org/en/latest/reference/filesystem.html docs.micropython.org - Working with filesystems]</ref>= | |||
В этой статье рассказывается о том, как [[MicroPython]] предоставляет доступ к файловой системе, смонтированной на устройство, позволяя использовать при работе с постоянной энергонезависимой памятью стандартные [[Python]]’овские файловые методы чтения/записи. | |||
[[MicroPython]] автоматически задает настройки по умолчанию и определяет главную файловую систему, поэтому это руководство будет полезно в первую очередь, если вам нужно переопределить разделы файловой системы, изменить ее тип или воспользоваться блочными устройствами с вашими настройками. | |||
Файловая система обычно монтируется на внутреннюю [[flash-память]] устройства, но она также может располагаться на внешней [[flash]]-памяти, в [[RAM]]-памяти или на блочном устройстве. | |||
На некоторых портах (например, на [[STM32-порт]]е) доступ к файловой системе можно получить на [[ПК]] через [[USB-накопитель]]. С помощью инструмента [http://docs.micropython.org/en/latest/reference/pyboard.py.html#pyboard-py pyboard.py] [[ПК-хост]] может получить доступ к файловой системе на всех портах. | |||
'''Примечание:''' Это касается, как правило, ''«голых»'' устройств вроде [[STM32]] или [[ESP32]]. На портах с операционной системой (например, на [[Unix-порт]]е) за файловую систему отвечает [[ОС]] хоста. | |||
== VFS == | |||
В MicroPython используется [[VFS]] (от англ. ''«virtual file system»'', что можно перевести как ''«виртуальная файловая система»'') на манер той, что используется в [[Unix]]. Все смонтированные файловые системы комбинируются в одну виртуальную файловую систему с корневой директорией ''«/»''. Файловые системы монтируются в директории этой структуры, и при запуске рабочей директорией становится та, куда смонтирована главная файловая система. | |||
На [[STM32]]/[[Pyboard]] внутренняя [[flash-память]] монтируется в ''«/flash»'', а опциональная [[SD-карта]] – в ''«/sd»''. На [[ESP8266]]/[[ESP32]] главная файловая система монтируется в ''«/»''. | |||
== Блочные устройства == | |||
Блочное устройство – это экземпляр класса, в котором реализован протокол [http://docs.micropython.org/en/latest/library/uos.html#uos.AbstractBlockDev uos.AbstractBlockDev]. | |||
=== Встроенные блочные устройства === | |||
В некоторых портах есть встроенные блочные устройства для доступа к их основной [[flash-памяти]]. | |||
При включении [[MicroPython]] попытается определить файловую систему на основной [[flash-памяти]], а также автоматически настроить и смонтировать ее. Если файловой системы найдено не будет, [[MicroPython]] попытается создать [[FAT]]-систему на всей [[flash-памяти]]. У используемого вами порта также может быть механизм сброса основной [[flash-памяти]] к заводским настройкам – обычно это выполняется нажатием на определенные кнопки при включении. | |||
==== STM32/Pyboard ==== | |||
На этих платформах доступ к внутренней [[flash-памяти]] осуществляется с помощью класса [http://docs.micropython.org/en/latest/library/pyb.Flash.html#pyb-flash pyb.Flash]. На некоторых платах с большой внешней памятью (вроде Pyboard D) именно она будет использоваться вместо внутренней [[flash-памяти]]. При этом обязательно нужно указать именованный аргумент (kwarg) start – например, '''pyb.Flash(start=0)'''. | |||
'''Примечание:''' При конструировании без аргументов – например, '''pyb.Flash()''' – в этом классе будет реализован только простой блочной интерфейс и отражено виртуальное устройство, показываемое [[USB-накопителю]] (т.е. на старте в нем будет виртуальная таблица разделов). Это сделано в целях обратной совместимости. | |||
==== ESP8266 ==== | |||
Внутренняя [[flash-память]] отображается в виде объекта блочного устройства, создаваемого на запуске в модуле [[flashbdev]]. По умолчанию этот объект добавляется как глобальная переменная, поэтому, как правило, доступ к нему можно получить просто через [[bdev]]. Это реализует расширенный интерфейс. | |||
==== ESP32 ==== | |||
Класс [http://docs.micropython.org/en/latest/library/esp32.html#esp32.Partition esp32.Partition] реализует блочное устройство для разделов, заданных для платы. Как и в случае с [[ESP8266]], в нем есть глобальная переменная [[bdev]], в которой задано базовое исходное разделение. Это реализует расширенный интерфейс. | |||
=== Настраиваемые блочные устройства === | |||
Класс ниже реализует простое блочное устройство, которое хранит свои данные в [[RAM-памяти]] при помощи [[bytearray]]: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
class RAMBlockDev: | |||
def __init__(self, block_size, num_blocks): | |||
self.block_size = block_size | |||
self.data = bytearray(block_size * num_blocks) | |||
def readblocks(self, block_num, buf): | |||
for i in range(len(buf)): | |||
buf[i] = self.data[block_num * self.block_size + i] | |||
def writeblocks(self, block_num, buf): | |||
for i in range(len(buf)): | |||
self.data[block_num * self.block_size + i] = buf[i] | |||
def ioctl(self, op, arg): | |||
if op == 4: # считываем количество блоков | |||
return len(self.data) // self.block_size | |||
if op == 5: # считываем размер блока | |||
return self.block_size | |||
</syntaxhighlight> | |||
Воспользоваться им можно следующим образом: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
import os | |||
bdev = RAMBlockDev(512, 50) | |||
os.VfsFat.mkfs(bdev) | |||
os.mount(bdev, '/ramdisk') | |||
</syntaxhighlight> | |||
Ниже показан пример блочного устройства, поддерживающего простой и расширенный интерфейс – т.е. одновременно сигнатуры и поведение обоих форм методов [http://docs.micropython.org/en/latest/library/uos.html#uos.AbstractBlockDev.readblocks uos.AbstractBlockDev.readblocks()] и [http://docs.micropython.org/en/latest/library/uos.html#uos.AbstractBlockDev.writeblocks uos.AbstractBlockDev.writeblocks()]: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
class RAMBlockDev: | |||
def __init__(self, block_size, num_blocks): | |||
self.block_size = block_size | |||
self.data = bytearray(block_size * num_blocks) | |||
def readblocks(self, block_num, buf, offset=0): | |||
addr = block_num * self.block_size + offset | |||
for i in range(len(buf)): | |||
buf[i] = self.data[addr + i] | |||
def writeblocks(self, block_num, buf, offset=None): | |||
if offset is None: | |||
# сначала стираем, потом записываем: | |||
for i in range(len(buf) // self.block_size): | |||
self.ioctl(6, block_num + i) | |||
offset = 0 | |||
addr = block_num * self.block_size + offset | |||
for i in range(len(buf)): | |||
self.data[addr + i] = buf[i] | |||
def ioctl(self, op, arg): | |||
if op == 4: # счетчик блоков | |||
return len(self.data) // self.block_size | |||
if op == 5: # размер блока | |||
return self.block_size | |||
if op == 6: # стирание блока | |||
return 0 | |||
</syntaxhighlight> | |||
Поскольку это блочное устройство поддерживает расширенный интерфейс, его можно использовать с [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsLfs2 littlefs]: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
import os | |||
bdev = RAMBlockDev(512, 50) | |||
os.VfsLfs2.mkfs(bdev) | |||
os.mount(bdev, '/ramdisk') | |||
</syntaxhighlight> | |||
Когда файловая система будет смонтирована, ее (независимо от типа) можно использовать так, как ее обычно используют в [[Python-код]]е. Например: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
with open('/ramdisk/hello.txt', 'w') as f: | |||
f.write('Hello world') | |||
print(open('/ramdisk/hello.txt').read()) | |||
</syntaxhighlight> | |||
== Файловые системы == | |||
MicroPython-портами поддерживаются файловые системы [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsFat FAT], [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsLfs1 littlefs v1] и [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsLfs2 littlefs v2]. | |||
В таблице ниже показано, какие файловые системы включены в прошивку по умолчанию в разных портах/платах (они, впрочем, могут быть опционально включены в пользовательскую прошивку): | |||
Плата FAT littlefs v1 littlefs v2 | |||
pyboard 1.0, 1.1, D Да Нет Да | |||
Другие STM32 Да Нет Нет | |||
ESP8266 (1M) Нет Нет Да | |||
ESP8266 (2M+) Да Нет Да | |||
ESP32 Да Нет Да | |||
=== FAT === | |||
Главное преимущество файловой системы типа FAT в том, что доступ к ней можно получить с помощью USB-накопителя (на тех платах, где это поддерживается – например, на STM32) безо всяких дополнительных драйверов для ПК-хоста. | |||
Однако FAT не терпима к перебоям питания во время записи – это может привести к повреждению файловой системы. Поэтому в приложениях, не требующих использования USB-накопителя, рекомендуем использовать файловую систему типа littlefs. | |||
Чтобы отформатировать всю flash-память при помощи FAT, выполните следующее: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
# ESP8266 и ESP32: | |||
import os | |||
os.umount('/') | |||
os.VfsFat.mkfs(bdev) | |||
os.mount(bdev, '/') | |||
# STM32: | |||
import os, pyb | |||
os.umount('/flash') | |||
os.VfsFat.mkfs(pyb.Flash(start=0)) | |||
os.mount(pyb.Flash(start=0), '/flash') | |||
os.chdir('/flash') | |||
</syntaxhighlight> | |||
=== Littlefs === | |||
[https://github.com/ARMmbed/littlefs Littlefs] – это файловая система, разработанная специально устройств, полагающихся на [[flash-память]]. Она гораздо более устойчива к повреждениям. | |||
'''Примечание:''' По сообщениям некоторых пользователей, ''littlefs v1'' и ''littlefs v2'' в некоторых ситуациях все же дают сбой. Более подробно читайте [https://github.com/ARMmbed/littlefs/issues/347 тут] и [https://github.com/ARMmbed/littlefs/issues/295 тут]. | |||
'''Примечание:''' Доступ к файловой системе ''littlefs'' можно получить и при помощи [[USB-накопителя]] – для этого нужно воспользоваться [https://github.com/ARMmbed/littlefs-fuse/tree/master/littlefs драйвером «littlefs-fuse»]. Помните, чтобы изменить размер блока, вам нужно будет воспользоваться опцией '''-b=4096'''. | |||
Чтобы отформатировать всю [[flash-память]] при помощи ''littlefs v2'', выполните следующее: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
# ESP8266 и ESP32: | |||
import os | |||
os.umount('/') | |||
os.VfsLfs2.mkfs(bdev) | |||
os.mount(bdev, '/') | |||
# STM32: | |||
import os, pyb | |||
os.umount('/flash') | |||
os.VfsLfs2.mkfs(pyb.Flash(start=0)) | |||
os.mount(pyb.Flash(start=0), '/flash') | |||
os.chdir('/flash') | |||
</syntaxhighlight> | |||
=== Гибридная система (STM32) === | |||
Вы можете создавать блочные устройства, занимающие лишь часть [[flash-памяти]] – это делается с помощью именованных аргументов start и len в pyb.Flash. | |||
К примеру, чтобы настроить первые ''256 Кб'' как [[FAT]] (и сделать их доступными через USB-накопитель), а всю остальную память – как ''littlefs'', выполните следующее: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
import os, pyb | |||
os.umount('/flash') | |||
p1 = pyb.Flash(start=0, len=256*1024) | |||
p2 = pyb.Flash(start=256*1024) | |||
os.VfsFat.mkfs(p1) | |||
os.VfsLfs2.mkfs(p2) | |||
os.mount(p1, '/flash') | |||
os.mount(p2, '/data') | |||
os.chdir('/flash') | |||
</syntaxhighlight> | |||
То есть благодаря этому вы можете настроить доступ к Python-файлам, настройкам и прочему редко изменяемому контенту через [[USB-накопитель]], а часто изменяемые данные приложения – разместить на ''littlefs'', которая более устойчива к перебоям питания и т.д. | |||
Разбитие на разделы со смещением ''«0»'' будет смонтировано и определение типа файловой системы будут выполнены автоматически, но если вы хотите сделать раздел ''«/data»'', в ''boot.py'' можно добавить следующее: | |||
<syntaxhighlight lang="python" enclose="div"> | |||
import os, pyb | |||
p2 = pyb.Flash(start=256*1024) | |||
os.mount(p2, '/data') | |||
</syntaxhighlight> | |||
=== Гибридная система (ESP32) === | |||
Если вы создаете свою прошивку для [[ESP32]], то собственное разбиение на разделы можно задать, отредактировав ''partitions.csv''. | |||
При загрузке раздел под названием ''«vfs»'' по умолчанию будет смонтирован в ''«/»'', а дополнительные разделы можно создать в ''boot.py'': | |||
<syntaxhighlight lang="python" enclose="div"> | <syntaxhighlight lang="python" enclose="div"> | ||
import esp32, os | |||
p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo') | |||
os.mount(p, '/foo') | |||
</syntaxhighlight> | |||
=См.также= | =См.также= |
Версия от 19:10, 21 августа 2020
Работа с файловыми системами[1]
В этой статье рассказывается о том, как MicroPython предоставляет доступ к файловой системе, смонтированной на устройство, позволяя использовать при работе с постоянной энергонезависимой памятью стандартные Python’овские файловые методы чтения/записи.
MicroPython автоматически задает настройки по умолчанию и определяет главную файловую систему, поэтому это руководство будет полезно в первую очередь, если вам нужно переопределить разделы файловой системы, изменить ее тип или воспользоваться блочными устройствами с вашими настройками.
Файловая система обычно монтируется на внутреннюю flash-память устройства, но она также может располагаться на внешней flash-памяти, в RAM-памяти или на блочном устройстве.
На некоторых портах (например, на STM32-порте) доступ к файловой системе можно получить на ПК через USB-накопитель. С помощью инструмента pyboard.py ПК-хост может получить доступ к файловой системе на всех портах.
Примечание: Это касается, как правило, «голых» устройств вроде STM32 или ESP32. На портах с операционной системой (например, на Unix-порте) за файловую систему отвечает ОС хоста.
VFS
В MicroPython используется VFS (от англ. «virtual file system», что можно перевести как «виртуальная файловая система») на манер той, что используется в Unix. Все смонтированные файловые системы комбинируются в одну виртуальную файловую систему с корневой директорией «/». Файловые системы монтируются в директории этой структуры, и при запуске рабочей директорией становится та, куда смонтирована главная файловая система.
На STM32/Pyboard внутренняя flash-память монтируется в «/flash», а опциональная SD-карта – в «/sd». На ESP8266/ESP32 главная файловая система монтируется в «/».
Блочные устройства
Блочное устройство – это экземпляр класса, в котором реализован протокол uos.AbstractBlockDev.
Встроенные блочные устройства
В некоторых портах есть встроенные блочные устройства для доступа к их основной flash-памяти.
При включении MicroPython попытается определить файловую систему на основной flash-памяти, а также автоматически настроить и смонтировать ее. Если файловой системы найдено не будет, MicroPython попытается создать FAT-систему на всей flash-памяти. У используемого вами порта также может быть механизм сброса основной flash-памяти к заводским настройкам – обычно это выполняется нажатием на определенные кнопки при включении.
STM32/Pyboard
На этих платформах доступ к внутренней flash-памяти осуществляется с помощью класса pyb.Flash. На некоторых платах с большой внешней памятью (вроде Pyboard D) именно она будет использоваться вместо внутренней flash-памяти. При этом обязательно нужно указать именованный аргумент (kwarg) start – например, pyb.Flash(start=0).
Примечание: При конструировании без аргументов – например, pyb.Flash() – в этом классе будет реализован только простой блочной интерфейс и отражено виртуальное устройство, показываемое USB-накопителю (т.е. на старте в нем будет виртуальная таблица разделов). Это сделано в целях обратной совместимости.
ESP8266
Внутренняя flash-память отображается в виде объекта блочного устройства, создаваемого на запуске в модуле flashbdev. По умолчанию этот объект добавляется как глобальная переменная, поэтому, как правило, доступ к нему можно получить просто через bdev. Это реализует расширенный интерфейс.
ESP32
Класс esp32.Partition реализует блочное устройство для разделов, заданных для платы. Как и в случае с ESP8266, в нем есть глобальная переменная bdev, в которой задано базовое исходное разделение. Это реализует расширенный интерфейс.
Настраиваемые блочные устройства
Класс ниже реализует простое блочное устройство, которое хранит свои данные в RAM-памяти при помощи bytearray:
class RAMBlockDev:
def __init__(self, block_size, num_blocks):
self.block_size = block_size
self.data = bytearray(block_size * num_blocks)
def readblocks(self, block_num, buf):
for i in range(len(buf)):
buf[i] = self.data[block_num * self.block_size + i]
def writeblocks(self, block_num, buf):
for i in range(len(buf)):
self.data[block_num * self.block_size + i] = buf[i]
def ioctl(self, op, arg):
if op == 4: # считываем количество блоков
return len(self.data) // self.block_size
if op == 5: # считываем размер блока
return self.block_size
Воспользоваться им можно следующим образом:
import os
bdev = RAMBlockDev(512, 50)
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/ramdisk')
Ниже показан пример блочного устройства, поддерживающего простой и расширенный интерфейс – т.е. одновременно сигнатуры и поведение обоих форм методов uos.AbstractBlockDev.readblocks() и uos.AbstractBlockDev.writeblocks():
class RAMBlockDev:
def __init__(self, block_size, num_blocks):
self.block_size = block_size
self.data = bytearray(block_size * num_blocks)
def readblocks(self, block_num, buf, offset=0):
addr = block_num * self.block_size + offset
for i in range(len(buf)):
buf[i] = self.data[addr + i]
def writeblocks(self, block_num, buf, offset=None):
if offset is None:
# сначала стираем, потом записываем:
for i in range(len(buf) // self.block_size):
self.ioctl(6, block_num + i)
offset = 0
addr = block_num * self.block_size + offset
for i in range(len(buf)):
self.data[addr + i] = buf[i]
def ioctl(self, op, arg):
if op == 4: # счетчик блоков
return len(self.data) // self.block_size
if op == 5: # размер блока
return self.block_size
if op == 6: # стирание блока
return 0
Поскольку это блочное устройство поддерживает расширенный интерфейс, его можно использовать с littlefs:
import os
bdev = RAMBlockDev(512, 50)
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/ramdisk')
Когда файловая система будет смонтирована, ее (независимо от типа) можно использовать так, как ее обычно используют в Python-коде. Например:
with open('/ramdisk/hello.txt', 'w') as f:
f.write('Hello world')
print(open('/ramdisk/hello.txt').read())
Файловые системы
MicroPython-портами поддерживаются файловые системы FAT, littlefs v1 и littlefs v2.
В таблице ниже показано, какие файловые системы включены в прошивку по умолчанию в разных портах/платах (они, впрочем, могут быть опционально включены в пользовательскую прошивку):
Плата FAT littlefs v1 littlefs v2 pyboard 1.0, 1.1, D Да Нет Да Другие STM32 Да Нет Нет ESP8266 (1M) Нет Нет Да ESP8266 (2M+) Да Нет Да ESP32 Да Нет Да
FAT
Главное преимущество файловой системы типа FAT в том, что доступ к ней можно получить с помощью USB-накопителя (на тех платах, где это поддерживается – например, на STM32) безо всяких дополнительных драйверов для ПК-хоста.
Однако FAT не терпима к перебоям питания во время записи – это может привести к повреждению файловой системы. Поэтому в приложениях, не требующих использования USB-накопителя, рекомендуем использовать файловую систему типа littlefs.
Чтобы отформатировать всю flash-память при помощи FAT, выполните следующее:
# ESP8266 и ESP32:
import os
os.umount('/')
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/')
# STM32:
import os, pyb
os.umount('/flash')
os.VfsFat.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')
Littlefs
Littlefs – это файловая система, разработанная специально устройств, полагающихся на flash-память. Она гораздо более устойчива к повреждениям.
Примечание: По сообщениям некоторых пользователей, littlefs v1 и littlefs v2 в некоторых ситуациях все же дают сбой. Более подробно читайте тут и тут.
Примечание: Доступ к файловой системе littlefs можно получить и при помощи USB-накопителя – для этого нужно воспользоваться драйвером «littlefs-fuse». Помните, чтобы изменить размер блока, вам нужно будет воспользоваться опцией -b=4096.
Чтобы отформатировать всю flash-память при помощи littlefs v2, выполните следующее:
# ESP8266 и ESP32:
import os
os.umount('/')
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/')
# STM32:
import os, pyb
os.umount('/flash')
os.VfsLfs2.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')
Гибридная система (STM32)
Вы можете создавать блочные устройства, занимающие лишь часть flash-памяти – это делается с помощью именованных аргументов start и len в pyb.Flash.
К примеру, чтобы настроить первые 256 Кб как FAT (и сделать их доступными через USB-накопитель), а всю остальную память – как littlefs, выполните следующее:
import os, pyb
os.umount('/flash')
p1 = pyb.Flash(start=0, len=256*1024)
p2 = pyb.Flash(start=256*1024)
os.VfsFat.mkfs(p1)
os.VfsLfs2.mkfs(p2)
os.mount(p1, '/flash')
os.mount(p2, '/data')
os.chdir('/flash')
То есть благодаря этому вы можете настроить доступ к Python-файлам, настройкам и прочему редко изменяемому контенту через USB-накопитель, а часто изменяемые данные приложения – разместить на littlefs, которая более устойчива к перебоям питания и т.д.
Разбитие на разделы со смещением «0» будет смонтировано и определение типа файловой системы будут выполнены автоматически, но если вы хотите сделать раздел «/data», в boot.py можно добавить следующее:
import os, pyb
p2 = pyb.Flash(start=256*1024)
os.mount(p2, '/data')
Гибридная система (ESP32)
Если вы создаете свою прошивку для ESP32, то собственное разбиение на разделы можно задать, отредактировав partitions.csv.
При загрузке раздел под названием «vfs» по умолчанию будет смонтирован в «/», а дополнительные разделы можно создать в boot.py:
import esp32, os
p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
os.mount(p, '/foo')