41. Внешняя память: SPI Flash, NAND, SD
Встроенной Flash у микроконтроллера мало: 64 КБ у BluePill, 1 МБ у F407 — и вся она занята программой. Как только нужно писать логи, хранить картинки для дисплея или накапливать измерения, приходится ставить внешнюю память.
Как это называется в документации
Оригинал Перевод page program запись страницы sector / block erase стирание сектора / блока write enable latch защёлка разрешения записи busy flag флаг занятости endurance ресурс, число циклов перезаписи data retention срок хранения данных wear levelling выравнивание износа bad block management обработка сбойных блоков error correction code (ECC) код коррекции ошибок memory-mapped mode режим отображения в память
41.1 Что бывает
| Тип | Объём | Чтение | Запись | Ресурс | Чем хорош |
|---|---|---|---|---|---|
| EEPROM | байты–килобайты | побайтно | побайтно | 1 млн | настройки, счётчики |
| FRAM | килобайты | побайтно | побайтно, мгновенно | 10¹² | там, где пишут постоянно |
| NOR Flash | 1–32 МБ | побайтно | страницами | 100 тыс. | логи, картинки, прошивки |
| NAND Flash | 128 МБ–ГБ | страницами | страницами | 1–100 тыс. | много данных, дёшево |
| SD / eMMC | гигабайты | блоками | блоками | зависит | съёмный носитель, готовая ФС |
То, что в обиходе зовут «SPI‑флешкой» — микросхемы вроде W25Q или AT45 — это NOR, а не NAND. Разница принципиальная.
41.2 NOR против NAND
| NOR | NAND | |
|---|---|---|
| чтение | любого байта напрямую | только страницами целиком |
| единица стирания | сектор 4 КБ | блок 64–256 КБ |
| сбойные блоки | нет | есть с завода, это норма |
| коррекция ошибок | не нужна | обязательна |
| цена за мегабайт | выше | ниже |
| типовой объём | до 32 МБ | от 128 МБ |
| сложность драйвера | низкая | высокая |
Главное практическое различие. У NAND часть блоков нерабочая прямо с завода — производитель это документирует и помечает такие блоки специальным байтом. Драйвер обязан их находить, обходить и вести таблицу замен. Плюс биты в NAND самопроизвольно переворачиваются, поэтому нужен код коррекции ошибок: обычно от 1 до 8 бит на каждые 512 байт, на странице 2 КБ — до 32 бит. У NAND с интерфейсом SPI коррекция и учёт сбойных блоков часто встроены в микросхему — с неё проще начать, если NAND всё же нужна.
Отсюда вывод: начинать с NAND в своём проекте не стоит. Если нужны мегабайты — берите NOR по SPI, драйвер к ней пишется за вечер. Если нужны гигабайты — берите SD‑карту, где вся эта кухня уже спрятана внутри карты её собственным контроллером.
NAND оправдана, когда нужен именно большой объём в маленьком корпусе и есть готовый слой трансляции — например, файловые системы, специально рассчитанные на неё.
41.3 Чем подключать
| Интерфейс | Скорость | Где есть | Замечания |
|---|---|---|---|
| SPI | до 2,25 МБ/с на F103 (18 МГц), до 5,25 МБ/с на F407 (SPI1, 42 МГц) |
везде | четыре провода, проще некуда |
| Quad SPI | до ~50 МБ/с | F412, F446, F469 и старше | отображение в память: содержимое видно как обычный массив |
| SDIO | до ~24 МБ/с | F103 высокой плотности, F4 | четырёхбитная шина для SD |
| FSMC / FMC | десятки МБ/с | F103xC/xD/xE в корпусах на 100 и 144 вывода, F405/F407 (FSMC), F42x и F446 (FMC); у F401 и F411 нет | параллельная шина: SRAM, NOR, NAND, SDRAM |
⚠️ У BluePill нет ни SDIO, ни FSMC. STM32F103C8 — «средней плотности», эти контроллеры появляются только у старших кристаллов линейки (xC/xD/xE). Так что на BluePill остаётся SPI — и его, как правило, достаточно.
Про memory‑mapped режим Quad SPI стоит знать отдельно: контроллер показывает содержимое внешней флешки как обычную область адресов. Читать оттуда можно как из массива, и даже исполнять код — приём, которым расширяют программную память, когда встроенной не хватает.
41.4 Как устроена SPI NOR Flash
Три уровня организации, и их легко перепутать:
| Единица | Размер | Что с ней можно |
|---|---|---|
| страница | 256 байт | записать за одну команду |
| сектор | 4 КБ | минимум, который можно стереть |
| блок | 32 или 64 КБ | стереть быстрее, чем посекторно |
Ключевое правило флеш‑памяти: запись умеет только превращать единицы в нули. Обратно — лишь стиранием, и только целым сектором. Поэтому «переписать один байт» физически означает: считать сектор в ОЗУ, стереть сектор, изменить байт, записать сектор обратно.
Отсюда и подход к логам: писать последовательно, никогда не возвращаясь назад, а стирать секторами по мере надобности.
Порядок любой операции записи:
- послать
Write Enable— иначе микросхема молча проигнорирует команду; - послать саму команду с адресом и данными;
- дождаться сброса флага занятости в регистре статуса.
Разрешение записи сбрасывается железом автоматически после каждой операции, поэтому его взводят перед каждой, а не один раз при старте.
41.5 🔧 W25Q128FV: типовая микросхема
16 МБ, страница 256 байт, сектор 4 КБ, питание 2,7–3,6 В — подключается к STM32 напрямую, без согласования уровней.
Первым делом — проверка связи. Микросхема должна назвать себя:
#define W25_CMD_JEDEC_ID 0x9Fu
uint32_t w25_read_id(void)
{
uint32_t id;
CS_LOW();
spi_xfer(W25_CMD_JEDEC_ID);
id = (uint32_t)spi_xfer(0xFF) << 16; /* производитель: 0xEF — Winbond */
id |= (uint32_t)spi_xfer(0xFF) << 8; /* тип памяти */
id |= (uint32_t)spi_xfer(0xFF); /* ёмкость: 0x18 = 128 Мбит */
CS_HIGH();
return id;
}
Для W25Q128FV ответ — 0xEF4018. Получили нули или 0xFFFFFF — проблема
в обвязке, а не в коде: проверяйте питание, CS, режим SPI и подтяжки
WP#/HOLD#.
Чтение — самая простая операция, стирания не требует:
void w25_read(uint32_t addr, uint8_t *buf, uint32_t len)
{
CS_LOW();
spi_xfer(0x03); /* Read Data */
spi_xfer((uint8_t)(addr >> 16));
spi_xfer((uint8_t)(addr >> 8));
spi_xfer((uint8_t)addr);
for (uint32_t i = 0; i < len; i++) {
buf[i] = spi_xfer(0xFF);
}
CS_HIGH();
}
Запись и стирание обходятся двумя служебными командами. Write Enable
(0x06) разрешает одну следующую запись или стирание, а бит 0 BUSY
регистра статуса 1 (чтение — командой 0x05) стоит в единице, пока
операция идёт:
static void w25_write_enable(void)
{
CS_LOW();
spi_xfer(0x06); /* Write Enable */
CS_HIGH();
}
static void w25_wait_ready(void)
{
CS_LOW();
spi_xfer(0x05); /* Read Status Register-1 */
while (spi_xfer(0xFF) & 0x01u) { } /* бит 0 — BUSY */
CS_HIGH();
}
Бит 1 того же регистра — WEL: после Write Enable он становится
единицей, а по окончании записи или стирания микросхема сбрасывает его
сама. По нему проверяют, что разрешение дошло. В изделии ожидание
ограничивают тайм-аутом.
Запись страницы — с обязательным разрешением и ожиданием:
void w25_page_program(uint32_t addr, const uint8_t *buf, uint32_t len)
{
/* Запись, вышедшая за границу страницы, заворачивается на её начало
и затирает то, что там уже лежало. Обрезаем по границе. */
uint32_t space = 256u - (addr % 256u);
if (len > space) {
len = space;
}
w25_write_enable();
CS_LOW();
spi_xfer(0x02); /* Page Program */
spi_xfer((uint8_t)(addr >> 16));
spi_xfer((uint8_t)(addr >> 8));
spi_xfer((uint8_t)addr);
for (uint32_t i = 0; i < len; i++) {
spi_xfer(buf[i]);
}
CS_HIGH();
w25_wait_ready();
}
⚠️ Заворачивание внутри страницы — ловушка, которую замечают не сразу. Если начать запись с адреса
0x0000 00F0и передать 32 байта, то первые 16 лягут по адресамF0…FF, а следующие 16 — не в начало следующей страницы, а обратно в00…0Fтой же страницы, поверх уже записанного. Ошибки не будет, данные просто окажутся не там. Отсюда обрезка по границе в примере выше.
А поскольку запись умеет только превращать единицы в нули, завернувшиеся байты не заменят старые, а сольются с ними по И — испорчено будет и то, и другое.
Стирание — только целым сектором:
void w25_sector_erase(uint32_t addr) /* 4 КБ, адрес любой внутри сектора */
{
w25_write_enable();
CS_LOW();
spi_xfer(0x20); /* Sector Erase */
spi_xfer((uint8_t)(addr >> 16));
spi_xfer((uint8_t)(addr >> 8));
spi_xfer((uint8_t)addr);
CS_HIGH();
w25_wait_ready(); /* до 400 мс — это долго */
}
⚠️ Стирание занимает сотни миллисекунд, стирание всей микросхемы — десятки секунд. Ожидание в цикле опроса на это время блокирует программу. В реальном изделии флаг занятости опрашивают из основного цикла или по таймеру, а не крутят в ожидании.
Почему трёхбайтный адрес — это ровно 16 МБ. Три байта дают 2²⁴ = 16 777 216 адресов. W25Q128 использует их полностью, и это предел: микросхемы большего объёма требуют перехода в режим четырёхбайтной адресации, где у всех команд появляется лишний байт.
41.6 🔧 AT45DB161E: когда микросхема нестандартная
Полезный контрпример. Тоже SPI Flash, тоже 3,3 В, но устроена иначе.
| W25Q128FV | AT45DB161E | |
|---|---|---|
| объём | 16 МБ | 2 МБ |
| страница | 256 байт | 528 байт |
| буферы на кристалле | нет | два SRAM по 528 байт |
| наименьшее стирание | сектор 4 КБ | страница 528 байт |
| правка байта на месте | через ОЗУ: считать сектор, стереть, записать | внутри микросхемы: чтение, изменение, стирание и запись через буфер |
Откуда 528. Это 512 байт данных плюс 16 служебных — исторически под
контрольные суммы и метки. Микросхема умеет работать и с 512‑байтными
страницами. У серии E переключение обратимо: команда 3D 2A 80 A6
ставит 512 байт, 3D 2A 80 A7 — снова 528, а размер хранится
в энергонезависимом регистре, рассчитанном примерно на 10 000 переключений.
Однократным оно было у старой серии D — ревизию проверяют по даташиту.
Два буфера — её сильная сторона. Можно записать данные в буфер, а потом одной командой перенести в основной массив, причём с автоматическим стиранием страницы. Пока идёт перенос, во второй буфер уже можно готовить следующую порцию. Для микроконтроллера с 20 КБ ОЗУ это ощутимая экономия: не нужно держать копию сектора у себя.
Когда какую брать:
- нужен объём, стандартность и простой слой под файловую систему — W25Q128;
- нужна частая правка отдельных записей при малом ОЗУ, а объёма 2 МБ хватает — AT45DB161E;
- для FatFS первая удобнее: 4096 делится на 512 нацело, а 528 требует либо переключения на 512-байтные страницы, либо трансляции адресов.
41.7 SD‑карта по SPI
Способ получить гигабайты и съёмный носитель, не разбираясь с NAND: внутри карты свой контроллер, который уже занимается износом, сбойными блоками и коррекцией ошибок.
Порядок инициализации:
- подать не менее 74 тактов при поднятом
CS— карта «просыпается»; CMD0— перевести в режим ожидания;CMD8— выяснить версию (SDv1 или SDv2);ACMD41в цикле — дождаться готовности;CMD58— узнать, адресуется карта байтами или блоками.
⚠️ Инициализация идёт на низкой скорости — не быстрее 400 кГц. Только после её завершения делитель SPI переключают на рабочую частоту. Попытка начать сразу на 20 МГц даёт карту, которая «не определяется», причём у разных карт по‑разному, что запутывает окончательно.
CMD0 приходит, пока карта ещё в родном режиме SD, где сумма проверяется
всегда, а CMD8 карта проверяет по контрольной сумме даже в режиме SPI,
поэтому их кадры берут готовыми: 40 00 00 00 00 95 и 48 00 00 01 AA 87 —
последний байт и есть CRC7 со стоповой единицей. У остальных команд сумму
в SPI по умолчанию не проверяют, и на её месте шлют 0x01 — одну
стоповую единицу.
Обмен блоком:
- чтение —
CMD17с номером блока (у старых карт SDSC — с байтовым адресом, это и выясняетCMD58), ответ R1 = 0x00, затем ждать маркер 0xFE, принять 512 байт и 2 байта CRC; - запись —
CMD24, ответ R1 = 0x00, послать маркер 0xFE, 512 байт и два любых байта CRC. Карта отвечает байтом видаxxx00101— «данные приняты» — и держит MISO в нуле, пока пишет: ждать 0xFF.
После снятия CS нужен ещё один байт 0xFF: карта отпускает MISO только
по тактам, и без него соседняя микросхема на той же шине (например,
W25Q128 из 41.5) прочитает мусор.
Блок у SD всегда 512 байт — ровно сектор FAT, поэтому слой диска для файловой системы пишется почти без переходников.
Питание — отдельная тема: карта в пике потребляет до 100 мА и роняет напряжение на тонких проводниках. Конденсатор рядом с держателем обязателен.
41.8 Грабли
Забытый Write Enable. Команда записи выполняется «вхолостую», ошибки нет,
данные не меняются. Проверять — чтением бита WEL (бит 1 регистра
статуса 1).
Запись без стирания. Флеш меняет единицы на нули, но не обратно. Записали в непустой сектор — получите побитовое «И» старого и нового. Данные будут похожи на правду, но испорчены.
Ожидание занятости. Стирание сектора длится сотни миллисекунд. Программа на это время замирает, сторожевой таймер может успеть сработать.
Неверный режим SPI. Большинство таких микросхем работают в режимах 0 и 3.
Перепутанные CPOL/CPHA дают чтение мусора, часто устойчивого — похоже
на «микросхема битая».
Незакреплённые WP# и HOLD#. Оставленные висеть в воздухе, они ловят
наводки: запись то проходит, то нет. Подтянуть к питанию.
Длинные провода. На макетке при 20 МГц SPI работает через раз. Для проверки снизьте частоту до пары мегагерц: заработало — дело в разводке, а не в коде.
Буфер DMA в CCM. Если передача идёт через DMA, буфер не должен лежать в памяти, подключённой напрямую к ядру (раздел 9).
Ресурс. 100 000 циклов на сектор — много, но не бесконечно. Журнал, пишущий в один и тот же сектор каждую секунду, изнашивает его за сутки. Пишите последовательно по кругу.
41.9 Итоги
- «SPI‑флешка» — это NOR. NAND дешевле и объёмнее, но требует обработки сбойных блоков и коррекции ошибок; в своём проекте с неё начинать не стоит.
- Нужны мегабайты — NOR по SPI. Нужны гигабайты — SD‑карта, где всё сложное спрятано внутри.
- У BluePill нет SDIO и FSMC — только SPI. Quad SPI появляется у F412 и старше и умеет показывать флешку как область памяти.
- Запись превращает единицы в нули; вернуть обратно можно только стиранием целого сектора.
- Перед каждой записью —
Write Enable; после — ожидание флага занятости. - Запись за границу страницы заворачивается на её начало и портит данные без всякой ошибки.
- Трёхбайтный адрес — ровно 16 МБ; дальше нужен четырёхбайтный режим.
- Инициализация SD идёт на скорости не выше 400 кГц, потом переключение на рабочую.
Дальше: раздел 41а «SD-карта по SDIO: четыре линии данных, команды, DMA и FatFS» — та же карта, но по родному протоколу и вчетверо быстрее.