Внешняя SPI flash
Модуль spiflash подключает внешнюю последовательную NOR flash к SPI2 или
SPI3. Накопитель можно использовать напрямую либо как LittleFS с именем
spilfs.
local jedec = spiflash.init(
spiflash.SPI2,
pio.GPIO12, -- MISO
pio.GPIO13, -- MOSI
pio.GPIO14, -- CLK
pio.GPIO15, -- CS
spiflash.FREQ_10M
)
print(string.format("JEDEC ID: %06X", jedec))
local info = spiflash.info()
print(info.capacity, info.page_size, info.sector_size)
Поддерживаемые микросхемы и подключение
Драйвер рассчитан на SPI NOR с обычным JEDEC-набором команд: чтение 0x03,
page program 0x02, стирание сектора 0x20, chip erase 0xC7, write enable
0x06, чтение status register 1 и JEDEC ID. Размер страницы должен быть 256
байт, а erase-сектора — 4096 байт.
Ограничения реализации:
- режим SPI 0, только
spiflash.SPI2иspiflash.SPI3; - частота от 1 Гц до 20 МГц; предусмотрены константы
FREQ_10MиFREQ_20M; - трёхбайтовая адресация и ёмкость не более 16 МиБ;
- JEDEC capacity code от
0x10до0x18, то есть от 64 КиБ до 16 МиБ; - одна внешняя flash и один экземпляр
spilfsв системе.
Подавайте на микросхему напряжение, разрешённое её документацией и уровнями
GPIO ESP32, обычно 3,3 В. Соедините земли, установите развязывающий конденсатор
рядом с корпусом flash и не оставляйте входы WP# и HOLD#/RESET# в воздухе:
если они не используются, подтяните их к неактивному уровню согласно datasheet.
Шина может быть общей с другими устройствами. В таком случае MISO, MOSI и CLK должны совпадать с уже настроенными выводами шины, а CS flash должен быть отдельным. Драйвер удерживает общую блокировку SPI на протяжении всей команды, поэтому другой клиент не может вклиниться между opcode, адресом и данными.
Инициализация и информация
jedec_id = spiflash.init(spi, miso, mosi, clk, cs, clock)
Настраивает устройство, читает JEDEC ID и проверяет код ёмкости. Аргументы
miso, mosi, clk и cs — номера GPIO. Константы spiflash.SPI2 и
spiflash.SPI3 принадлежат этому модулю; не подменяйте их константами хоста из
другого API.
Повторная инициализация без spiflash.deinit() вызывает исключение. Ошибка SPI,
неподдерживаемый JEDEC ID, занятый CS или несовпадающие выводы общей шины также
вызывают исключение и освобождают уже выделенные ресурсы.
info = spiflash.info()
Возвращает таблицу:
| Поле | Значение |
|---|---|
jedec_id | 24-битный JEDEC ID |
capacity | обнаруженная ёмкость в байтах |
page_size | размер программируемой страницы, 256 байт |
sector_size | размер erase-сектора, 4096 байт |
clock | настроенная частота SPI в герцах |
spi | номер шины Lua RTOS: 2 или 3 |
spiflash.deinit()
Отключает устройство и освобождает CS и, если на шине больше нет клиентов,
ресурсы шины. Пока spilfs смонтирована, функция завершается исключением:
сначала закройте файлы и каталоги и вызовите fs.umount().
LittleFS на внешней flash
Поддержка файловой системы включается параметрами
CONFIG_LUA_RTOS_LUA_USE_SPIFLASH=y и
CONFIG_LUA_RTOS_LUA_USE_SPIFLASH_LFS=y. Размер задаёт
CONFIG_LUA_RTOS_SPIFLASH_BLOCK_COUNT: один блок равен 4096 байт, а произведение
не должно превышать обнаруженную ёмкость flash. Допустимо от 64 до 4096 блоков;
значение по умолчанию 2048 создаёт файловую систему размером 8 МиБ.
Автоматическое форматирование при ошибке mount по умолчанию выключено, потому что повреждение проводки или временная ошибка SPI не должны уничтожать данные. Новую микросхему один раз отформатируйте явно из интерактивной консоли:
spiflash.init(spiflash.SPI2, 12, 13, 14, 15, spiflash.FREQ_10M)
os.format("/spilfs", "spilfs") -- запросит подтверждение и удалит все данные
fs.mount("/spilfs", "spilfs")
При обычной загрузке форматирование не требуется:
spiflash.init(spiflash.SPI2, 12, 13, 14, 15, spiflash.FREQ_10M)
fs.mount("/spilfs", "spilfs")
local file = assert(io.open("/spilfs/state.bin", "wb"))
assert(file:write("ready\0"))
assert(file:close())
Если spilfs уже смонтирована, os.format("/spilfs", "spilfs") сначала
проверяет отсутствие открытых файлов и каталогов, затем отключает, форматирует
и снова подключает её. При открытых дескрипторах операция завершается ошибкой
EBUSY. Опция
CONFIG_LUA_RTOS_SPIFLASH_LFS_FORMAT_IF_MOUNT_FAILED=y разрешает
разрушительное автоформатирование, но для рабочих профилей её включать не
следует.
Корректное завершение работы:
fs.umount("/spilfs")
spiflash.deinit()
Прямой доступ
Прямые операции нельзя выполнять параллельно с файловым доступом к тому же носителю: они обходят метаданные LittleFS и могут повредить файловую систему.
data = spiflash.read(address, length)
Читает бинарную Lua-строку. length должен быть от 1 до 4096 байт, а весь
диапазон — находиться внутри обнаруженной ёмкости. Ошибки создают исключение.
status = spiflash.write(address, data [, length])
Записывает непустую бинарную строку. Драйвер сам делит операцию по границам
256-байтных страниц. Необязательный length сохранён для совместимости и должен
точно совпадать с длиной строки; обрезание по NUL не выполняется.
NOR flash умеет программировать биты только из 1 в 0. Перед повторной записью данных, требующей перехода из 0 в 1, сотрите соответствующий сектор.
status = spiflash.erase([address])
С адресом стирает один 4096-байтный сектор; адрес должен быть выровнен по 4096. Без адреса запускает стирание всей микросхемы, которое может продолжаться несколько минут.
spiflash.dump(address)
Читает 256 байт и выводит hex/ASCII dump в консоль. Диапазон должен полностью помещаться в flash.
write() и erase() возвращают числовой статус:
| Код | Значение |
|---|---|
| 0 | операция выполнена |
| 1 | ошибка передачи SPI |
| 2 | включена аппаратная или status-register защита записи |
| 3 | устройство не готово или не инициализировано |
| 4 | неверный адрес, длина или выравнивание |
| 5 | истёк тайм-аут операции |
| 6 | недостаточно памяти |
| 7 | шина, CS или устройство заняты |
| 8 | JEDEC ID или ёмкость не поддерживаются |
Для записи проверяйте status == 0. Драйвер подтверждает установку WEL,
ожидает снятия BUSY с ограниченным тайм-аутом и проверяет, что после
программирования или стирания WEL сброшен микросхемой.
Миграция со старого контура
SPI1,FREQ_40MиFREQ_80Mбольше не поддерживаются;- внешняя FAT (
spifat) удалена; для внешней NOR используется только LittleFS; - размеры read/prog/page/sector больше не задаются отдельно: они фиксированы как 256/256/256/4096 байт;
- автоформатирование при неудачном mount теперь выключено по умолчанию;
- старый третий аргумент
write(address, data, length)допустим только при точном совпаденииlengthс бинарной длинойdata.
Перед выпуском прошивки проверьте целевую микросхему на реальной плате: стабильность чтения на выбранной частоте, стирание и запись всех секторов, совместную работу остальных клиентов SPI и восстановление LittleFS после отключения питания во время записи.