diff --git a/examples/EmulateEEPROM/EmulateEEPROM.ino b/examples/EmulateEEPROM/EmulateEEPROM.ino index d13788a..8c6ec7e 100644 --- a/examples/EmulateEEPROM/EmulateEEPROM.ino +++ b/examples/EmulateEEPROM/EmulateEEPROM.ino @@ -5,14 +5,24 @@ Written by A. Christian Edited 14 Oct 2016 by Cristian Maglie + Updated 30 Sept 2020 by Alexander Christian */ // Include EEPROM-like API for FlashStorage #include +// Create an area in flash memory for EEPROM with 1024 bytes +// Keep in mind that EEPROM is backed due to technical reasons +// in RAM. EEPROM with 1024 bytes means: +// 1k of RAM just for accessing EEPROM +Flash(my_eeprom_storage, 1024); + void setup() { Serial.begin(9600); + // tell the EEPROM implementation which flash to use + EEPROM.setStorage(&my_eeprom_storage); + // If the EEPROM is empty then isValid() is false if (!EEPROM.isValid()) { diff --git a/readme.md b/readme.md index 8528f03..65988a0 100644 --- a/readme.md +++ b/readme.md @@ -8,8 +8,7 @@ code, but it can also be used to store user data. ## Supported hardware -Currently, ATSAMD21 and ATSAMD51 cpu are supported (and consequently every board based -on this cpu like the Arduino Zero or Aduino MKR1000). +Currently, ATSAMD21 and ATSAMD51 cpu are supported (and consequently every board based on this cpu like the Arduino Zero or Aduino MKR1000). ## Limited number of writes @@ -63,6 +62,8 @@ See [EmulateEEPROM](https://github.com/cmaglie/FlashStorage/tree/master/examples The API is very similar to the well known Arduino EEPROM.h API but with two additional functions: +* `EEPROM.setStorage(FlashClass*)` is required to point the EEPROM implementation to the underlying flashclass pointer. This method in mandatory. +* `EEPROM.setStorageForceValid(FlashClass*)` same as `setStorage()`, but forces the valid flag to true before EEPROM gets initialized. This can be useful if take care of the validity-status by yourself or if you don't care about the validity at all. * `EEPROM.isValid()` returns `true` if data in the EEPROM is valid or, in other words, if the data has been written at least once, otherwise EEPROM data is "undefined" and the function returns `false`. * `EEPROM.commit()` store the EEPROM data in flash. Use this with care: Every call writes the complete EEPROM data to flash. This will reduce the remaining flash-write-cycles. Don't call this method in a loop or [you will kill your flash soon](https://github.com/cmaglie/FlashStorage#limited-number-of-writes). @@ -82,7 +83,6 @@ sketch for an example on how to do it. Yes, every time you upload a new sketch, the previous content of the FlashStorage is erased and filled with 0's. The FlashStorage library does not allow to set another default value. - ### Do you recommend to use FLASH instead of EEPROM? No. If your micro provides an EEPROM it's almost always better to use that because diff --git a/src/FlashAsEEPROM.cpp b/src/FlashAsEEPROM.cpp index 72c6698..37fc170 100644 --- a/src/FlashAsEEPROM.cpp +++ b/src/FlashAsEEPROM.cpp @@ -2,7 +2,7 @@ EEPROM like API that uses Arduino Zero's flash memory. Written by A. Christian - Copyright (c) 2015-2016 Arduino LLC. All right reserved. + Copyright (c) 2015-2020 Arduino LLC. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -21,54 +21,86 @@ #include "FlashAsEEPROM.h" -FlashStorage(eeprom_storage, EEPROM_EMULATION); +// separate flash storage for eeprom valid flag +FlashStorage(eeprom_valid, int); EEPROMClass::EEPROMClass(void) : _initialized(false), _dirty(false) { // Empty } -uint8_t EEPROMClass::read(int address) -{ - if (!_initialized) init(); - return _eeprom.data[address]; +uint8_t EEPROMClass::read(int address) { + // if no storage has been set or address is out of range -> just do nothing + // and return 0 + if (flash_class == NULL || address >= flash_class->length()) + return 0; + + return _eeprom[address]; } -void EEPROMClass::update(int address, uint8_t value) -{ - if (!_initialized) init(); - if (_eeprom.data[address] != value) { +void EEPROMClass::write(int address, uint8_t value) { update(address, value); } + +void EEPROMClass::update(int address, uint8_t value) { + // if no storage has been set or address is out of range -> just do nothing + if (flash_class == NULL || address >= flash_class->length()) + return; + + if (_eeprom[address] != value) { _dirty = true; - _eeprom.data[address] = value; + _eeprom[address] = value; } } -void EEPROMClass::write(int address, uint8_t value) -{ - update(address, value); -} +bool EEPROMClass::isValid() { return eeprom_valid.read() == TRUE; } + +void EEPROMClass::commit() { + + // if no storage has been set, just do nothing + if (flash_class == NULL) + return; + + // if something changed and thus dirty-flag is set ... + if (_dirty) { + + // do the actual commit by erasing and writing data + flash_class->erase(); + flash_class->write(_eeprom); + + // set valid flag, if not yet done. + if (!isValid()) { + eeprom_valid.write((int)TRUE); + } -void EEPROMClass::init() -{ - _eeprom = eeprom_storage.read(); - if (!_eeprom.valid) { - memset(_eeprom.data, 0xFF, EEPROM_EMULATION_SIZE); + _dirty = false; // toggle dirty back to non-dirty } - _initialized = true; } -bool EEPROMClass::isValid() -{ - if (!_initialized) init(); - return _eeprom.valid; +void EEPROMClass::setStorageForceValid(FlashClass *ptr) { + if (!isValid()) { + eeprom_valid.write((int)TRUE); // force valid flag to true + } + setStorage(ptr); // continue with normal setStorage() ... } -void EEPROMClass::commit() -{ - if (!_initialized) init(); - if (_dirty) { - _eeprom.valid = true; - eeprom_storage.write(_eeprom); +// PRIVATE begins here + +void EEPROMClass::init() { + + // if no storage has been set, just do nothing + if (flash_class == NULL) + return; + + // init the local RAM version of EEPROM with correct size + _eeprom = new byte[flash_class->length()]; + + // read the flash and return eeprom data + flash_class->read(_eeprom); + + // if EEPROM value is not valid, clear EEPROM with 0xFF + if (!isValid()) { + memset(_eeprom, 0xFF, flash_class->length()); } + + _initialized = true; } EEPROMClass EEPROM; diff --git a/src/FlashAsEEPROM.h b/src/FlashAsEEPROM.h index 8ea3a91..f87560e 100644 --- a/src/FlashAsEEPROM.h +++ b/src/FlashAsEEPROM.h @@ -2,7 +2,7 @@ EEPROM like API that uses Arduino Zero's flash memory. Written by A. Christian - Copyright (c) 2015-2016 Arduino LLC. All right reserved. + Copyright (c) 2015-2020 Arduino LLC. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -24,15 +24,7 @@ #include "FlashStorage.h" -#ifndef EEPROM_EMULATION_SIZE -#define EEPROM_EMULATION_SIZE 1024 -#endif - -typedef struct { - byte data[EEPROM_EMULATION_SIZE]; - boolean valid; -} EEPROM_EMULATION; - +#define TRUE 0x01 class EEPROMClass { @@ -40,45 +32,73 @@ class EEPROMClass { EEPROMClass(void); /** - * Read an eeprom cell + * Read an EEPROM cell * @param index - * @return value + * @return value or 0 if index is out of EEPROM range */ uint8_t read(int); /** - * Write value to an eeprom cell + * Write value to an EEPROM cell. Will do nothing if index is out of EEPROM range. * @param index * @param value */ void write(int, uint8_t); /** - * Update a eeprom cell + * Update a EEPROM cell * @param index * @param value */ void update(int, uint8_t); /** - * Check whether the eeprom data is valid - * @return true, if eeprom data is valid (has been written at least once), false if not + * Check whether the EEPROM data is valid + * @return true, if EEPROM data is valid (has been written at least once), false if not */ bool isValid(); /** - * Write previously made eeprom changes to the underlying flash storage + * Write previously made EEPROM changes to the underlying flash storage * Use this with care: Each and every commit will harm the flash and reduce it's lifetime (like with every flash memory) */ void commit(); - uint16_t length() { return EEPROM_EMULATION_SIZE; } + /** + * Returns the size of the EEPROM in bytes + */ + uint32_t length() { + if (_initialized) { + return flash_class->length(); + } else { + return 0; + } + } + + /** + * For using the EEPROM, you need to specify the underlying FlashClass pointer. For example, useful + * + * Flash(my_eeprom_storage, EEPROM_EMULATION_SIZE); + * + * outside setup() and loop() and provide "my_eeprom_storage" as pointer to this method. + * EEPROM_EMULATION_SIZE specifies the size in bytes of your "virtual EEPROM" (1024, 2048, 4096, 8192). + */ + void setStorage(FlashClass* ptr) { + flash_class = ptr; + init(); + } + + /** + * Same as setStorage(), but forced the valid flag (before internal initalization is done) to true. + * Might be useful if you specify the flash-storage to a specific address of flash and maintain your own valid flag. + */ + void setStorageForceValid(FlashClass*); private: void init(); - + FlashClass* flash_class; bool _initialized; - EEPROM_EMULATION _eeprom; + byte* _eeprom; bool _dirty; };