Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions examples/EmulateEEPROM/EmulateEEPROM.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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 <FlashAsEEPROM.h>

// 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()) {

Expand Down
6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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).

Expand All @@ -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
Expand Down
94 changes: 63 additions & 31 deletions src/FlashAsEEPROM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
60 changes: 40 additions & 20 deletions src/FlashAsEEPROM.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,61 +24,81 @@

#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 {

public:
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
* <code>
* Flash(my_eeprom_storage, EEPROM_EMULATION_SIZE);
* </code>
* 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;
};

Expand Down