Skip to content

Add support for EEPROM #184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6b22fe6
Introduce EEPROM support (#166)
Oct 3, 2020
1888372
Merge branch 'master' into eeprom
Oct 5, 2020
1011dc4
Update CHANGELOG.md
Oct 5, 2020
3c437aa
Merge pull request #173 from jgfoster/eeprom
ianfixes Oct 5, 2020
c512f28
Only include EEPROM if board supports it.
Oct 5, 2020
d82c1cf
Merge branch 'master' into tdd
Oct 5, 2020
524d352
Add EEPROM_SIZE to mega boards.
Oct 6, 2020
8ed2732
Move `EEPROM_SIZE` macro to `misc/default.yml`.
Oct 6, 2020
fadc743
Use E2END to calculate EEPROM_SIZE.
Oct 6, 2020
0ee5170
EEPROM is tested by uno and due so we don't need to add mega2560 (one…
Oct 6, 2020
4605207
Simplify reference to EEPROM library.
Oct 7, 2020
3502211
Merge pull request #174 from jgfoster/eeprom
ianfixes Oct 9, 2020
74a35e0
Merge branch 'master' of https://github.com/Lizj96/arduino_ci into tdd
Oct 11, 2020
2ad0131
adding tests for EEPROM
Oct 11, 2020
5c26abd
test high memory and array read.
Oct 12, 2020
f923a7f
add setup to reset memory before each test
Oct 12, 2020
a0677ff
Merge pull request #178 from Arduino-CI/master
ianfixes Oct 12, 2020
9b5cd4c
Merge pull request #177 from Lizj96/tdd
ianfixes Oct 16, 2020
9c74e43
Add documentation of EEPROM (fix #190).
Oct 27, 2020
7784e37
Merge pull request #191 from jgfoster/tdd
ianfixes Oct 27, 2020
476dcda
Merge branch 'master' into tdd
ianfixes Nov 4, 2020
d7e7653
Rewrite EEPROM.h to avoid GNU-licensed code.
Nov 8, 2020
03e4168
Conditionalize inclusion of EEPROM.
Nov 9, 2020
a5dc7b6
Merge branch 'master' into tdd
ianfixes Nov 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- Add `__AVR__` to defines when compiling
- Support for mock EEPROM (but only if board supports it)

### Changed
- Move repository from https://github.com/ianfixes/arduino_ci to https://github.com/Arduino-CI/arduino_ci
Expand Down
37 changes: 37 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -581,3 +581,40 @@ unittest(spi) {
assertEqual("LMNOe", String(inBuf));
}
```

### EEPROM

`EEPROM` is a global with a simple API to read and write bytes to persistent memory (like a tiny hard disk) given an `int` location. Since the Arduino core already provides this as a global, and the core API is sufficient for basic testing (read/write), there is no direct tie to the `GODMODE` API. (If you need more, such as a log of intermediate values, enter a feature request.)

```C++
unittest(eeprom)
{
uint8_t a;
// size
assertEqual(EEPROM_SIZE, EEPROM.length());
// initial values
a = EEPROM.read(0);
assertEqual(255, a);
// write and read
EEPROM.write(0, 24);
a = EEPROM.read(0);
assertEqual(24, a);
// update
EEPROM.write(1, 14);
EEPROM.update(1, 22);
a = EEPROM.read(1);
assertEqual(22, a);
// put and get
const float f1 = 0.025f;
float f2 = 0.0f;
EEPROM.put(5, f1);
assertEqual(0.0f, f2);
EEPROM.get(5, f2);
assertEqual(0.025f, f2);
// array access
int val = 10;
EEPROM[2] = val;
a = EEPROM[2];
assertEqual(10, a);
}
```
78 changes: 78 additions & 0 deletions SampleProjects/TestSomething/test/eeprom.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include <ArduinoUnitTests.h>
#include <Arduino.h>

// Only run EEPROM tests if there is hardware support!
#if defined(EEPROM_SIZE) || (defined(E2END) && E2END)
#include <EEPROM.h>

GodmodeState* state = GODMODE();
unittest_setup()
{
state->reset();
}

unittest(length)
{
assertEqual(EEPROM_SIZE, EEPROM.length());
}

unittest(firstRead)
{
uint8_t a = EEPROM.read(0);
assertEqual(255, a);
}

unittest(writeRead)
{
EEPROM.write(0, 24);
uint8_t a = EEPROM.read(0);
assertEqual(24, a);

EEPROM.write(0, 128);
a = EEPROM.read(0);
assertEqual(128, a);

EEPROM.write(0, 256);
a = EEPROM.read(0);
assertEqual(0, a);

int addr = EEPROM_SIZE / 2;
EEPROM.write(addr, 63);
a = EEPROM.read(addr);
assertEqual(63, a);

addr = EEPROM_SIZE - 1;
EEPROM.write(addr, 188);
a = EEPROM.read(addr);
assertEqual(188, a);
}

unittest(updateWrite)
{
EEPROM.write(1, 14);
EEPROM.update(1, 22);
uint8_t a = EEPROM.read(1);
assertEqual(22, a);
}

unittest(putGet)
{
const float f1 = 0.025f;
float f2 = 0.0f;
EEPROM.put(5, f1);
assertEqual(0.0f, f2);
EEPROM.get(5, f2);
assertEqual(0.025f, f2);
}

unittest(array)
{
int val = 10;
EEPROM[2] = val;
uint8_t a = EEPROM[2];
assertEqual(10, a);
}

#endif

unittest_main()
166 changes: 166 additions & 0 deletions cpp/arduino/EEPROM.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
EEPROM.h - EEPROM library
Original Copyright (c) 2006 David A. Mellis. All right reserved.
New version by Christopher Andrews 2015.

Copy of https://github.com/arduino/ArduinoCore-megaavr/blob/c8a1dd996c783777ec46167cfd8ad3fd2e6df185/libraries/EEPROM/src/EEPROM.h
modified by James Foster in 2020 to work with Arduino CI.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably reconsider adding this directly since its license is inconsistent.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really out of my depth with regard to the terms of all the different licenses, and really can't offer advice one way or another. I have no idea whether your own rewrite counts as some "derivative" in the way the licenses are written.

I think as long as we're doing our best and intend to respond to any legal complaints -- fixing the license as appropriate -- (which I do intend to do) then I'm not sure what else it would take to move forward. In any case, if we keep the file let's keep the license and it can all get sorted out later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As owner of the project, you certainly have the last word on this. But (did you sense a "but" coming?), the difference between the GNU license and your Apache license is night and day (with slight exaggeration, think Catholics and Protestants in 16th, 17th, and 18th century Europe, or Antifa vs. Proud Boys today). While I can't promise that my replacement would withstand a legal challenge, I'm pretty confident that it would be better to leave out the GNU reference. Other than the file name and the published API, I made an effort to write the code from scratch and think that the risk is extremely small. By leaving in the GNU license, you risk attaching GNU to my code, and then you would need another rewrite.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw your rewrite after adding my comment, and I think your rewrite satisfies


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
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
*/

#ifndef EEPROM_h
#define EEPROM_h

#include <inttypes.h>
#include <avr/io.h>

// different EEPROM implementations have different macros that leak out
#if !defined(EEPROM_SIZE) && defined(E2END) && (E2END)
#define EEPROM_SIZE (E2END + 1)
#endif

// Does the current board have EEPROM?
#ifndef EEPROM_SIZE
// In lieu of an "EEPROM.h not found" error for unsupported boards
#error "EEPROM library not available for your board"
#endif

// On a real device this would be in hardware, but we have a mock board!
static uint8_t eeprom[EEPROM_SIZE];
inline uint8_t eeprom_read_byte( uint8_t* index ) { return eeprom[(unsigned long) index % EEPROM_SIZE]; }
inline void eeprom_write_byte( uint8_t* index, uint8_t value ) { eeprom[(unsigned long) index % EEPROM_SIZE] = value; }

// Everything following is from the original (referenced above)

/***
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.
***/

struct EERef{

EERef( const int index )
: index( index ) {}

//Access/read members.
uint8_t operator*() const { return eeprom_read_byte( (uint8_t*) index ); }
operator uint8_t() const { return **this; }

//Assignment/write members.
EERef &operator=( const EERef &ref ) { return *this = *ref; }
EERef &operator=( uint8_t in ) { return eeprom_write_byte( (uint8_t*) 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.
};

/***
EEPtr class.

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.
***/

struct EEPtr{

EEPtr( const int index )
: index( index ) {}

operator int() const { return index; }
EEPtr &operator=( int in ) { return index = in, *this; }

//Iterator functionality.
bool operator!=( const EEPtr &ptr ) { return index != ptr.index; }
EERef operator*() { return index; }

/** Prefix & Postfix increment/decrement **/
EEPtr& operator++() { return ++index, *this; }
EEPtr& operator--() { return --index, *this; }
EEPtr operator++ (int) { return index++; }
EEPtr operator-- (int) { return index--; }

int index; //Index of current EEPROM cell.
};

/***
EEPROMClass class.

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.
***/

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 ); }

//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 EEPROM_SIZE; }

//Functionality to 'get' and 'put' objects to and from EEPROM.
template< typename T > 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< typename T > const T &put( int idx, const T &t ){
EEPtr e = idx;
const uint8_t *ptr = (const uint8_t*) &t;
for( int count = sizeof(T) ; count ; --count, ++e ) (*e).update( *ptr++ );
return t;
}
};

static EEPROMClass EEPROM;
#endif
8 changes: 8 additions & 0 deletions cpp/arduino/Godmode.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <avr/io.h>
#include "WString.h"
#include "PinHistory.h"
#include "EEPROM.h"

// random
void randomSeed(unsigned long seed);
Expand Down Expand Up @@ -87,12 +88,19 @@ class GodmodeState {
spi.readDelayMicros = 0;
}

void resetEEPROM() {
for(int i = 0; i < EEPROM.length(); ++i){
EEPROM.update(i, 255);
}
}

void reset() {
resetClock();
resetPins();
resetInterrupts();
resetPorts();
resetSPI();
resetEEPROM();
seed = 1;
}

Expand Down