Electronics Design AU
Firmware

How Do You Store Data in Embedded Flash Memory?

Last updated 6 July 2026 · 8 min read

Direct Answer

Storing data in embedded flash means working around three physical constraints: flash can only change bits one way (write), must erase a whole sector (typically 4 KB on NOR flash) to change them back, and each sector survives a finite number of erase cycles (commonly specified between 10,000 and 100,000). The practical solutions form a ladder: a key-value store (ESP-IDF NVS, Zephyr NVS/settings, or an EEPROM-emulation layer on internal MCU flash) for device settings; littlefs for a real filesystem on raw SPI NOR flash — it is copy-on-write, power-loss resilient, and wear-aware by design; FatFs when the medium must be readable by a PC (SD cards, USB mass storage), noting that SD/eMMC bring their own internal wear-levelling controller while raw flash does not; and EEPROM or FRAM for small, frequently-updated data that would exhaust flash endurance. Whatever the layer, critical data needs an atomic update pattern — never a single copy updated in place — and the design deserves an endurance calculation before the first prototype ships.

Detailed Explanation

Almost every embedded product needs to remember something across power cycles — calibration constants, user settings, pairing keys, a data log, an event counter. The natural instinct is "just write it to flash," and that instinct collides with the physics of how flash memory actually works. Understanding three constraints up front turns non-volatile storage from a recurring source of field failures into a routine design task.

Why Flash Is Not an EEPROM

  1. Writes are one-directional. Programming flash can only change erased bits (1s) to 0s. Changing any 0 back to 1 requires erasing an entire sector — typically 4 KB on SPI NOR flash, and often larger on internal MCU flash (sector sizes vary widely by part; check the reference manual).
  2. Erases wear the cells. Each erase cycle degrades the sector slightly; datasheets specify endurance commonly between 10,000 cycles (much internal MCU flash) and 100,000 cycles (typical SPI NOR), after which retention is no longer guaranteed.
  3. Writes and erases are slow and can stall the CPU. Sector erases take tens to hundreds of milliseconds on typical parts, and on many MCUs the core cannot fetch instructions from a flash bank that is mid-erase — a detail that surfaces as mysterious latency spikes when storage and code share a bank. Where the data lives relative to code is a memory map and linker decision, not just a driver call.

NAND flash (the technology inside SD cards, eMMC, and high-density raw NAND chips) adds two more: factory and runtime bad blocks that must be mapped around, and read/program disturb effects requiring error-correcting codes. This is why raw NAND is rarely managed by application firmware directly — SD and eMMC embed a controller that handles it internally.

The Storage Technology Menu

TechnologyTypical useEndurance (typical spec)Notes
Internal MCU flashSettings via EEPROM emulationOften ~10k cyclesFree; shares die with code; write-stall behaviour
SPI NOR flashFilesystem, assets, logs, OTA staging~100k cyclesThe standard external storage for embedded; littlefs's home turf
SD card / eMMCBulk logging, PC-interchangeable dataManaged internallyBuilt-in controller does wear levelling and bad-block management
EEPROM (I2C/SPI)Small, frequently updated values~1M cycles typicalByte-writable, no sector erase; slow, small
FRAMHigh-rate counters, state that changes constantlyEffectively unlimited (~10¹⁴ class)Byte-writable at bus speed; costs more per byte

The pattern worth internalising: flash for data that changes occasionally, EEPROM/FRAM for data that changes constantly. A totaliser counter updated every second belongs in FRAM (or an RTC backup register), not in any flash-based store.

Wear Levelling: Spreading the Damage

Wear levelling means arranging writes so erase cycles spread across many sectors instead of hammering one. It can be as simple as a circular log (append records sequentially through a large region, erase the oldest sector only when wrapping) or as involved as the dynamic block allocation inside littlefs or an SD card's controller. The endurance arithmetic is always the same — total available erase cycles across the rotated region divided by the erase rate — and it should be run for every repeating write in the product before the storage layout is frozen. The worked example in the FAQ below shows how the same workload lasts decades or months depending purely on layout.

