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 Connect Stm32 To PC?

How do you set up ADC (Analog-to-Digital Converter) in STM32?