From 71de267527c055820fc266a9825a0af58ce16f6e Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Wed, 30 Sep 2020 13:42:03 +0200 Subject: [PATCH 1/7] changed EEPROM implementation according to discussion of https://github.com/cmaglie/FlashStorage/issues/44 --- examples/EmulateEEPROM/EmulateEEPROM.ino | 10 +++ readme.md | 7 +- src/FlashAsEEPROM.cpp | 92 ++++++++++++++++-------- src/FlashAsEEPROM.h | 60 ++++++++++------ 4 files changed, 115 insertions(+), 54 deletions(-) diff --git a/examples/EmulateEEPROM/EmulateEEPROM.ino b/examples/EmulateEEPROM/EmulateEEPROM.ino index d13788a..9b2f082 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 a 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..fff9178 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +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 +Currently, only ATSAMD21 cpu is supported (and consequently every board based on this cpu like the Arduino Zero or Aduino MKR1000). ## Limited number of writes @@ -63,6 +63,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). @@ -80,8 +82,7 @@ sketch for an example on how to do it. ### The content of the FlashStorage is erased each time a new sketch is uploaded? -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. - +Yes, every time you upload a new sketch, the previous content of the FlashStorage is erased. ### Do you recommend to use FLASH instead of EEPROM? diff --git a/src/FlashAsEEPROM.cpp b/src/FlashAsEEPROM.cpp index 72c6698..af4c30d 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-2021 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,84 @@ #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 not 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 not 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 not 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 bag to non-dirty } - _initialized = true; } -bool EEPROMClass::isValid() -{ - if (!_initialized) init(); - return _eeprom.valid; +void EEPROMClass::setStorageForceValid(FlashClass *ptr) { + 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 not 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..982d3d1 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-2021 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; }; From 8debc9eed251ce8e84e8f2970d9633a997d31858 Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Wed, 30 Sep 2020 13:46:23 +0200 Subject: [PATCH 2/7] fixed readme --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index fff9178..fbc5b63 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ code, but it can also be used to store user data. ## Supported hardware -Currently, only ATSAMD21 cpu is supported (and consequently every board based +Currently, ATSAMD21 and ATSAMD51 cpu are supported (and consequently every board based Currently, only ATSAMD21 cpu is supported (and consequently every board based on this cpu like the Arduino Zero or Aduino MKR1000). ## Limited number of writes @@ -82,7 +82,7 @@ sketch for an example on how to do it. ### The content of the FlashStorage is erased each time a new sketch is uploaded? -Yes, every time you upload a new sketch, the previous content of the FlashStorage is erased. +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? From ce54bf88dd1b5450b13c73debe072e9196ed1f77 Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Wed, 30 Sep 2020 13:47:15 +0200 Subject: [PATCH 3/7] fixed readme --- readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readme.md b/readme.md index fbc5b63..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 Currently, only ATSAMD21 cpu is 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 From f160e7411016e0ba619dc29eb570757555a18aef Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Wed, 30 Sep 2020 19:51:06 +0200 Subject: [PATCH 4/7] fixed typo --- examples/EmulateEEPROM/EmulateEEPROM.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/EmulateEEPROM/EmulateEEPROM.ino b/examples/EmulateEEPROM/EmulateEEPROM.ino index 9b2f082..8c6ec7e 100644 --- a/examples/EmulateEEPROM/EmulateEEPROM.ino +++ b/examples/EmulateEEPROM/EmulateEEPROM.ino @@ -11,7 +11,7 @@ // Include EEPROM-like API for FlashStorage #include -// Create a area in flash memory for EEPROM with 1024 bytes +// 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 From 52c482a5d93d9db6014807103c24f14ae6cab523 Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Thu, 1 Oct 2020 07:49:31 +0200 Subject: [PATCH 5/7] fixed typo --- src/FlashAsEEPROM.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FlashAsEEPROM.cpp b/src/FlashAsEEPROM.cpp index af4c30d..d7c4262 100644 --- a/src/FlashAsEEPROM.cpp +++ b/src/FlashAsEEPROM.cpp @@ -29,7 +29,7 @@ EEPROMClass::EEPROMClass(void) : _initialized(false), _dirty(false) { } uint8_t EEPROMClass::read(int address) { - // if not storage has been set or address is out of range -> just do nothing + // 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; @@ -40,7 +40,7 @@ uint8_t EEPROMClass::read(int address) { void EEPROMClass::write(int address, uint8_t value) { update(address, value); } void EEPROMClass::update(int address, uint8_t value) { - // if not storage has been set or address is out of range -> just do nothing + // if no storage has been set or address is out of range -> just do nothing if (flash_class == NULL || address >= flash_class->length()) return; @@ -54,7 +54,7 @@ bool EEPROMClass::isValid() { return eeprom_valid.read() == TRUE; } void EEPROMClass::commit() { - // if not storage has been set, just do nothing + // if no storage has been set, just do nothing if (flash_class == NULL) return; @@ -70,7 +70,7 @@ void EEPROMClass::commit() { eeprom_valid.write((int)TRUE); } - _dirty = false; // toggle dirty bag to non-dirty + _dirty = false; // toggle dirty back to non-dirty } } @@ -83,7 +83,7 @@ void EEPROMClass::setStorageForceValid(FlashClass *ptr) { void EEPROMClass::init() { - // if not storage has been set, just do nothing + // if no storage has been set, just do nothing if (flash_class == NULL) return; From 14e9d89c8ab83af31c76cf7e099223f4527c3b94 Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Thu, 1 Oct 2020 07:51:02 +0200 Subject: [PATCH 6/7] fixed wrong year in file-header --- src/FlashAsEEPROM.cpp | 2 +- src/FlashAsEEPROM.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FlashAsEEPROM.cpp b/src/FlashAsEEPROM.cpp index d7c4262..3f89710 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-2021 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 diff --git a/src/FlashAsEEPROM.h b/src/FlashAsEEPROM.h index 982d3d1..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-2021 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 From 6f377b06c52e47f235a1a2ce51e7deb31c54a8d8 Mon Sep 17 00:00:00 2001 From: Alexander Christian Date: Thu, 1 Oct 2020 10:40:10 +0200 Subject: [PATCH 7/7] improved forcing valid flag. Only set if not already set. --- src/FlashAsEEPROM.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/FlashAsEEPROM.cpp b/src/FlashAsEEPROM.cpp index 3f89710..37fc170 100644 --- a/src/FlashAsEEPROM.cpp +++ b/src/FlashAsEEPROM.cpp @@ -75,7 +75,9 @@ void EEPROMClass::commit() { } void EEPROMClass::setStorageForceValid(FlashClass *ptr) { - eeprom_valid.write((int)TRUE); // force valid flag to true + if (!isValid()) { + eeprom_valid.write((int)TRUE); // force valid flag to true + } setStorage(ptr); // continue with normal setStorage() ... }