Reading and Writing a struct to memory

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone,
I have been learning about the eeprom. I understand that writing a struct to eeprom memory is just case of writing each variable that is part of the struct. So my plan is to make a function called saveStruct (or something) and pass it the struct to save to eeprom.

Im having trouble though getting the variables written properly to eeprom. It compiles no problem, and at first glance I thought it had actually written the two data types, but then when I commented out the writes and just did the reads(it should have written to memory the time before) I get -1 for the number and a symbol that is hard to distingusish.

Have I missed something? Ive read around quite a lot so I might have got myself confused....

Also, there is a build option "Generate Hex file for EEPROM", do I need that set or something?

//#include "Menu/menuConfiguraton.h"
#include "main.hpp"
#include 
#include 
#include "string.h"
#include "LCD.hpp"
#include "KS0108.hpp"
#include "lunakey_glcd.h"
#include "Graphic.hpp"

#include "Eeprom.hpp"

#define nameLength 10

typedef struct {
	uint16_t number;
	char* name;
} mystruct_type;

mystruct_type EEMEM storeStruct;

int main(void) {
	LCD lcd;
	KS0108 ks0108;
	lcd.initialize(&ks0108);
	Graphic graphic(lcd);

	mystruct_type flashStruct;

//BLOCK TO WRITE THE INTEGER OF THE STRUCT:

//	flashStruct.number = 5;
//	eeprom_write_block((const void *) &flashStruct.number,
//			(void *) &storeStruct.number, sizeof(uint16_t));
//	flashStruct.number = 10;
	eeprom_read_block((void *) &flashStruct.number,
			(const void *) &storeStruct.number, sizeof(uint16_t));

//BLOCK TO WRITE THE STRING OF THE STRUCT:
//	flashStruct.name = "goodbye";
//	eeprom_write_block((const void *) &flashStruct.name,
//			(void *) &storeStruct.name, nameLength);
//	flashStruct.name = "hello";
	eeprom_read_block((void *) &flashStruct.name,
			(const void *) &storeStruct.name, nameLength);

	lcd.clearScreen();

	//write the string to lcd
	lcd.goTo(20, 30);
	lcd.writeString(flashStruct.name, 0);
	//write the number to lcd
	lcd.goTo(20, 40);
	lcd.writeInt((int) flashStruct.number, 0);

	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Also, there is a build option "Generate Hex file for EEPROM", do I need that set or something?
Yes you do, otherwise nothing will get sent to the EEPROM...at programming time.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why not just read or write the whole struct in one go ?

Of course you can do everything a member at a time. Your struct has only got two members so it won't make much difference. However, many structs have lots of members. You might even have an array of structs.

Note that you really don't want to have memory pointers as members of non-volatile storage. When you re-compile a new version of your program, the pointers will alter. It is easier to just use strings (i.e. char arrays).

Note that char arrays may be inefficient to store variable length strings. OTOH, it is normally acceptable to have a maximum length of string.

You can use pointers and strings in a heap. Only you know what your data is like.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh right, I had misunderstood then. Got it working I think storing Structs.

Do I need 2 structs for each thing stored then?
i.e
I need a struct to read the eeprom stored data back into, and I need a struct that is stored in the Eeprom - is that always only in eeprom - i.e will a struct

StructType EEMEM storedStruct;

use up flash memory?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A typical struct:

typedef struct {
    char name[20];
    int age;
    float height;
    char town[20];
    char postcode[8];
} person_t;

person_t david, alex;    // in SRAM
person_t EEMEM jeremy, dennis;   // in eeprom
person_t PROGMEM brian;  // in flash

    ...
    eeprom_write_block(&jeremy, &david, sizeof(person_t));
    ...
    person_t temp;
    pgm_read_block(&temp, &brian, sizeof(person_t));
    eeprom_write_block(&dennis, &temp, sizeof(person_t));

Untested. Note that each struct is 54 bytes. No great problem for flash but you won't get many in the eeprom (or SRAM).

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah I was wondering, because Ive got it all working with structs now, if I want to move the structs to progmem memory, how could I reference them by address and just store the address in the eeprom? I can make your person_t EEMEM jeremy etc a pointer and then that points to the struct stored in flash?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No. You go to bed, and have a good night's sleep.

Then think carefully about what you want to do.

Yes, &jeremy is simply an address in EEMEM space. This tells eeprom_write_block() where to start writing any data.

I suppose that you could store a 'flash address' if you wanted. OTOH, the flash is never going to change, so there is little point.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Right after a good nights sleep...
Imagine you had a menu listing of a load of your person_t structs - an address book for instance. Each struct was stored in flash memory.
I want to make a favourites menu - i.e most frequently used contacts - the user can click a button and the currently selected gets added to the favourites menu. Thats basically what my goal is.
So I have it storing the whole struct in eeprom now, but I want to just store the address of it in flash memory so that I am not storing the whole struct in eeprom.
Whats confusing me is the difference between your write block and mine:
Mine:

eeprom_write_block((const void *) &flashStruct,(void *) &storeStruct, sizeof(struct)); 

Yours:

eeprom_write_block(&jeremy, &david, sizeof(person_t));

Now regardless for a minute of the names of the variables, I am casting to [const] void* pointers. Because thats what I got off non-guru.com, We are however doing the same thing right - storing the whole struct in eeprom?

So to store a pointer to the place in flash would it be something like:

StructType PROGMEM inFlash;
StructType* structPointer;
structPointer = &inFlash;
structType* EEMEM inEeprom;

eeprom_write_block((const void *) structPointer,(void *) &inEeprom, sizeof(struct));

?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why wouldn't you use an array of structs then store the index numbers of the favourites in EEPROM? No need for the actual addresses so if the code is changed and the structs move the index numbers would still be valid (addresses wouldn't be). What's more if you don't offer more than 256 favourites then the indices can be stored as 8bits each (struct pointers would be 16 bit).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My posts were all untested. If you are calling an external function that specifies void*, you can pass any address as an argument.