Filesystems: littlefs and FatFs

A filesystem earns its overhead when the data is genuinely file-shaped — variable-size records, multiple independent items, growth over time. The two embedded standards solve different problems:

  • littlefs was designed for raw NOR flash on microcontrollers: every update is copy-on-write (the old data remains valid until the new version is completely committed, so power loss at any instant leaves either the old or the new state — never a corrupt one), wear levelling is built in, and RAM use is small and bounded. It is the default choice in Zephyr, Mbed, and much of the modern ecosystem — for internal product storage it should be the starting assumption. (In Zephyr-based firmware it slots in behind the standard filesystem and settings APIs.)
  • FatFs implements FAT — the format PCs, cameras, and card readers understand. Use it when interoperability is the requirement: SD cards a user removes, USB mass-storage. Its weaknesses are the mirror of littlefs's strengths: FAT updates its allocation tables in place (corruption-prone on power loss without careful mount/sync discipline) and has no concept of wear — acceptable on SD/eMMC whose controller handles wear internally, hazardous on raw NOR/NAND without a translation layer.

And sometimes no filesystem is the right answer: a fixed-layout circular record log on raw sectors is simpler, faster, more endurance-predictable, and easier to make power-fail-safe than any filesystem — the standard choice for high-rate black-box logging.

Power-Fail-Safe Writes

Products lose power mid-write — batteries die, users unplug, brownouts happen. The design rule: critical data is never updated in place as a single copy. The minimal robust pattern is two copies plus validation: write the new version to the other slot (with a CRC and an incrementing sequence number), verify it, and only then consider it current; on boot, load the highest-sequence copy with a valid CRC. Key-value stores (ESP-IDF NVS, Zephyr NVS) and littlefs implement equivalent guarantees internally — one reason to use them instead of hand-rolled sector writes. Whatever the mechanism, the verification standard is empirical: a test rig that kills power randomly during sustained writes, thousands of times, and checks state on every reboot. Designs that haven't survived that test haven't demonstrated power-fail safety — they've assumed it.

Fitting Storage into the Flash Map

Data storage shares the flash with the application image and the update machinery, so the partition layout is designed once, together: bootloader, application slot(s), OTA staging area, and the data region(s) — with the data partitions deliberately excluded from what an OTA update erases, so settings survive firmware upgrades, and deliberately included in what a factory reset clears.

Design Considerations

  • Run the endurance arithmetic first. Every repeating write — logs, counters, settings autosave, boot counters — gets the cycles-versus-rate calculation before the layout is frozen. The failures this prevents appear two years after shipping and cannot be fixed remotely.
  • Batch and buffer writes. Accumulate log records in RAM and flush periodically (or on meaningful events) rather than per sample; on sleep-heavy designs, flush before entering deep sleep rather than on every wake. Fewer, larger writes mean fewer erases and less time stalled.
  • Separate configuration from logging. Settings (small, rare writes, must never corrupt) and logs (bulk, constant writes, individual records expendable) have opposite requirements — give them separate partitions and, where budget allows, separate technologies.
  • Design the factory-reset and migration story up front. What clears on factory reset, what survives OTA updates, and how version N+1 firmware reads version N's stored data are schema decisions — add a version field to every stored structure from day one.
  • Plan for manufacturing. First-boot behaviour with blank flash, programming calibration data in production, and pre-loading assets are part of the storage design, not an afterthought for the production line to discover.

Storage bugs are among the most expensive class of field failures because they destroy user data and trust — Zeus Design's firmware team designs and validates non-volatile storage layers, from partition maps to power-fail test rigs, for production embedded products.

