microflash is a small C99 flash abstraction for embedded systems.
It requires C99 or newer, does not allocate from the heap, and does not depend on an OS.
It provides:
- caller-owned partition storage,
- caller-owned statistics storage,
- partition-relative read/write/erase operations,
- CRC32 helpers,
- per-partition operation counters,
- explicit NOR erase/write alignment rules.
It does not provide heap allocation, global adapter state, or microconf compatibility helpers.
- Partition names are borrowed strings and must remain valid for the life of the partition.
- The driver context is caller-owned and must outlive the
mflash_tinstance. - Duplicate partition names are rejected.
- Overlapping partition ranges are rejected.
mflash_erase_write()only accepts full erase-unit NOR updates; partial NOR updates are rejected.- RAM erase fills the selected range with zeroes.
- EEPROM, FRAM, and RAM use validated write semantics for
mflash_erase_write(). - Zero-length read/write/erase operations succeed without driver I/O.
- Statistics count successful public read/write/erase operations and saturate at
UINT32_MAX. mflash_crc32(NULL, 0)is supported and returns0.
With CMake:
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failureWith the documented Makefile path:
cd tests
makeDirect C99 compile:
cc -std=c99 -Wall -Wextra -Wpedantic -Werror -Iinclude src/mflash.c tests/test_all.c -o test_all ./test_all
Install and consume with find_package:
cmake -S . -B build-install cmake --build build-install cmake --install build-install --prefix "$PWD/prefix" cmake -S tests/consumer -B build-consumer -DCMAKE_PREFIX_PATH="$PWD/prefix" cmake --build build-consumer ctest --test-dir build-consumer --output-on-failure
ARM compile-only check:
arm-none-eabi-gcc -std=c99 -Wall -Wextra -Wpedantic -Werror -ffreestanding -mcpu=cortex-m3 -mthumb -Iinclude -c src/mflash.c -o mflash.o arm-none-eabi-gcc -std=c99 -Wall -Wextra -Wpedantic -Werror -ffreestanding -mcpu=cortex-m3 -mthumb -Iinclude -c tests/arm_consumer.c -o arm_consumer.o
#include "mflash.h" #include <stdbool.h> #include <stdint.h> #include <string.h> typedef struct { uint8_t *mem; uint32_t size; } mock_ctx_t; static int mock_read(uint32_t addr, void *buf, uint32_t len, void *ctx) { mock_ctx_t *mock = (mock_ctx_t *)ctx; if (mock == NULL || buf == NULL || addr > mock->size || len > mock->size - addr) { return -1; } memcpy(buf, mock->mem + addr, len); return 0; } static int mock_write(uint32_t addr, const void *buf, uint32_t len, void *ctx) { mock_ctx_t *mock = (mock_ctx_t *)ctx; if (mock == NULL || buf == NULL || addr > mock->size || len > mock->size - addr) { return -1; } memcpy(mock->mem + addr, buf, len); return 0; } int main(void) { uint8_t storage[256]; mflash_partition_t partitions[4]; mflash_stats_t stats[4]; mock_ctx_t ctx = { storage, sizeof(storage) }; mflash_driver_t driver = { mock_read, mock_write, NULL, &ctx }; mflash_t flash; uint8_t value = 0x42U; memset(storage, 0xFF, sizeof(storage)); if (mflash_init(&flash, &driver, partitions, stats, 4U) != MFLASH_OK) { return 1; } if (mflash_add_partition(&flash, "config", 0U, 256U, 0U, 1U, MFLASH_TYPE_RAM, false) < 0) { return 1; } if (mflash_write(&flash, 0U, 0U, &value, 1U) != MFLASH_OK) { return 1; } if (mflash_read(&flash, 0U, 0U, &value, 1U) != MFLASH_OK) { return 1; } return value == 0x42U ? 0 : 1; }
- API Reference
- Cookbook
- Design
- Porting Guide
- Troubleshooting
- Verification
- Contributing
- Security
- Changelog
MIT, see LICENSE.