If an argument is specified as a particular type, then you must use that type.

Likewise, you may have a typedef'd struct that specifies a data organisation. Note that pointers to EEMEM, PROGMEM or SRAM are all different.

If you have an array of person_t records in flash, you could store pointers in eeprom. OTOH, I would store the record numbers rather than pointers.

e.g. you have 200 records in flash, numbered 0-199. You just store your favourite record numbers in eeprom like 3, 65, 2, 99, ...

David.

Edit. I see that Cliff and I share the same idea!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thats also a very good point. I didnt think of that.

Can I make an array of structs stored in progmem and define them after, I mean:

struct myStructType {
int i;
int j;
};
int main(){
myStructType PROGMEM structArray[256];
structArray[0] = { 1, 2 };
//etc etc - define the rest of them

Then store 0 in eeprom if I want that one to be a favourite.

Usually when you put something in PROGMEM you define it then and there - here I am defining it after, is that alright?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anything in PROGMEM (unless you are willing to mess around with SPM and bootloaders) is const so can only be initialized when you build the code. As such:

typedef struct {
  int i;
  int j;
} mystruct_t;

const mystruct_t data[256] PROGMEM = {
  {37, 12345},
  {9923, 5421},
  {81, 6228},
  {93, 52334},
etc. 
} ;

Later show these to the user and if [5] is showing and they say "make favourite" then store 5 to the list in EEPROM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, I understand PROGMEM, I understand arrays, I understand structs. Im stuggling to merge the three theories together.

What I understand is you cant have an array of references.
I cant initialize a struct at the same time as defining it.

typedef struct {
	unsigned int itemValue;
	char name[15];
} MenuItem;

int main(){
	MenuItem PROGMEM item1;  //compiler warns me this is not being stored in progmem/flash
	strcpy(item1.name, "Item 1\0");
	item1.itemValue= 0b1000100100001011;

	MenuItem PROGMEM item2;
	strcpy(item2.name, "Item 2\0");
        item1.itemValue= 0b1000100111111011;


	MenuItem menuItems[2] PROGMEM= {item1,item2};
}

So currently the array menuItems is storing copies of each menu item from flash into an array stored in flash.
I cant make it an array of references so Im using twice the amount of memory as I should need.
If this were a heap, I could create the menuItems, copy them into the array and destory them again - but I cant do that here can I, at least that doesnt sound sensible (to use malloc and free).

As Cliff mentioned - the reason Im trying to do this is so that I can just store the array positions in eeprom to get a list of favourites.

Last question - and side point. If I have lots of menu items to define, they are structs so I cant initialize them globally - I have to do it within a function. Where do people usually do this kind of thing - static information liek this that will never change - do you have a file with a function that initializes all your static structs?

EDIT: Didnt see your latest post Cliff. I'll give that a go.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You cannot do the strcpy's you show above. The point about PROGMEM is that it can only be read not written (modern avr-gcc insists on the use of "const" which should help to remind you). Also everything in PROGMEM must be defined globally so you cannot define them locally as your code above shows. Finally you were defining item1 and item2 separately which suggests you haven't got the point about using an array. Your menuitems[2] to the conglomerate the individual objects is not the way to approach this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Right Ive got a much better understanding now of what would happen. I've changed my struct to the actualy struct I am going to use, its a list of piano chords.

The following compiles, however it continually resets the avr.
I suspect the issue revolves around either how I am storing the chords in the array - i.e I am storing a char array and how I am defining its value, or how I am reading back out of eeprom and mapping to the progmem stored array with memcpy_P.

BTW I notice people talk about pgm_read_block but this doesnt exist according to nongnu.or - hence why im using memcpy_P.

#include "main.hpp"
#include 
#include 
#include 
#include "string.h"
#include "LCD.hpp"
#include "KS0108.hpp"
#include "lunakey_glcd.h"
#include "Graphic.hpp"
#include "CircleOfFifths.hpp"
#include "Backlight.hpp"
#include "Eeprom.hpp"

typedef struct {
	unsigned int chord;
	char name[15];
} Chord;

const Chord Chords[5] PROGMEM = {
		{0b1000100100001011, "C Major"},
		{0b1000100100001011, "C# Major"},
		{0b0010001001001001, "D Major"},
		{0b0010000100101001, "D# Major"},
		{0b0000100010010111, "E Major"},
		};


uint8_t retrieveNumberChordFavourites();
void updateNumberChordFavourites();
void storeFavouriteChord(uint8_t favourite, uint8_t position);
uint8_t retrieveFavouriteChord(uint8_t position);

uint8_t EEMEM numberChordFavourites;
uint8_t EEMEM eFavouriteChord[5]; //can persist 5 favourite chords


int main(void) {
	LCD lcd;
	KS0108 ks0108;
	lcd.initialize(&ks0108);
	Graphic graphic(lcd);
	Eeprom eeprom;
	Backlight backlight(eeprom);
	backlight.On();
	lcd.bitmap(lunakey_glcd_bmp, 0, 0, LUNAKEY_GLCD_WIDTH, LUNAKEY_GLCD_HEIGHT);

	uint8_t positionChordAt = retrieveNumberChordFavourites(); //where to put the next favourite in the array
	storeFavouriteChord(1, positionChordAt);//store a favourite in that position

	lcd.clearScreen();
	lcd.goTo(0, 40);

	Chord tempChord;
	memcpy_P(&tempChord, &Chords[retrieveFavouriteChord(0)], sizeof(Chord));
	lcd.writeString((char*) tempChord.name, 0);

	while (1){
	}

	return 0;
}

//methods to store and retrieve favourite information from/to eeprom
uint8_t retrieveNumberChordFavourites(){
	return eeprom_read_byte(&numberChordFavourites);
}
void updateNumberChordFavourites(){
	eeprom_update_byte(&numberChordFavourites, retrieveNumberChordFavourites() + 1); //might have to be put in temp var first
}
void storeFavouriteChord(uint8_t favourite, uint8_t position) {
	//favourite is the index number of the chord in the array, position is where it should be in the array
	eeprom_write_block((const void *) &favourite,
			(void *) &eFavouriteChord[position], sizeof(uint8_t));

	updateNumberChordFavourites();//update the number of stored favourites
}

uint8_t retrieveFavouriteChord(uint8_t position) {
	uint8_t temp;
	//read a favourite back to be put in menu
	eeprom_read_block((void *) &temp,
			(const void *) &eFavouriteChord[position], sizeof(Chord));
	return temp;
}

I have a warning on the initialization of Chords[5], saying "only initialized variables can be placed into progmem area", this is how you did it tho Cliff isnt it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've got to go out to walk the Bengals but as far as I can see the initialization looks right so I guess there's an error in one of the headers having a knock on effect perhaps?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm I was hoping you were going to shout at me for being an idiot and point out the blatently obvious.

LCD is and has been working fine, I've stored and read back the backlight intensity no problems from eeprom, and I have drawn a graphic to the lcd fine.

The line that is causing the problem is:

memcpy_P(&tempChord, &Chords[retrieveFavouriteChord(0)], sizeof(Chord));

as if I comment it out it stops resetting.
Enjoy your walk. Weather's beautiful here, dont want to miss the little we get...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker wrote:

uint8_t temp;
   //read a favourite back to be put in menu
   eeprom_read_block((void *) &temp,
         (const void *) &eFavouriteChord[position], sizeof(Chord));

???

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Awesome. Thanks alot.

Just had to do a check to see whether positionChordAt == 0xFF (therefore not set) and if so set it to 0.
Job done.