Common Mistakes

  • A single settings copy updated in place. The classic design that works on the bench for months and bricks devices in the field the first time power fails mid-write. Two slots, CRC, sequence number — or a store that provides the same guarantee.
  • Ignoring erase-cycle arithmetic for "small" repeating writes. A counter written once a minute to the same sector consumes a 10,000-cycle internal flash in a week. The write's size is irrelevant; the erase rate per sector is everything.
  • FAT on raw NOR flash because a filesystem was needed and FAT was familiar. No wear levelling, in-place metadata updates, no power-loss safety — three field-failure mechanisms in one decision. Use littlefs for internal raw flash; reserve FAT for media a PC must read.
  • Storing constantly-changing state in flash at all. Run-hour totalisers, live positions, rolling averages — these belong in FRAM, battery-backed RAM, or RTC backup registers, with flash receiving occasional checkpoints only.
  • Testing power-fail safety by unplugging a few times. Corruption windows are microseconds wide; hitting them takes an automated rig cutting power at random offsets across thousands of write cycles. A design review that asks "what state results if power dies between these two lines?" for every write path is the cheap version of the same discipline.

Frequently Asked Questions

Should I use littlefs or FatFs?
Decide by who else needs to read the medium. If the storage is internal to the product — settings, logs, assets on a SPI NOR flash chip — littlefs is the stronger default: it was designed for exactly this environment, with copy-on-write updates that survive power loss at any moment, built-in dynamic wear levelling, and small bounded RAM use. If a human will pull the medium out and mount it on a computer — an SD card of data logs, a USB mass-storage interface — FAT is the interoperability standard and FatFs is the de facto embedded implementation. The combination is common: littlefs on internal NOR for configuration, FatFs on the removable SD card for user-facing data. Avoid FAT on raw NOR/NAND flash without a flash translation layer — FAT rewrites its allocation table and directory entries in place, concentrating wear and corrupting easily on power loss.
Can I store settings in the MCU's internal flash?
Yes, and it's standard practice — with two caveats. First, endurance: internal MCU flash is often rated at the lower end (commonly 10,000 cycles, part-dependent), so an EEPROM-emulation scheme that rotates writes across two or more reserved sectors is the normal approach; most vendors publish one (ST's EEPROM-emulation application notes, ESP-IDF's NVS on the ESP32's external flash, Zephyr's NVS backend). Second, concurrency: on many MCUs, writing or erasing flash stalls code execution from the same flash bank, so writes must be scheduled where a few milliseconds of stall (or a RAM-executed write routine) is acceptable — and the sectors used for data must be kept out of the application image's way in the linker script and any OTA partition map.
How do I estimate how long the flash will last?
Multiply out the write pattern against the endurance rating. The arithmetic: (number of sectors available for rotation × rated erase cycles) ÷ (erases required per day) = lifetime in days. For example, a log appending 100 bytes per minute fills a 4 KB sector roughly every 40 minutes — about 36 sector-erases a day if written naively to one region. Rotated across a 1 MB littlefs partition (256 × 4 KB sectors), those erases spread out, and at a typical 100,000-cycle NOR rating the arithmetic gives decades. Concentrated in a single fixed sector, the same workload consumes 100,000 cycles in under eight years — and a 10,000-cycle internal flash in well under one. Do this calculation for every repeating write in the system, including 'rare' ones like a counter updated every boot.
What about SD cards in a production product?
SD cards embed their own controller with internal wear levelling, which removes the raw-flash management burden — but they introduce different production risks: consumer cards vary enormously in endurance (TLC/QLC consumer NAND is not specified for continuous logging), counterfeit and grey-market cards are widespread, cards can vanish from the market mid-production, and the mechanical socket is a real failure point in vibration or field-service contexts. For products that must log continuously for years, industrial-grade cards (or soldered eMMC, which is the same managed-NAND concept without the socket) with specified endurance ratings are the defensible choice, combined with write-reduction in firmware — batching, buffering, and avoiding sync-heavy filesystem patterns.

References

Related Questions

Related Forum Discussions