From 0a4b70742e692997142ff3064a9569fb75167473 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Sun, 13 Oct 2019 21:27:40 -0600 Subject: [PATCH] Re-write of EEPROM functions This uses the more common EEPROM methods currently built into Arduino. The approach is a lot more complex but handles any size variable and struct more gracefully than previous approach. I originally hit this problem when trying to record a struct. This new approach works flawlessly. Tested against examples and various attempts to break it. --- .../Example1_GetPut/Example1_GetPut.ino | 2 +- .../Example2_AllFunctions.ino | 31 ++- libraries/EEPROM/src/EEPROM.cpp | 175 +-------------- libraries/EEPROM/src/EEPROM.h | 207 ++++++++++++------ 4 files changed, 172 insertions(+), 243 deletions(-) diff --git a/libraries/EEPROM/examples/Example1_GetPut/Example1_GetPut.ino b/libraries/EEPROM/examples/Example1_GetPut/Example1_GetPut.ino index ea86af35..b7b07be6 100644 --- a/libraries/EEPROM/examples/Example1_GetPut/Example1_GetPut.ino +++ b/libraries/EEPROM/examples/Example1_GetPut/Example1_GetPut.ino @@ -25,7 +25,7 @@ void setup() { - Serial.begin(9600); + Serial.begin(115200); Serial.println("EEPROM Examples"); byte myValue1 = 200; diff --git a/libraries/EEPROM/examples/Example2_AllFunctions/Example2_AllFunctions.ino b/libraries/EEPROM/examples/Example2_AllFunctions/Example2_AllFunctions.ino index ce9f1536..89d868fb 100644 --- a/libraries/EEPROM/examples/Example2_AllFunctions/Example2_AllFunctions.ino +++ b/libraries/EEPROM/examples/Example2_AllFunctions/Example2_AllFunctions.ino @@ -23,7 +23,7 @@ void setup() { - Serial.begin(9600); + Serial.begin(115200); Serial.println("EEPROM Examples"); randomSeed(analogRead(A0)); @@ -106,8 +106,8 @@ void setup() uint32_t myValue8 = 241544; randomLocation = random(0, AP3_FLASH_EEPROM_SIZE); - EEPROM.update(randomLocation, myValue7); - EEPROM.update(randomLocation + 4, myValue8); + EEPROM.put(randomLocation, myValue7); + EEPROM.put(randomLocation + 4, myValue8); int32_t response7; uint32_t response8; @@ -124,8 +124,8 @@ void setup() float myValue10 = 5.22; randomLocation = random(0, AP3_FLASH_EEPROM_SIZE); - EEPROM.update(randomLocation, myValue9); - EEPROM.update(randomLocation + 4, myValue10); + EEPROM.put(randomLocation, myValue9); + EEPROM.put(randomLocation + 4, myValue10); float response9; float response10; @@ -145,8 +145,8 @@ void setup() double myValue12 = 384.95734987; randomLocation = random(0, AP3_FLASH_EEPROM_SIZE); - EEPROM.update(randomLocation, myValue11); - EEPROM.update(randomLocation + 8, myValue12); + EEPROM.put(randomLocation, myValue11); + EEPROM.put(randomLocation + 8, myValue12); double response11; double response12; @@ -156,7 +156,22 @@ void setup() Serial.printf("Location %d should be %lf: %lf\n", randomLocation + 8, myValue12, response12); //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - Serial.println(); + Serial.println(""); + Serial.println("String test"); + + //String write test + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + String myString = "How are you today?"; + randomLocation = random(0, AP3_FLASH_EEPROM_SIZE); + EEPROM.put(randomLocation, myString); + + String readMy; + EEPROM.get(randomLocation, readMy); + Serial.printf("Location %d string should read 'How are you today?': ", randomLocation); + Serial.println(readMy); + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + Serial.println(""); Serial.print("Flash Contents:"); for (uint16_t x = 0; x < 8 * 4; x += 4) { diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp index 863b2b89..863c8b31 100644 --- a/libraries/EEPROM/src/EEPROM.cpp +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -40,190 +40,37 @@ #include "EEPROM.h" #include "Arduino.h" -//Constructor -ap3_EEPROM::ap3_EEPROM() -{ -} - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //Write a byte to a given "EEPROM" location //Automatically masks user's byte into flash without //affecting other bytes in this flash word -void ap3_EEPROM::write(uint16_t eepromLocation, uint8_t dataToWrite) +void write(uint16_t eepromLocation, uint8_t dataToWrite) { uint32_t flashLocation = AP3_FLASH_EEPROM_START + eepromLocation; writeWordToFlash(flashLocation, (uint32_t)dataToWrite | 0xFFFFFF00); } //Read a byte from a given location in "EEPROM" -uint8_t ap3_EEPROM::read(uint16_t eepromLocation) +uint8_t read(uint16_t eepromLocation) { uint32_t flashLocation = AP3_FLASH_EEPROM_START + eepromLocation; return (*(uint8_t *)flashLocation); } -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -//Get method is overloaded with the following variable types -//char, byte, int, unsigned int, long, unsigned long, float, double? - -void ap3_EEPROM::get(uint16_t eepromLocation, uint8_t &dataToGet) -{ - dataToGet = *(uint8_t *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, uint16_t &dataToGet) -{ - dataToGet = *(uint16_t *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, int16_t &dataToGet) -{ - dataToGet = *(int16_t *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, int &dataToGet) -{ - dataToGet = *(int *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, unsigned int &dataToGet) -{ - dataToGet = *(unsigned int *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, int32_t &dataToGet) -{ - dataToGet = *(int32_t *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, uint32_t &dataToGet) -{ - dataToGet = *(uint32_t *)(AP3_FLASH_EEPROM_START + eepromLocation); -} -void ap3_EEPROM::get(uint16_t eepromLocation, float &dataToGet) -{ - union { - float f; - uint32_t b; - } temp; - temp.b = *(uint32_t *)(AP3_FLASH_EEPROM_START + eepromLocation); - - dataToGet = temp.f; -} - -void ap3_EEPROM::get(uint16_t eepromLocation, double &dataToGet) -{ - union { - double lf; - uint32_t b[2]; - } temp; - temp.b[1] = *(uint32_t *)(AP3_FLASH_EEPROM_START + eepromLocation); //LSB; - temp.b[0] = *(uint32_t *)(AP3_FLASH_EEPROM_START + eepromLocation + 4); //MSB; - dataToGet = temp.lf; -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -//Put method is overloaded with the following variable types -//char, byte, int, unsigned int, long, unsigned long, float, double? - -void ap3_EEPROM::put(uint16_t eepromLocation, uint8_t dataToWrite) -{ - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)dataToWrite | 0xFFFFFF00); -} -void ap3_EEPROM::put(uint16_t eepromLocation, uint16_t dataToWrite) -{ - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)dataToWrite | 0xFFFF0000); -} -void ap3_EEPROM::put(uint16_t eepromLocation, int16_t dataToWrite) -{ - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)dataToWrite | 0xFFFF0000); -} -void ap3_EEPROM::put(uint16_t eepromLocation, int dataToWrite) //ints are 32 bit on M4F -{ - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)dataToWrite); -} -void ap3_EEPROM::put(uint16_t eepromLocation, unsigned int dataToWrite) //ints are 32 bit on M4F +//Write a new byte to a given location in "EEROM" only if new data is there +void update(uint16_t eepromLocation, uint8_t dataToWrite) { - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)dataToWrite); -} -void ap3_EEPROM::put(uint16_t eepromLocation, int32_t dataToWrite) -{ - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (int32_t)dataToWrite); -} -void ap3_EEPROM::put(uint16_t eepromLocation, uint32_t dataToWrite) -{ - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)dataToWrite); -} -void ap3_EEPROM::put(uint16_t eepromLocation, float dataToWrite) -{ - union { - float f; - uint32_t b; - } temp; - temp.f = dataToWrite; - - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)temp.b); -} - -void ap3_EEPROM::put(uint16_t eepromLocation, double dataToWrite) //64 bits -{ - union { - double lf; - uint32_t b[2]; - } temp; - temp.lf = dataToWrite; - - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation), (uint32_t)temp.b[1]); //LSB - writeWordToFlash((AP3_FLASH_EEPROM_START + eepromLocation + 4), (uint32_t)temp.b[0]); //MSB -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -//The update functions simply call the put functions -//Put automatically checks to see if a spot needs updating -void ap3_EEPROM::update(uint16_t eepromLocation, uint8_t dataToWrite) -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, uint16_t dataToWrite) -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, int16_t dataToWrite) -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, int dataToWrite) //ints are 32 bit on M4F -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, unsigned int dataToWrite) //ints are 32 bit on M4F -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, int32_t dataToWrite) -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, uint32_t dataToWrite) -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, float dataToWrite) -{ - put(eepromLocation, dataToWrite); -} -void ap3_EEPROM::update(uint16_t eepromLocation, double dataToWrite) //64 bits -{ - put(eepromLocation, dataToWrite); + if (read(eepromLocation) != dataToWrite) + { + write(eepromLocation, dataToWrite); + } } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -uint16_t ap3_EEPROM::length() -{ - return (AP3_FLASH_EEPROM_SIZE); -} - //Erase 8k page encapsulating the EEPROM section -void ap3_EEPROM::erase() +void EEPROMClass::erase() { am_hal_flash_page_erase(AM_HAL_FLASH_PROGRAM_KEY, AM_HAL_FLASH_ADDR2INST(AP3_FLASH_EEPROM_START), @@ -240,7 +87,7 @@ void ap3_EEPROM::erase() //4) Write SRAM back into flash //5) Write user's data onto the spot with recently created 0xFFs //Note - this code assumes EEPROM temp space is contained in one page -void ap3_EEPROM::writeWordToFlash(uint32_t flashLocation, uint32_t dataToWrite) +void writeWordToFlash(uint32_t flashLocation, uint32_t dataToWrite) { //Error check if (flashLocation >= AP3_FLASH_EEPROM_START + AP3_FLASH_EEPROM_SIZE) @@ -306,4 +153,4 @@ void ap3_EEPROM::writeWordToFlash(uint32_t flashLocation, uint32_t dataToWrite) AP3_FLASH_EEPROM_SIZE); } -ap3_EEPROM EEPROM; \ No newline at end of file +//ap3_EEPROM EEPROM; \ No newline at end of file diff --git a/libraries/EEPROM/src/EEPROM.h b/libraries/EEPROM/src/EEPROM.h index deb359d6..9eff1ffa 100644 --- a/libraries/EEPROM/src/EEPROM.h +++ b/libraries/EEPROM/src/EEPROM.h @@ -8,8 +8,6 @@ https://www.sparkfun.com/products/15411 https://www.sparkfun.com/products/15412 - Written by Nathan Seidle @ SparkFun Electronics, June 16th, 2019 - Pseudo-EEPROM on the Cortex-M4F https://github.com/sparkfun/SparkFun_Apollo3 @@ -30,13 +28,26 @@ Development environment specifics: Arduino IDE 1.8.x - This program is distributed in the hope that it will be useful, + Original Copyright (c) 2006 David A. Mellis. All right reserved. + New version by Christopher Andrews 2015. + This copy has minor modificatons for use with Teensy, by Paul Stoffregen + This copy has minor modificatons for use with Artemis, by Nathan Seidle + + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + EERef class. + + This object references an EEPROM cell. + Its purpose is to mimic a typical byte of RAM, however its storage is the EEPROM. + This class has an overhead of two bytes, similar to storing a pointer to an EEPROM cell. - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ #ifndef _EEPROM_H @@ -58,81 +69,137 @@ Error : EEPROM start address must be divisble by 8192 //to 8096 if needed #define AP3_FLASH_EEPROM_SIZE 1024 - //class TwoWire : public Stream, public IOMaster{} + uint8_t + read(uint16_t eepromLocation); +void write(uint16_t eepromLocation, uint8_t dataToWrite); +void update(uint16_t eepromLocation, uint8_t dataToWrite); +void erase(); //Erase entire EEPROM +void writeWordToFlash(uint32_t flashLocation, uint32_t dataToWrite); - class ap3_EEPROM +struct EERef { -public: - ap3_EEPROM(); - - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - - //8 bit - uint8_t read(uint16_t eepromLocation); - void write(uint16_t eepromLocation, uint8_t dataToWrite); - void get(uint16_t eepromLocation, uint8_t &dataToGet); - - //16 bit - void get(uint16_t eepromLocation, uint16_t &dataToGet); - void get(uint16_t eepromLocation, int16_t &dataToGet); - - //32 bit - void get(uint16_t eepromLocation, int &dataToGet); - void get(uint16_t eepromLocation, unsigned int &dataToGet); - void get(uint16_t eepromLocation, int32_t &dataToGet); - void get(uint16_t eepromLocation, uint32_t &dataToGet); - void get(uint16_t eepromLocation, float &dataToGet); - - //64 bit - void get(uint16_t eepromLocation, double &dataToGet); - - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - - //8 bit - void put(uint16_t eepromLocation, uint8_t dataToWrite); + EERef(const int index) + : index(index) {} + + //Access/read members. + uint8_t operator*() const { return read(index); } + operator const uint8_t() const { return **this; } + + //Assignment/write members. + EERef &operator=(const EERef &ref) { return *this = *ref; } + EERef &operator=(uint8_t in) { return write(index, in), *this; } + EERef &operator+=(uint8_t in) { return *this = **this + in; } + EERef &operator-=(uint8_t in) { return *this = **this - in; } + EERef &operator*=(uint8_t in) { return *this = **this * in; } + EERef &operator/=(uint8_t in) { return *this = **this / in; } + EERef &operator^=(uint8_t in) { return *this = **this ^ in; } + EERef &operator%=(uint8_t in) { return *this = **this % in; } + EERef &operator&=(uint8_t in) { return *this = **this & in; } + EERef &operator|=(uint8_t in) { return *this = **this | in; } + EERef &operator<<=(uint8_t in) { return *this = **this << in; } + EERef &operator>>=(uint8_t in) { return *this = **this >> in; } + + EERef &update(uint8_t in) { return in != *this ? *this = in : *this; } + + /** Prefix increment/decrement **/ + EERef &operator++() { return *this += 1; } + EERef &operator--() { return *this -= 1; } + + /** Postfix increment/decrement **/ + uint8_t operator++(int) + { + uint8_t ret = **this; + return ++(*this), ret; + } + + uint8_t operator--(int) + { + uint8_t ret = **this; + return --(*this), ret; + } + + int index; //Index of current EEPROM cell. +}; - //16 bit - void put(uint16_t eepromLocation, uint16_t dataToWrite); - void put(uint16_t eepromLocation, int16_t dataToWrite); +/*** + EEPtr class. - // 32 bit - void put(uint16_t eepromLocation, int dataToWrite); - void put(uint16_t eepromLocation, unsigned int dataToWrite); - void put(uint16_t eepromLocation, int32_t dataToWrite); - void put(uint16_t eepromLocation, uint32_t dataToWrite); - void put(uint16_t eepromLocation, float dataToWrite); + This object is a bidirectional pointer to EEPROM cells represented by EERef objects. + Just like a normal pointer type, this can be dereferenced and repositioned using + increment/decrement operators. +***/ - //64 bit - void put(uint16_t eepromLocation, double dataToWrite); +struct EEPtr +{ - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + EEPtr(const int index) + : index(index) {} - //8 bit - void update(uint16_t eepromLocation, uint8_t dataToWrite); + operator const int() const { return index; } + EEPtr &operator=(int in) { return index = in, *this; } - //16 bit - void update(uint16_t eepromLocation, uint16_t dataToWrite); - void update(uint16_t eepromLocation, int16_t dataToWrite); + //Iterator functionality. + bool operator!=(const EEPtr &ptr) { return index != ptr.index; } + EERef operator*() { return index; } - // 32 bit - void update(uint16_t eepromLocation, int dataToWrite); - void update(uint16_t eepromLocation, unsigned int dataToWrite); - void update(uint16_t eepromLocation, int32_t dataToWrite); - void update(uint16_t eepromLocation, uint32_t dataToWrite); - void update(uint16_t eepromLocation, float dataToWrite); + /** Prefix & Postfix increment/decrement **/ + EEPtr &operator++() { return ++index, *this; } + EEPtr &operator--() { return --index, *this; } + EEPtr operator++(int) { return index++; } + EEPtr operator--(int) { return index--; } - //64 bit - void update(uint16_t eepromLocation, double dataToWrite); + int index; //Index of current EEPROM cell. +}; - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +/*** + EEPROMClass class. - uint16_t length(); - void erase(); //Erase entire EEPROM + This object represents the entire EEPROM space. + It wraps the functionality of EEPtr and EERef into a basic interface. + This class is also 100% backwards compatible with earlier Arduino core releases. +***/ -private: - void writeWordToFlash(uint32_t flashLocation, uint32_t dataToWrite); +struct EEPROMClass +{ + //Basic user access methods. + EERef operator[](const int idx) { return idx; } + uint8_t read(int idx) { return EERef(idx); } + void write(int idx, uint8_t val) { (EERef(idx)) = val; } + void update(int idx, uint8_t val) { EERef(idx).update(val); } + void erase(); + + //STL and C++11 iteration capability. + EEPtr begin() { return 0x00; } + EEPtr end() { return length(); } //Standards requires this to be the item after the last valid entry. The returned pointer is invalid. + uint16_t length() { return AP3_FLASH_EEPROM_SIZE + 1; } + + //Functionality to 'get' and 'put' objects to and from EEPROM. + template + T &get(int idx, T &t) + { + EEPtr e = idx; + uint8_t *ptr = (uint8_t *)&t; + for (int count = sizeof(T); count; --count, ++e) + *ptr++ = *e; + return t; + } + + template + const T &put(int idx, const T &t) + { + const uint8_t *ptr = (const uint8_t *)&t; + + //TODO - Write Artemis compatible function for block write + //#ifdef __arm__ + // eeprom_write_block(ptr, (void *)idx, sizeof(T)); + //#else + EEPtr e = idx; + for (int count = sizeof(T); count; --count, ++e) + (*e).update(*ptr++); + //#endif + return t; + } }; -extern ap3_EEPROM EEPROM; - -#endif +static EEPROMClass EEPROM __attribute__((unused)); +#endif //_EEPROM_H