Continuation of an old forum: https://forum.arduino.cc/index.php?topic=430778.00

@MartinL

Hello Martin,
In https://forum.arduino.cc/index.php?topic=430778.00 you described a very useful solution for Simon_C.
He had some issues writing to an External EEPROM and you provided a library that does that.
Though this topic already is very old, the outcome is still useful for me.

I ducked into the code in which you explained the steps your example code takes.
However, I still do have some questions.

Hi,

First add following includes at the beginning of your sketch:

#include <Wire.h>

#include <ExtEEPROM.h>
#include <EEPROMAnything2.h>




The EEPROM_writeAnything() and EEPROM_readAnything() functions have two parameters. The first argument is the address, this is an "int" type, while the second is the data, which can be cast (or in other words converted) to any data type.

The address references the 24LC256's 16-bit address, starting at 0, whose location contains a single byte value. The data itself however, be it simple data type (byte, int, long, etc..) or data structure, is stored within the EEPROM at consecutive addresses as byte values.

So for example to store a floating point number at address 0:



EEPROM_writeAnything(0, (float)1.24567);



...and to read it back:


float y;
EEPROM_readAnything(0, y);



As a float takes 4 bytes and each location in the 24LC256 is only one byte long, to store the next piece of data we need to use address: 4 and so on.

This leads on to how to address data withing the EEPROM itself. It can be useful to store your EEPROM data in a large structure. Using EEPROM_Anything functions makes it possible either to transfer large data structures between EEPROM and the Due's RAM in one line of code, or to access individual pieces of data within the structure itself.

