Режимы SPI-флеш ESP32 (QIO, QOUT, DIO, DOUT)
Запись в режиме qio проходит без ошибок, а плата после сброса не грузится и сыплет мусор в лог — классический симптом неверного режима флеш. Разбираемся, чем четыре режима отличаются и почему dio часто единственный рабочий.
Обновлено Редакция GAW
Режим SPI-флеш (SPI flash mode) — способ, которым ESP32 обменивается данными с микросхемой SPI-флеш: сколько линий задействовано и какие команды чтения используются. По документации esptool режимов четыре — DIO, DOUT, QIO, QOUT; их задают опцией --flash-mode команды write-flash, и значение попадает в заголовок образа. Для ошибок прошивки режим — одна из трёх типовых причин, см. статью «Прошивка ESP32 через esptool»; физика четырёхпроводной шины — в термине QSPI.
Четыре режима
--flash-mode | Название | Линии | Команда чтения | Скорость по esptool |
|---|---|---|---|---|
qio | Quad I/O | 4 для адреса и данных | Quad I/O Fast Read, EBh | самая высокая |
qout | Quad Output | 4 только для данных | Quad Output Fast Read, 6Bh | ≈ на 15 % ниже qio |
dio | Dual I/O | 2 для адреса и данных | Dual I/O Fast Read, BBh | ≈ на 45 % ниже qio |
dout | Dual Output | 2 только для данных | Dual Output Fast Read, 3Bh | ≈ на 50 % ниже qio |
Обычный SPI использует четыре вывода — CLK, MOSI, MISO, CS — и передаёт 1 бит за такт: при 40 МГц это 40 Мбит/с, или 5 МБ/с. В dual-режимах MOSI и MISO работают вместе и несут по 2 бита за такт; в «Output»-вариантах так передаются только данные, в «I/O» — ещё и адрес. Quad-режимы добавляют две линии на выводах WP и HOLD флеш и удваивают скорость относительно dual. За одну команду ESP32 читает 32 байта, поэтому выигрыш dio над dout — всего около 5 %.
Когда выбирать dio
По FAQ esptool quad-режимы не работают по трём причинам:
- выводы WP и HOLD микросхемы флеш не подключены к нужным GPIO — на части плат и модулей их не разводят;
- флеш не поддерживает quad — это видно в её даташите, модель можно узнать командой
esptool flash-id; - quad не включается программно для этой модели: SPI-флеш не стандарт, и команды включения у производителей разные. Чип стартует в dual-режиме, а программа пытается перевести флеш в quad; незнакомую модель она переключить не сможет.
Есть и четвёртый случай: часть микросхем поддерживает только команды Dual/Quad Output, тогда dout и qout работают, а dio и qio — нет. Запись в неверном режиме проходит успешно: по документации esptool --flash-mode лишь обновляет заголовок образа и определяет режим только для первого старта после сброса, то есть на саму запись (через флеш-стаб) не влияет, — а вот прочитать код при старте чип уже не может. Отсюда правило: если плата не грузится после прошивки, пробуйте --flash-mode dio.
Потеря скорости меньше, чем кажется: код выполняется из флеш через кеш, и к флеш чип обращается только при промахе — вдвое медленнее лишь перезаполнение кеша. esptool советует в dual-режиме поднимать частоту: 80 МГц в Dual I/O быстрее 40 МГц в Quad I/O.
Где хранится режим
Режим, частота и размер флеш записаны в заголовке bootloader.bin (в ESP-IDF — CONFIG_ESPTOOLPY_FLASHMODE, FLASHFREQ, FLASHSIZE); --flash-mode при записи обновляет этот заголовок. Загрузчик ПЗУ читает заголовок при сбросе и печатает в лог строку вида mode:DIO, clock div:1, где делитель считается от APB 80 МГц. Но QIO и QOUT на этом этапе не поддерживаются: на ESP32 при проекте под qio/qout загрузчик второй ступени записывается в dio/dout, а quad включает уже он сам, определив модель флеш; дальше он перенастраивает флеш по заголовку приложения. Так что mode:DIO в логе у qio-проекта — норма, а не ошибка. У чипов с PSRAM и octal-флеш ESP-IDF умеет подбирать режим автоматически (CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT): для octal-памяти ставится OPI, для quad при выбранном OPI — DIO. Чем файл .bin отличается от HEX и ELF, — в статье «Файлы прошивки».
Частые вопросы
Чем отличаются QIO и DIO?
Числом линий данных. По документации esptool в dio адрес и данные идут по двум линиям (MOSI и MISO, по 2 бита за такт, команда Dual I/O Fast Read BBh), в qio — по четырём, с задействованием выводов WP и HOLD флеш (команда Quad I/O Fast Read EBh, 4 бита за такт). Поэтому qio вдвое быстрее dio при передаче адреса и данных; по таблице esptool dio примерно на 45 % медленнее qio.
Почему модуль работает только в dio, а в qio не грузится?
esptool называет три причины: выводы WP и HOLD флеш не подключены к нужным GPIO чипа (на части плат и модулей их не разводят), сама микросхема флеш не поддерживает quad-режимы, или для этой модели флеш не умеют включить quad программно — каждый производитель делает это своими командами. Запись при этом проходит: --flash-mode лишь обновляет заголовок образа и задаёт режим для старта после сброса, а вот читать код при старте чип не может. Решение — записать с --flash-mode dio.
Насколько dio замедлит программу по сравнению с qio?
Не вдвое. По esptool чип выполняет код из флеш через кеш, и команды чтения уходят в флеш только при промахе кеша; вдвое медленнее лишь само перезаполнение кеша. Документация советует в dual-режиме поднять частоту флеш: 80 МГц в Dual I/O быстрее, чем 40 МГц в Quad I/O.
Источники
- Espressif. esptool Documentation (latest), ESP32: SPI Flash Modes — четыре режима DIO, DOUT, QIO, QOUT задаются опцией --flash-mode команды write-flash; таблица Summary: qio — 4 линии для адреса и данных, самый быстрый; qout — 4 линии для данных, примерно на 15 % медленнее qio; dio — 2 линии для адреса и данных, примерно на 45 % медленнее; dout — 2 линии для данных, примерно на 50 % медленнее; обычный SPI — CLK, MOSI, MISO, CS, максимум бит в секунду равен частоте: 40 МГц = 5 МБ/с; dout — команда Dual Output Fast Read (3BH), адрес по обычному SPI, данные по MOSI и MISO по 2 бита за такт; dio — Dual I/O Fast Read (BBH), адрес тоже по двум линиям; за команду читается 32 байта, dio примерно на 5 % быстрее dout; quad использует выводы WP и HOLD; qout — Quad Output Fast Read (6BH), qio — Quad I/O Fast Read (EBH); FAQ: quad не работает, если WP и HOLD не разведены, флеш не поддерживает quad или не включён нужный командой — чип стартует в dual и программа пытается включить quad; часть флеш поддерживает только Output-команды, тогда dout/qout работают, а dio/qio нет; код из флеш кешируется, перезаполнение кеша в dual примерно вдвое медленнее; 80 МГц в dual I/O быстрее 40 МГц в quad I/O; режим хранится в заголовке bootloader .bin, ПЗУ читает его при сбросе, --flash-mode обновляет заголовок при записи; на ESP32 при qio/qout загрузчик ESP-IDF записывается в dio/dout, а quad включает сам загрузчик по модели флеш
- Espressif. esptool Documentation (latest), ESP32: Boot Mode Selection; Advanced Options — строка лога mode:DIO, clock div:1 — режим и делитель из заголовка загрузчика, соответствуют --flash-mode и --flash-freq; mode может быть DIO, DOUT, QIO, QOUT, но QIO/QOUT здесь не поддерживаются — ПЗУ грузит программный загрузчик в dual, а тот включает quad; clock div — делитель от APB 80 МГц (80 МГц = 1, 40 МГц = 2); ПЗУ грузит загрузчик на частоте кварца, с кварцем 40 МГц SPI-такт вдвое ниже заданного (20 МГц), программный загрузчик поднимает APB до 80 МГц; Advanced Options: --spi-connection поддерживает только NOR-флеш как минимум с Dual I/O
- Espressif. ESP-IDF Programming Guide v6.1, ESP32: Bootloader; Configuration Options Reference — в заголовке каждого .bin — CONFIG_ESPTOOLPY_FLASHMODE, FLASHFREQ, FLASHSIZE; ПЗУ грузит вторую ступень по её заголовку на пониженной частоте и не во всех режимах, вторая ступень перенастраивает флеш по заголовку выбранного приложения; CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT: для octal-флеш режим меняется на OPI, для quad-флеш при выбранном OPI — на DIO
Нашли ошибку? ·
Смотрите также
- QSPISPI с четырьмя линиями данных для флеш-памяти и PSRAM
- Загрузчик второй ступени ESP-IDFbootloader.bin по адресу 0x1000: таблица разделов, выбор приложения, флеш, secure boot
- Флеш-стаб esptool (flasher stub)Программа esptool в RAM чипа вместо загрузчика ПЗУ: быстрее, со сжатием, отключается --no-stub
- PSRAMВнешняя оперативная память на шине SPI: размеры, режимы, выводы
- Прошивка ESP32Загрузчик ПЗУ, GPIO0 и автосброс, write-flash и адреса, ошибки esptool и питание
- Файлы прошивки: HEX, BIN, ELFELF, Intel HEX, S-record, BIN и UF2: адреса, секции, objcopy, size