How to store config file on microcontroller?

 You treat the “config file” as a small block of non-volatile data and give it a safe place + a simple protocol, rather than literally thinking “files on a PC”. Here’s a practical way to do it.




1. Decide where to store the config

Depending on your MCU and how big the config is:

  1. Internal EEPROM (AVR, some STM8, some STM32, PIC, etc.)

    • Easiest: byte-wise writes, survives reset and power off.

    • Great for a few bytes to a few kB (baud rate, calibration constants, flags…).

  2. Internal Flash (most Cortex-M, ESP32, etc.)

    • Use one or more flash pages/sectors reserved for config.

    • You must erase a whole page before rewriting; limited erase cycles (often 10k–100k).

    • Common for MCUs without EEPROM (STM32, nRF, etc.).

  3. External non-volatile memory

    • I²C/SPI EEPROM/FRAM/Flash if you need more space or don’t want to wear out internal flash.

    • FRAM is great when you want many writes (wear basically not a problem).

  4. SD card / filesystem

    • Overkill for tiny configs but nice if you want human-editable text/JSON.

    • Needs a FAT/exFAT or littlefs library and a bigger MCU.

For typical small configs (<< 1kB): internal EEPROM or a dedicated flash page is the sweet spot.


2. Design a simple config “schema”

Think of your config as a small struct with a header:

#define CFG_MAGIC 0x43464731u // "CFG1" #define CFG_VERSION 1 typedef struct { uint32_t magic; // CFG_MAGIC uint16_t version; // CFG_VERSION uint16_t length; // sizeof(Config) uint32_t crc; // CRC over the payload // ---- payload starts here ---- uint32_t baudrate; uint8_t mode; uint8_t flags; uint16_t reserved; // add more fields as needed } Config;

Why the header is important:

  • magic – lets you detect “there’s actually config here” vs erased memory.

  • version – lets you change the struct in future firmware and still handle old configs.

  • length – sanity check, and can help if you add future fields.

  • crc – detect corruption (power loss mid-write, noise, etc.).


3. Overall flow at startup

Pseudo-logic your firmware should follow:

Config g_config; void config_load_or_default(void) { Config tmp; nvm_read(&tmp, NVM_CFG_ADDRESS, sizeof(Config)); // EEPROM/flash read if (tmp.magic != CFG_MAGIC || tmp.version != CFG_VERSION || tmp.length != sizeof(Config) || crc32(&tmp, sizeof(Config) - sizeof(tmp.crc)) != tmp.crc) { // Invalid or old config → use defaults set_default_config(&g_config); config_save(); // store defaults to NVM } else { g_config = tmp; // ok } }

And to save:

void config_save(void) { g_config.magic = CFG_MAGIC; g_config.version = CFG_VERSION; g_config.length = sizeof(Config); g_config.crc = crc32(&g_config, sizeof(Config) - sizeof(g_config.crc)); nvm_erase_sector(NVM_CFG_ADDRESS); // for flash; not needed for EEPROM nvm_write(NVM_CFG_ADDRESS, &g_config, sizeof(Config)); }

nvm_read / nvm_write / nvm_erase_sector are your platform-specific functions
(HAL drivers, EEPROM library, or direct register code).


4. Flash vs EEPROM specifics

If you use internal EEPROM

  • You can usually write byte-by-byte or word-by-word.

  • But: limited write cycles → don’t save on every little change.

  • Strategy:

    • Only call config_save() when user changes something or from a “settings” menu.

    • Optionally rotate location (“wear leveling”) when writing frequently.

Example (Arduino AVR):

#include <EEPROM.h> Config cfg; void config_eeprom_save() { cfg.magic = CFG_MAGIC; cfg.version = CFG_VERSION; cfg.length = sizeof(Config); cfg.crc = crc32((uint8_t*)&cfg, sizeof(Config) - 4); EEPROM.put(0, cfg); // writes entire struct } void config_eeprom_load_or_default() { EEPROM.get(0, cfg); if (cfg.magic != CFG_MAGIC || cfg.version != CFG_VERSION) { set_default_config(&cfg); config_eeprom_save(); } }

If you use internal Flash (no EEPROM)

  • Pick one page (or two for safety) close to the end of flash and never put code there.

  • Flash rules:

    • Erase sets all bits to 1.

    • You can only change 1 → 0 by writing.

    • To go back to 1, you must erase the whole page.

  • Typical pattern:

    • erase page → write whole config struct whenever saving.

    • To avoid frequent erases, you can:

      • Buffer changes in RAM and only save occasionally.

      • Use two slots and ping-pong between them:

        • Write new config to slot B, mark it valid.

        • Next time, write to slot A, etc.

        • On boot, pick the most recent valid slot.


5. Make it robust against power loss

You don’t want a half-written config to brick your settings. Easy tricks:

  1. Two copies / ping-pong

    • Slot A & slot B.

    • Each has its own header, CRC, and maybe a “sequence number”.

    • When saving:

      • Find the older slot.

      • Erase & write the new config there, with sequence++.

    • On boot:

      • Check both slots; pick the one with valid CRC and highest sequence.

  2. Validity flag written last

    • Keep magic or a valid field as the last thing written.

    • On read: if CRC is OK but valid is unset, treat as invalid.

With these patterns, a power cut during save just leaves you with the previous valid config.


6. If config should be human-editable (text, JSON…)

Sometimes you want a config that a user edits on PC:

  • Add an SD card or USB mass storage.

  • Use a text format (INI/JSON) and a file system (FAT, littlefs, etc.).

  • On boot:

    • Try to open /config.txt or /config.json

    • Parse it and fill your Config struct.

    • If missing or invalid → defaults and (optionally) regenerate the file.

This is heavier (code size, RAM, libraries) but nice for complex systems like ESP32, STM32 with RTOS, etc.


7. Summary: a “template” you can adapt

  1. Pick storage: EEPROM, flash page, or external NVM.

  2. Define a Config struct with magic + version + length + crc + payload.

  3. On boot: read → validate → fallback to defaults if invalid.

  4. On change: config_save() that rewrites the struct to NVM, respecting flash/EEPROM rules.

  5. For reliability: use two slots / ping-pong and CRC to survive power loss.

评论

此博客中的热门博文

Detailed Explanation of STM32 HAL Library Clock System

How to remove write protection of STM32 chip?

The automatic white balance algorithm of Raspberry Pi