The reason for using the unsigned long (aka uint32_t) casts in the example code, is that it's useful for storing and retrieving data from a structure. What's happening is that we're taking the base address of the structure in the Due's (RAM) memory, hence 32-bits, then subtracting the address of the data (again in the Due's RAM) we want from within the structure. This gives the offset address of the data relative to the start of the structure. So, if we store this same data structure in the EEPROM at address 0, the members of that structure are offset relative to this 0 address.

The example code below demonstrates how this works. Just hook up your Due to the your 24LC256 EEPROM, connect the Due to the programming port and try the code. You'll see that it's possible to store the TestStruct to EEPROM then retrieve or change its data members individually:



#include <Wire.h>
#include <ExtEEPROM.h>
#include <EEPROMAnything2.h>

struct TestStruct   // Create a test structure
{
  int32_t x;
  float y;
  uint8_t z;
} testStruct = { 5, 1.23483748, 0x08 };

void setup()
{
  Serial.begin(115200);                   // Initialise the Serial port
  Wire.begin();                           // Initialise the I2C library
  Serial.println(“Begin Test…”);

EEPROM_writeAnything(0, testStruct);    // Write the test structure to EEPROM
 
  int32_t valueX;
  float valueY;
  uint8_t valueZ;
 
  EEPROM_readAnything((uint32_t)&testStruct.y - (uint32_t)&testStruct, valueY);   // Read the EEPROM values individually
  EEPROM_readAnything((uint32_t)&testStruct.z - (uint32_t)&testStruct, valueZ);
  EEPROM_readAnything((uint32_t)&testStruct.x - (uint32_t)&testStruct, valueX);
  Serial.println(valueX);                                                         // Display the values
  Serial.println(valueY, 6);
  Serial.println(valueZ);
  EEPROM_writeAnything((uint32_t)&testStruct.y - (uint32_t)&testStruct, (float)3.35343);  // Change the float value
  EEPROM_readAnything((uint32_t)&testStruct.y - (uint32_t)&testStruct, valueY);   // Read the EEPROM values individually
  EEPROM_readAnything((uint32_t)&testStruct.z - (uint32_t)&testStruct, valueZ);
  EEPROM_readAnything((uint32_t)&testStruct.x - (uint32_t)&testStruct, valueX);
  Serial.println(valueX);                                                         // Display the values again
  Serial.println(valueY, 6);
  Serial.println(valueZ);
  EEPROM_readAnything(0, testStruct);     // Read from EEPROM back to the test structure
  Serial.println(testStruct.x);           // Display the test structure
  Serial.println(testStruct.y, 6);
  Serial.println(testStruct.z);
  Serial.println(“End Test…”);
}

void loop() {}

In the code:

EEPROM_writeAnything((uint32_t)&testStruct - (uint32_t)&testStruct, (float)3.35343);  // Change the float value

the line of data contains first the address data in which the second part, the data to be stored.
And If I understand it correctly, by subtracting the two uint32-t values, the outcome is the change of address, right?

But then:

struct TestStruct   // Create a test structure
{
  int x;
  float y;
  uint8_t z;
} testStruct = { 5, 1.23483748, 0x08 };

This “Struct” is created… Now I do not know what that is and I could not find anything on the reference page on the Arduino.CC site, so I guess it is something from within the library?
Again, guessing, The struct is a line of data containing the int, float and uint8_t, along with of course the address data.
Now I am not intending to use more than “just” one float, so I guess there is no need for the struct?
But I can imagine the address is then left out in the line of data, so the code does not know where to address it…

Therefore… Is there a way to just use the EEPROM_writeanything and EEPROM_readanything code without using the struct? And If so, how would I write the address (which changes with every write-cycle of course) along with the variable being stored?

To approach from another angle:
If I’m only using one float to be stored, I still can use the struct of course… But how can I read/find the specific address used for every cycle?
I am intending to write a float for every x time and I want to recall that data and even look for a specific stored float. How do I recall that? I mean… Is there a table of addresses? And (here again) how can I give a command to specificly target one address to retrieve the contained data?

You can be proud a library like yours is still courant and wanted!
I cannot express my joy in finding a solution for a struggle for so long. Not as if there are no other ways, but common solutions are hard to understand and absorb. :wink:

Regards,
FTMZ

Hi FTMZ,

When you declare a float variable in you sketch, the compiler reserves 4-bytes at an arbitary location or address in the microcontroller's RAM memory. The exact location of the variable is decided by the compiler and we don't necessarily have to know or care where it is stored in the RAM itself.

float variables take up 4-bytes, in order to store the sign (1-bit), exponent (8-bits) and mantissa (23-bits), necessary to represent a single precision floating point number: Single-precision floating-point format - Wikipedia.

The 24LC256 EEPROM stores 256KBits of data, or in other words 32KBytes (256 / 8-bits). To locate the data, each of these 32,768 (32 * 1024) one-byte locations has addresses from 0 to 32,767.

Unlike variables in RAM, the EEPROMAnything2 library requires that the programmer chose and control the address at which the data is stored in the EEPROM. Therefore to store a floating point number at address location 0 in the EEPROM we write:

EEPROM_writeAnything(0, (float)1.234);

However, to store the second float it's necessary to jump 4-bytes to address location 4, since a float takes up 4-bytes:

EEPROM_writeAnything(4, (float)4.321);

To retrieve this second floating point number to a variable called "value":

EEPROM_readAnything((4, value);

As you can see, this requires the programmer to keep track of where each variable is stored on the EEPROM itself. This is where a structure or struct can come in handy.

Arduino's programming language is actually a subset of the "C" programming language. C defines a set of fixed data types such as char, byte, int, long, float and double. In addition however, it's possible to group together these data types into more complex data structures, using a C construct called a struct.

In the EEPROMAnything2 example, the structure is simply grouping together variables of various data types:

struct TestStruct   // Create a test structure
{
  int32_t x;
  float y;
  uint8_t z;
} testStruct = { 5, 1.23483748, 0x08 };

The code creates a new type called "TestStruct" containing a long (int32_t), float and byte (uint8_t) and declares a new variable called "testStruct" (of type "TestStruct"). It also initialises the structure's variables with 5, 1.2345678 and 0x08 respectively.

The compiler reserves the struct in the microcontroller's RAM memory. Again we're not really interested in where. The address of the "testStuct" and the it's first int32_t variable, testStruct.x are the same and are what is know as the base address of the structure. The addresses of other variables testStruct.y (float) and testStruct.z (uint8_t) are offset from this base address. Therefore testStruct.y is at base address + 4 bytes (as int32_t takes 4-bytes like a float) and testStruct.z is at base address + 8 bytes.

What the struct allows us to do, is to conveniently store all the values under one heading and get EEPROMAnything2 library to store or retrieve the whole data structure in one go, using a single line of code. Furthermore, it now relieves the programmer of having to know where the variables are stored on the EEPROM, so long as we know the location of the structure's base address.

For example, if we would like to store the whole testStruct at address 4 in the EEPROM, we could write:

EEPROM_writeAnything(4, testStruct);    // Write the test structure to EEPROM at address 4

In order to read the floating point variable testStruct.y from the EEPROM, it's necessary to:

  • Specify the structure's offset in the EEPROM memory, in this case 4.
  • Add to this the address location of the testStruct.y variable in RAM.
  • Subtract the adress location of the structure's base address in RAM.
    Note that we don't care or need to know the explicit location of the structure in the microcontroller's RAM, as we're simply subtracting the location of the variable testStruct.y from the structure's base address. In other words, we're actually finding the address of testStruct.y variable relative to the structure's base address in the EEPROM.

Here the floating point number testStruct.y is read and stored in a float variable called valueY:

EEPROM_readAnything(4 + (uint32_t)&testStruct.y - (uint32_t)&testStruct, valueY);

In C, it's possible to find the address or location of a variable by adding the ampersand "&" prefix that reads "address of" or "location of".

In summary, it's possible to store and retrieve a floating point number from any address location within the 24LC256 EEPROM, each using a single line of code. A struct conveniently allows groups of data structures to be stored to EEPROM in one line of code. It also allows for the retrieval of a given variable within the structure, so long as we know the offset address of the structure with the EEPROM itself.

Thank you very much, Martin.
Not all is clear to me right now, but I think I can manage.
Don't want to look stupid here, but that struct part..., is that actually a function? :confused:

And... the Sentence "The address of the "testStuct" and the it's first int32_t variable, ..." has an error which blocks my understanding of it. Could you clarify it somewhat?

I'll work my way forth for now!

Regards!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.