Files
ch32v003-ext-board/simple_eeprom.h

149 lines
3.2 KiB
C

#ifndef __SIMPLE_EEPROM_H
#define __SIMPLE_EEPROM_H
#include <stdint.h>
#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