#ifndef __SIMPLE_EEPROM_H #define __SIMPLE_EEPROM_H #include #include "ch32v003fun.h" // config #define EEPROM_PAGE_SIZE 64 // size in bytes #define EEPROM_HALFWORD_SIZE 2 // size of halfword in bytes #define EEPROM_MAX_ENTRIES (EEPROM_PAGE_SIZE / EEPROM_HALFWORD_SIZE) typedef enum { EEPROM_OK = 0, EEPROM_ERROR_WRITE, EEPROM_ERROR_ERASE, EEPROM_ERROR_VERIFY } eeprom_status_t; // ######## internal variables // import from .ld, halal by // https://sourceware.org/binutils/docs/ld/Source-Code-Reference.html extern char _reserved_nv_start[]; extern char _reserved_nv_end[]; #define EEPROM_BASE (FLASH_BASE + (uint32_t)(uintptr_t)_reserved_nv_start) static void flash_unlock(void) { if (FLASH->CTLR & 0x80) { FLASH->KEYR = FLASH_KEY1; FLASH->KEYR = FLASH_KEY2; } } static void flash_lock(void) { FLASH->CTLR = 0x80; } static void flash_unlock_fast_mode(void) { if (FLASH->CTLR & 0x8000) { FLASH->MODEKEYR = FLASH_KEY1; FLASH->MODEKEYR = FLASH_KEY2; } } static void flash_wait_busy(void) { while (FLASH->STATR & FLASH_STATR_BSY); } static eeprom_status_t flash_erase_page(void) { uint16_t *page_addr = (uint16_t *)EEPROM_BASE; flash_unlock_fast_mode(); flash_wait_busy(); // erase page FLASH->CTLR = CR_PAGE_ER; FLASH->ADDR = (uint32_t)page_addr; FLASH->CTLR = CR_STRT_Set | CR_PAGE_ER; flash_wait_busy(); // verify erase for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { if (page_addr[i] != 0xFFFF) { return EEPROM_ERROR_ERASE; } } // disable fast mode FLASH->CTLR = 0x8000; return EEPROM_OK; } static int find_next_free_slot(void) { uint16_t *word = (uint16_t *)EEPROM_BASE; for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { if (word[i] == 0xFFFF) { // look for erased entries return i; } } return EEPROM_MAX_ENTRIES; } eeprom_status_t eeprom_write(uint16_t data) { flash_unlock(); // find next available slot int slot = find_next_free_slot(); uint16_t *word = (uint16_t *)EEPROM_BASE + slot; // page is full, erase it if (slot >= EEPROM_MAX_ENTRIES) { eeprom_status_t status = flash_erase_page(); if (status != EEPROM_OK) { flash_lock(); return status; } word = (uint16_t *)EEPROM_BASE; // reset to start of page } // write data FLASH->CTLR = CR_PG_Set; *word = data; // write full 16-bit value flash_wait_busy(); // verify write if (*word != data) { flash_lock(); return EEPROM_ERROR_VERIFY; } flash_lock(); return EEPROM_OK; } uint16_t eeprom_read(void) { uint16_t *word = (uint16_t *)(EEPROM_BASE + EEPROM_PAGE_SIZE - EEPROM_HALFWORD_SIZE); // scan back from the end until we find a non-erased value for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { uint16_t val = *word; if (val != 0xFFFF) { return val; } word--; } return 0xFFFF; // nothing found } eeprom_status_t eeprom_init(void) { uint16_t *page = (uint16_t *)EEPROM_BASE; // check empty/partial page int needs_erase = 1; for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { if (page[i] != 0xFFFF) { needs_erase = 0; break; } } if (needs_erase) { return flash_erase_page(); } return EEPROM_OK; } eeprom_status_t eeprom_wipe(void) { return flash_erase_page(); } #endif