โจ EEMUL is a portable, robust, and configurable EEPROM emulation library for microcontrollers that lack built-in EEPROM.
It leverages on-chip Flash to provide persistent parameter storage with:
- ๐ก๏ธ Atomic commits
- ๐ Wear leveling
- โ Bad-block handling
- ๐ฆ Flexible block modes
- ๐งพ Descriptor-based layout
- ๐ก๏ธ Atomic commit protocol โ prevents corrupted states after power loss.
- ๐ Wear leveling (rotate) โ spreads writes evenly across blocks.
- โ Bad-block resilience โ skip or permanently mark failing pages.
- ๐ฆ Multiple block modes:
- Page mode โ one Flash page = one block (simple).
- Sub-block mode โ multiple blocks per page (space efficient).
- ๐ Header flexibility โ compact (16B) or full (32B) headers.
- ๐งพ Descriptor-driven storage โ define parameter sizes once, library handles offsets.
- ๐ ๏ธ Shadow buffer in RAM โ enables differential writes & no-op suppression.
- โ๏ธ Configurable buffer strategy โ static arrays (ROM friendly) or dynamic alloc (flexible).
- ๐ Portable HAL port layer โ implement erase/program/read (CRC optional).
- ๐น Reserved Flash region is divided into logical blocks.
- ๐น Each block = Header + Payload snapshot.
- ๐น At runtime:
- ๐ Locate latest valid committed block.
- ๐ฅ Load payload into RAM shadow buffer.
- โ๏ธ On updates, create a new block (erase โ precommit header โ program payload โ finalize commit).
- ๐ข Sequence counters ensure monotonic versioning.
โก Power-loss safe: only blocks with commit_flag=0x00000000 are treated as valid.
[ Page 0 ] => Block0 (Header + Payload)
[ Page 1 ] => Block1 (Header + Payload)
...
[ Page N ]
+---------+---------+---------+
| Block0 | Block1 | Block2 |
| H + P | H + P | H + P |
+---------+---------+---------+
- Page mode โ simple alignment, fewer blocks.
- Sub-block mode โ better density, useful when payload is small vs page size.
| Mode | Size | Fields | Use case |
|---|---|---|---|
| Compact | 16B | magic, sequence, CRC, commit | Space efficient |
| Full | 32B | magic, sequence, CRCs, payload len, bad-marker, commit flag | Max integrity |
๐ฎ Magic constants:
- Full header โ
"EEMULATE"(64-bit ASCII). - Compact header โ
"EMUL"(32-bit ASCII).
#define EEMUL_REGION_END_ADDR 0x08010000U // Region end address (exclusive)
#define EEMUL_NUMBER_OF_FLASH_PAGES 2U // Reserved pages
#define EEMUL_FLASH_PAGE_SIZE 0x400U // 1KB per page
#define EEMUL_ALIGN_BYTES 4U // Program alignment
#define EEMUL_ENABLE_DYNAMIC_ALLOC 1U // Use malloc/calloc for buffers
#define EEMUL_USE_FULL_BLOCK_HEADER 1U // 1=full (32B), 0=compact (16B)Derived:
- ๐งฎ
EEMUL_REGION_SIZE_BYTES = PAGES ร PAGE_SIZE - ๐งฎ
block_bytes = align_up(header + payload, PAGE_SIZE) - ๐งฎ
blocks_count = REGION_SIZE / block_bytes
typedef uint8_t eemul_version_t[4];
typedef uint8_t eemul_statistics_t[20];
typedef uint8_t eemul_counter_t[1];
typedef uint8_t eemul_error_log_item_t[1];
const uint8_t s_descriptor[] = {
/* ID 1: FW version */
sizeof(eemul_version_t),
/* IDs 2..12: error log entries */
sizeof(eemul_error_log_item_t), sizeof(eemul_error_log_item_t), /* ... */
/* ID 13: statistics page */
sizeof(eemul_statistics_t),
/* ID 14: counter */
sizeof(eemul_counter_t)
};// 1๏ธโฃ Provide Flash ops
const eemul_port_ops_t ops = {
.erase_page = my_erase,
.program = my_prog,
.read = my_read,
.crc32 = NULL // SW CRC fallback
};
// 2๏ธโฃ Configure
eemul_handle_t h;
const eemul_init_config_t cfg = {
.badblock_policy = EEMUL_BADBLOCK_POLICY_MARK,
.block_mode = EEMUL_BLOCK_MODE_SUBBLOCKS,
.bad_retry_threshold = 2,
.enable_monotonic_sequence = true,
};
// 3๏ธโฃ Init
eemul_init(&h, &ops, &cfg, s_descriptor, sizeof(s_descriptor));
// 4๏ธโฃ Write parameter
uint32_t val = 0x12345678;
eemul_write_param(&h, 0, &val, sizeof(val));
// 5๏ธโฃ Read parameter
val = 0;
eemul_read_param(&h, 0, &val, sizeof(val));Two built-in test modes:
- ๐ Region fill test โ sequential writes until the emulation region is full.
- ๐ Overflow test โ observe behavior after storage exceeds capacity.
Logs include:
- ๐ข Active block index + sequence
- ๐ Active block address
- โ Read-back verification
Minimal hooks to implement per MCU:
bool erase_page(uint32_t addr);
bool program(uint32_t addr, const void *src); // size = EEMUL_ALIGN_BYTES
void read(uint32_t addr, void *dst, uint32_t len);
uint32_t crc32(const void *data, uint32_t len); // optionalTemplate: eemul_port_template.c demonstrates GD32 implementation.
โโโ eemul.h # Public API & configuration
โโโ eemul.c # Implementation
โโโ eemul_port.h # Port interface definition
โโโ eemul_port_template.c # Example port
โโโ tests/
โ โโโ eemul_test.h # Test declarations
โ โโโ eemul_test.c # Region fill & overflow tests
โโโ README.md # Project documentation
๐ก Contributions are welcome:
- ๐ Report issues & suggest improvements.
- ๐ด Fork and submit pull requests.
- ๐งช Extend tests, add new port templates, or propose features (e.g., encryption).
Released under the MIT License.
Use freely in personal, academic, and commercial projects.
๐ฅ EEMUL โ reliable EEPROM emulation on any MCU, with Flash-backed safety.