diff --git a/lib/flash_internal.c b/lib/flash_internal.c index 31fb9fd..576053c 100644 --- a/lib/flash_internal.c +++ b/lib/flash_internal.c @@ -31,6 +31,13 @@ #include "flash_internal.h" // flash storage library API #include "global.h" // global definitions +/** number of flash pages, located at the end of flash memory, to use for EEPROM functionality */ +static uint16_t flash_internal_eeprom_pages = 0; +/** start address of flash memory used for the emulated EEPROM */ +static uint32_t flash_internal_eeprom_start = 0; +/** start address of emulated EEPROM */ +static uint32_t flash_internal_eeprom_address = 0; + /** verify if the data is in the internal flash area * @param[in] address start address of the data to read * @param[in] size how much data to read or write, in bytes @@ -217,3 +224,101 @@ int32_t flash_internal_write(uint32_t address, const uint8_t *buffer, size_t siz return written; } + +void flash_internal_eeprom_setup(uint16_t pages) +{ + flash_internal_eeprom_pages = pages; // just need to remember the number of pages + + // get allocated memory address + if ((uint32_t)&__flash_end > (FLASH_BASE + DESIG_FLASH_SIZE * 1024)) { // user specified larger flash than advertised by chip + flash_internal_eeprom_start = (uint32_t)&__flash_end - flash_internal_eeprom_pages * flash_internal_page_size(); + } else { + flash_internal_eeprom_start = (FLASH_BASE + DESIG_FLASH_SIZE * 1024) - flash_internal_eeprom_pages * flash_internal_page_size(); + } + flash_internal_eeprom_start -= flash_internal_eeprom_start % flash_internal_page_size(); // ensure it starts at start of page + + // find EEPROM in flash + flash_internal_eeprom_address = flash_internal_eeprom_start; // by default start with start of allocated flash memory + for (uint32_t addr = flash_internal_eeprom_start; addr < flash_internal_eeprom_start + flash_internal_eeprom_pages * flash_internal_page_size() - 2; addr += 2) { + if (0 != *(uint16_t*)addr) { // 0 is invalidated flash + flash_internal_eeprom_address = addr; // we found a valid address, which should be the size of the EEPROM + break; + } + } + uint16_t size = *(uint16_t*)flash_internal_eeprom_address; + if (size + 2U > flash_internal_eeprom_pages * flash_internal_page_size()) { // there is not enough space + flash_internal_eeprom_address = flash_internal_eeprom_start; // set back to start + } + if (0 != size && flash_internal_eeprom_address + 2U + size > flash_internal_eeprom_start + flash_internal_eeprom_pages * flash_internal_page_size()) { // the size seems to be valid to there is not enough remaining space + flash_internal_eeprom_address = flash_internal_eeprom_start; // set back to start + } +} + +bool flash_internal_eeprom_read(uint8_t *eeprom, uint16_t size) +{ + // sanity checks + if (NULL == eeprom || 0 == size || 0 == flash_internal_eeprom_pages || 0 == flash_internal_eeprom_start || 0 == flash_internal_eeprom_address) { + return false; + } + if (size + 2U > flash_internal_eeprom_pages * flash_internal_page_size()) { // not enough space + return false; + } + + if (size != *(uint16_t*)flash_internal_eeprom_address) { // check if size match + return false; + } + + return flash_internal_read(flash_internal_eeprom_address + 2, eeprom, size); // read data +} + +int32_t flash_internal_eeprom_write(const uint8_t *eeprom, uint16_t size) +{ + // sanity checks + if (NULL == eeprom || 0 == size || 0 == flash_internal_eeprom_pages || 0 == flash_internal_eeprom_start || 0 == flash_internal_eeprom_address) { + return -1; + } + if (size + 2U > flash_internal_eeprom_pages * flash_internal_page_size()) { // not enough space + return -2; + } + + uint16_t current_size = *(uint16_t*)flash_internal_eeprom_address; + if (size == current_size) { // check if it already the same + bool identical = true; + for (uint16_t i = 0; i < size; i++) { + if (eeprom[i] != *((uint8_t*)flash_internal_eeprom_address + 2 + i)) { + identical = false; + break; + } + } + if (identical) { // no need to write since it's identical + return size; + } + } + + // one optimisation would be to check if we just need to flip bits to 0, than we could reuse the same location + + // invalidate current EEPROM + const uint8_t zero[2] = {0, 0}; + flash_internal_write(flash_internal_eeprom_address, zero, 2, false); + flash_internal_eeprom_address += 2; + while (current_size && flash_internal_eeprom_address < flash_internal_eeprom_start + flash_internal_eeprom_pages * flash_internal_page_size()) { + flash_internal_write(flash_internal_eeprom_address, zero, 2, false); + current_size -= 2; + flash_internal_eeprom_address += 2; + } + + // go to start if there is not enough remaining space + if (flash_internal_eeprom_address + size + 2U > flash_internal_eeprom_start + flash_internal_eeprom_pages * flash_internal_page_size()) { + flash_internal_eeprom_address = flash_internal_eeprom_start; + } + + int32_t rc = flash_internal_write(flash_internal_eeprom_address, (uint8_t*)&size, 2, false); + if (2 != rc) { + return (-10 + rc); + } + rc = flash_internal_write(flash_internal_eeprom_address + 2, eeprom, size, false); + if (size != rc) { + return (-10 + rc); + } + return rc; +} diff --git a/lib/flash_internal.h b/lib/flash_internal.h index 925c3f4..f1e891f 100644 --- a/lib/flash_internal.h +++ b/lib/flash_internal.h @@ -40,3 +40,20 @@ int32_t flash_internal_write(uint32_t address, const uint8_t *buffer, size_t siz * @return flash page size (in bytes) */ uint16_t flash_internal_page_size(void); + +/** setup the emulated EEPROM area + * @param[in] pages number of flash pages to allocate for the emulated EEPROM + * @warn area must be at least 4 bytes larger than the structure to write + */ +void flash_internal_eeprom_setup(uint16_t pages); +/** read emulated EEPROM area + * @param[out] eeprom where to store the EEPROM data + * @param[in] size size of the EEPROM area (in bytes) + */ +bool flash_internal_eeprom_read(uint8_t *eeprom, uint16_t size); +/** write emulated EEPROM area + * @param[in] eeprom EEPROM data to be stored + * @param[in] size size of the EEPROM area (in bytes) + * @return number of bytes written (including preserved data), or negative in case of error + */ +int32_t flash_internal_eeprom_write(const uint8_t *eeprom, uint16_t size);