diff --git a/CHANGELOG.md b/CHANGELOG.md index db353f1c..a6c65d9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add `__AVR__` to defines when compiling - Add support for `diditalPinToPort()`, `digitalPinToBitMask()`, and `portOutputRegister()` +- 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 diff --git a/REFERENCE.md b/REFERENCE.md index 41925bdc..c231bba4 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -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); +} +``` diff --git a/SampleProjects/TestSomething/test/eeprom.cpp b/SampleProjects/TestSomething/test/eeprom.cpp new file mode 100644 index 00000000..8a844249 --- /dev/null +++ b/SampleProjects/TestSomething/test/eeprom.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +// Only run EEPROM tests if there is hardware support! +#if defined(EEPROM_SIZE) +#include + +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, 255); + a = EEPROM.read(0); + assertEqual(255, 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() diff --git a/cpp/arduino/EEPROM.h b/cpp/arduino/EEPROM.h new file mode 100644 index 00000000..05b3daa5 --- /dev/null +++ b/cpp/arduino/EEPROM.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include + +// 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 + +class EEPROMClass { +private: + GodmodeState* state; +public: + // constructor + EEPROMClass() { + state = GODMODE(); + } + // array subscript operator + uint8_t &operator[](const int index) { + assert(index < EEPROM_SIZE); + return state->eeprom[index]; + } + + uint8_t read(const int index) { + assert(index < EEPROM_SIZE); + return state->eeprom[index]; + } + + void write(const int index, const uint8_t value) { + assert(index < EEPROM_SIZE); + state->eeprom[index] = value; + } + + void update(const int index, const uint8_t value) { + assert(index < EEPROM_SIZE); + state->eeprom[index] = value; + } + + uint16_t length() { return EEPROM_SIZE; } + + // read any object + template T &get(const int index, T &object) { + uint8_t *ptr = (uint8_t *)&object; + for (int i = 0; i < sizeof(T); ++i) { + *ptr++ = read(index + i); + } + return object; + } + + // write any object + template const T &put(const int index, T &object) { + const uint8_t *ptr = (const uint8_t *)&object; + for (int i = 0; i < sizeof(T); ++i) { + write(index + i, *ptr++); + } + return object; + } +}; + +// global available in Godmode.cpp +extern EEPROMClass EEPROM; diff --git a/cpp/arduino/Godmode.cpp b/cpp/arduino/Godmode.cpp index 102afca6..f68867e4 100644 --- a/cpp/arduino/Godmode.cpp +++ b/cpp/arduino/Godmode.cpp @@ -113,3 +113,8 @@ SPIClass SPI = SPIClass(&GODMODE()->spi.dataIn, &GODMODE()->spi.dataOut); // defined in Wire.h TwoWire Wire = TwoWire(); + +#if defined(EEPROM_SIZE) + #include + EEPROMClass EEPROM; +#endif diff --git a/cpp/arduino/Godmode.h b/cpp/arduino/Godmode.h index de7a299e..d955e9d5 100644 --- a/cpp/arduino/Godmode.h +++ b/cpp/arduino/Godmode.h @@ -30,6 +30,17 @@ unsigned long micros(); #define NUM_SERIAL_PORTS 0 #endif +// different EEPROM implementations have different macros that leak out +#if !defined(EEPROM_SIZE) && defined(E2END) && (E2END) + // public value indicates that feature is available + #define EEPROM_SIZE (E2END + 1) + // local array size + #define _EEPROM_SIZE EEPROM_SIZE +#else + // feature is not available but we want to have the array so other code compiles + #define _EEPROM_SIZE (0) +#endif + class GodmodeState { private: struct PortDef { @@ -56,6 +67,7 @@ class GodmodeState { struct PortDef serialPort[NUM_SERIAL_PORTS]; struct InterruptDef interrupt[MOCK_PINS_COUNT]; // not sure how to get actual number struct PortDef spi; + uint8_t eeprom[_EEPROM_SIZE]; void resetPins() { for (int i = 0; i < MOCK_PINS_COUNT; ++i) { @@ -94,6 +106,12 @@ class GodmodeState { mmapPorts[i] = 1; } } + + void resetEEPROM() { + for(int i = 0; i < EEPROM_SIZE; ++i) { + eeprom[i] = 255; + } + } void reset() { resetClock(); @@ -102,6 +120,7 @@ class GodmodeState { resetPorts(); resetSPI(); resetMmapPorts(); + resetEEPROM(); seed = 1; }