[TUT] [C] Using the EEPROM memory in AVR-GCC

Go To Last Post
356 posts / 0 new

Pages

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

Can anyone help me out with a multidimensional array. I have the following array:

unsigned int EEMEM eaiConfig[19][2] =	{{15,30},{10,20},{3,10},{5,10},{8,15},
											{10,20},{10,20},{5,10},{8,15},{3,10},
											{10,20},{3,10},{3,10},{3,10},{3,10},
											{10,20},{10,20},{10,20},{3,10}};
															//minimum run,max run.

I would like to read elements out individually. I know I can't do:

a = eaiConfig[x][y]; 

...so would it be something like:

a = eeprom_read_word(????)

Thanks in advance

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
a = eeprom_read_word(&eaiConfig[x][y])

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

of course, thanks.

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

Hello,
I'm wondering if i can read data from the eeprom into a buffer, with a specific start position in the buffer?

Something like this: (not working)


char sram_buf[30];
unsigned char EEMEM _eeprom_string = {"test\n"};

eeprom_read_block(&sram_buf[10], (const void *)&_eeprom_string, 5);

What I'm trying to do is to read the word "test" into the sram_buffer, with a 10 bytes offset. Is it possible to do such things?

Thank you in advance,
/D

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

Sure, that'll work just fine. What isn't working for you? Remember that in C a zero byte is treated as a null-terminator -- if you have junk data in the first 10 bytes of the string, chances are one might be a terminator which will stop any display of the buffer contents before the contents of the EEPROM read string is reached.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
if you have junk data in the first 10 bytes of the string, chances are one might be a terminator which will stop any display of the buffer contents before the contents of the EEPROM read string is reached.

Aaaahh... Spot on! Thanks!

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

Hello guys, I have been programming AVRs using ASM for quite sometime already, and I am currently into learning C, and stumbling in loads of programming problems at that, especially regarding EEPROM.

I have declared/initialized my array like this:

uint8_t EEMEM memory[16][14];

And a snippet of my code is like this:

	u16 i, j;
	output[0] = target;
	for (i=1, j=0 ; i<=16 ; i++, j++)
	{
		output[i] = eeprom_read_byte(&memory[target][j]);
	} 
	output[18] = '\0';
	USART_printstream(output); // prints the whole array of string

Problem is, the program can't get over the

output[i] = eeprom_read_byte(&memory[target][j]); 

part. So it just hangs.

I'm kinda noob at C, so any ideas? TIA! :)

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

Hi,
While on this topic, if a programmer is designed to look for EEPROM and USERMEM just above the maximum flash code address for the device, only one hex file need be generated for code and data, instead of multiple files. In that case can GCC support placement of data outside the flash address area? What will be your comments on such a scheme?
With regards,
Laktronics

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

Your post should really be in the GCC forum but to do what you suggest would either need a different handling for each size of AVR or the data would have to be placed after the largest (256K) device but this then faces problems when 512K AVRs arrive. Also the programmer software would need to know about this split so all the various programmers (Ponyprog, avrdude, AVR Studio, etc, etc) would need to be updated to recognise where code ends and EEPROM data begins.

Anyway, what's the problem with using two files (which make a very definite split and can easily be handled by any programming software) ?

PS By the way if you just modify the rule that generates the .hex to not do the "-R .eeprom" then that data will be written into the .hex file alongside the code flash data (but at an address offset of 0x810000). The problem you'll face (because your programming software won't understand this) is that it will say "code image is too large for device" when you try to program it as it thinks you have code stretching out to 8MB+ (that offset picked so it would be bigger than the largest AVR might ever get)

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

Hello Clawson,
Thank you for your reply. Yes, you are right, I should post this question in the GCC Forum to hear their point of view.
I have posted in this forum to get a feedback from users on this scheme of initialising the EEPROM and USER Memory while programming. PIC ICs for example use such a scheme to embed EEPROM data within the main code so that users need not have to worry about handling two or more files (including USER MEM if available), in Assembly language. Also, Atmel's 89S8252 places its EEPROM address immediately after the Flash address only while programming. It helps much in documentation if number of files to be handled are few. Further, the device programming software need to open only one file during programming, thus saving some programming time.
As far as the device programming software is concerned, it is always aware of the chip specific memory map so that any data placed just above the flash address of the specific device in the hex file can be mapped on to the EEPROM/USER MEM area. So, it is not necessary to keep the data at the end of the flash address of the highest end AVR device.
Moving the EEPROM data to address just above the flash address in assembly can easily be done using ORG and DB/DW Directives.
Also, being a programmer specific architecture, there is no need to modify any of the existing programmer designs.
Thanking you once again for your valuable comments,
With regards,
Laktronics

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

Quote:

As far as the device programming software is concerned, it is always aware of the chip specific memory map so that any data placed just above the flash address of the specific device in the hex file can be mapped on to the EEPROM/USER MEM area.

That maybe true of PIC programming software - presumably because it was done like this from day one. I'm afraid your idea kind of missed the boat for AVR where there are just too many different programming programs in existence now for them all to be updated to recognise this code/EPPROM split thing. Else what happens when someone uses a toolchain that builds combined .hex files but tries to feed it to programming software that does not uderstand the split? If Intel .hex contained an embedded version number then programmers could reject files they don't "understand" but that's not the case.

Probably the best hope would be to develop/use programming software that could interpret the ELF format. Clearly all the binary data is in that file and the various sections could be pulled out and programmed appropriately. Given that the source of avrdude is open I guess it would be the best base for developing something like that.

Cliff

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

Hi,

firstly i would like to thank Dean for this tutorial. Though, for a beginner like me, there will always be tons of question. Here it goes;

1. Ali_dehbidi wrote :

Quote:
but i think the max endurance is 100,000 Write/Erase Cycles

I respectfully agree with Endurance is 100,000 Write/Erase Cycles, but doubtful with the 'max'. As i read from the Atmega48/88/168 User guide stating
Quote:
The EEPROM has an endurance of at least 100,000 write/erase cycles

2. i read earlier regarding the performance question. So if i want to have a high performance, does it mean that i would strive for low usage of RAM and possibly low fetch-time?

3. referring to the BOD, if i understand fully from what is discussed previously, does it means that i only need to set my BOD fuse bit with the threshold voltage desired? Then this will protect malfunction of my EEPROM, what is the min voltage value of the EEPROM before it goes crazy? :)

Regards,
Nicholas

Last Edited: Fri. Apr 24, 2009 - 01:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This an useful tutorial for me :D

Thanks a lot :)

ArcticSoul
Industrial Electronic Engineering, College Student

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

note if global interrupts are enabled; you must disable them "cli();" before performing a read; and enable them afterwards; otherwise the mcu will go into reset.

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

How would one go about reading and writing a floating point variable from/to EEPROM?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
float f;
eeprom_write_block(&f, e_adr, sizeof(f));

eeprom_read_block(&f, e_adr, sizeof(f));

(actually sizeof(f) is 4 as 32 bit IEEE754 floats are used)

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

hayashi_leo wrote:
note if global interrupts are enabled; you must disable them "cli();" before performing a read; and enable them afterwards; otherwise the mcu will go into reset.
Is this correct, or is this handled by the eeprom.h functions?

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

Let's say you have stored a function pointer, or a structure containing a function pointer in EEMEM. How do you pull it out and call the function?

struct mystruct{
char text;
int (*f_ptr)(int);
};
..
..
struct mystruct mydata[] EEMEM = {
{1, &f1},
{2, &f2}
}

int (*f2_ptr)(int) = NULL;
int result;
f2_ptr=(int*)eeprom_read_word(&mydata[0].f_ptr);

result = f2_ptr(332);

Should the (int*) be (unsigned int*).

I get various compiler errors on the same line:
warning: passing argument 1 of 'eeprom_read_word' from incompatible pointer type
warning: assignment from incompatible pointer type

Last Edited: Tue. May 26, 2009 - 03:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

split63,

Do yourself a favour and get into the habit of typedef'in things as it'll make your code simpler to read:

typedef int (*f_ptr_type)(int);
typedef struct {
 char * text;
 f_ptr_type f_ptr;
} mystruct_type;

Put that into a shared .h, then in a .c file:

mystruct_type mydata[] EEMEM = { 
  {1, &f1}, 
  {2, &f2} 
} 

f_ptr_type f2_ptr = NULL;
f2_ptr = (f_ptr_type) eeprom_read_word(&mydata[0].f_ptr); 

n = f2_ptr( m );

This keeps the "complexity" of the function pointer type definition in one place and replaces uses of it with the easily digestible 'f_ptr_type' (which you might have your own name for). As I suggested in another thread, splitting the declaration and the definition of mydata[] so it can be easily made extern is further helped by typedef'ing the struct {}

Cliff

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

from a much earlier post on page 1:

Quote:
double ReadDoubleFromEeprom(void){
double temp;
eeprom_read_block((void*)&temp, (const void*)&EEVar, sizeof(double));
return(temp);

Can someone explain why the (void*) cast is necessary and what it does in this case?

I found that in the case of characters, I have not needed this. But when I tried this for a float, I had to add the (void*).

char temp[10];
eeprom_read_block(&temp,&EEVar, 10);

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

Well start by looking at avr/eeprom.h where the prototype for eeprom_read_block is:

static __inline__ void
eeprom_read_block (void *__dst, const void *__src, size_t __n)

So it's destination pointer is given as void *. The reason for this is that the authors of eeprom_read_block() cannot possibly know what the given pointer might be pointing at. You could do:

char c;
int n;
long l;
struct {
 char * p;
 int array[10];
} s;

eeprom_read_block((void *)&c, &e_c, 1);
eeprom_read_block((void *)&n, &e_n, 2);
eeprom_read_block((void *)&l, &e_l, 4);
eeprom_read_block((void *)&s, &e_s, 12);

In the first the first parameter is a pointer to char, in the second a pointer to int, in the third a pointer to long and in the fourth a pointer to a 12 byte struct.

So when the type that a pointer variable is pointing at may not be known the C language allows for "void *" which kind of means "this could be pointing at anything".

The typecast is simply to tell the compiler to interpret the pointers as "void *" at the moment the eeprom_read_block() line is being compiled so they will match the defined paramater type given in the prototype.

Cliff

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

uint8_t  EEMEM NonVolatileChar; 
uint16_t EEMEM NonVolatileInt; 
uint8_t  EEMEM NonVolatileString[10]; 

int main(void) 
{ 
    uint8_t  SRAMchar; 
    uint16_t SRAMint; 
    uint8_t  SRAMstring[10];    

    SRAMchar = eeprom_read_byte(&NonVolatileChar); 
    SRAMint  = eeprom_read_word(&NonVolatileInt); 
    eeprom_read_block((void*)&SRAMstring, (const void*)&NonVolatileString, 10); 
} 

Hi, this is my first post here and I'm trying to use the EEPROM with GCC.

Shouldn't be the last line:

eeprom_read_block((void*)SRAMstring, (const void*)NonVolatileString, 10); 

or

eeprom_read_block((void*)&SRAMstring[0], (const void*)&NonVolatileString[0], 10);

since the arrays are already the addresses the '&' operators need to be avoided.

Please correct me if I am wrong.
Cheers

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

In the case of using the array name it doesn't matter if you use & or not.

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

Hello!

I write in the EEPROM :
char tex1[20] = "12.05 BedTi 130";

when i read it i want to use only the 130 value to comapre it with another value if (something) then display a message on lcd. If anyone can tell me how to read only the 130?
I use AVR studio 4 , GCC compiler.

Thank you in advance,

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

If the string will always be the same length, then just read characters 12 through 14. If it might change, read the entire string and just look at the last three characters. To get it from a string to a number (if this is what you want), use atoi().

Regards,
Steve A.

The Board helps those that help themselves.

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

plz give me some example code to write data in EEPROM using "void eeprom_write_word (uint16_t *addr, uint16_t value)" :oops: :oops:

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

uint16_t EEMEM EEint;

int main(void) {
 uint16_t i;
 i = PINC | (PINB << 8); // set it to something useful
 eeprom_write_word(&EEint, i);
}

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

Nice tutorial!

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

Hi, i think you made an error in your code...

-edit- wh00ps, &array_name is the same as &array_name[0], so it's okay, sorry :)

Last Edited: Wed. Aug 26, 2009 - 10:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Suggest you take a look at the generated code both with and without the '&' - notice anything?

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

What about negative numbers?

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

Just store retrieve as uchar/uint then cast the signed interpretation onto them

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

sorry for this but I am stuck.
Here is my code

uint16_t EEMEM Eapp = 200;

char s[4]="";
uint16_t temp = 0;
temp = eeprom_read_word(&Eapp);
itoa((signed int)temp,s,4);

s is always "3020"
What is going wrong with my code?

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

Well did you realise that 4 in your itoa() is asking for the conversion to be made in base 4? So 3020 means 0 units, 2 lots of 4^1, 0 lots of 4^2 and 3 lots of 4^3. IOW 8 + (3 * 64). Which, if I got my sums right is 200 in decimal. You may want to try ",10" rather than ",4" ;-)

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

Ok.
I am so embarrassed now!!!
Somehow I thought 4 was the size of s!
Thank you very much. I ate 6 hours trying to figure out what was going on!
Thanks again.

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

You are welcome to locate the thread "The Stupid Things We Do" in the OT Forum and contribute to it, just as we have. Everyone does Really Stupid Things [tm], so don't be too embarrassed. :D

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

hi all
i'm working with STK500 and Atmega128 microcontroller.
i'm newbie in C programming.Currently i try to make a program that when a character is receive from a microcontroller, it will write the received character into the EEPROM. Start from address 0 and increment this address location for the next character.Upon finishing writing 1 character into the EEPROM, i also want to read previous written character and transmit to the PC for verifications that the characters written to the EEPROM is correct.Below is my coding:

#include 
#include 
#include 
#define BUFFER_SIZE 10
#define	BAUD_57600_BPS	15
#define TRUE 1
#define FALSE 0
volatile uint8_t rxBuffer [BUFFER_SIZE];
volatile uint8_t readPtr;
volatile uint8_t writePtr;
volatile uint8_t txComplete;
uint8_t addressWrite EEMEM  = 0;
uint8_t addressRead EEMEM = 0;

//Inizialize USART '0'
void USART_Init (uint16_t data)
{
	/* Set baud rate */
	UBRR0H = (uint8_t)(data>>8);
	UBRR0L = (uint8_t)data;

	//Double Speed Operation
	UCSR0A = 1 << U2X;
   
    //Enable Transmitter and receiver;enable RX interrupt;enable TX interupt
	UCSR0B |= (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<< TXCIE); 
	
	//Set frame format 8bit
	UCSR0C |= (1<<UCSZ1)|(1<<UCSZ0);
}

void transmitData (uint8_t data)
{	
	UDR0 = data;
}

uint8_t bufferEmpty (void)
{
 	uint8_t emptyFlag;

	emptyFlag = FALSE;

	if(readPtr == writePtr)
	{ 	//buffer is empty
		emptyFlag = TRUE;
	}
	return(emptyFlag);		
}

void writeBuffer (uint8_t data)
{	
	rxBuffer [writePtr] = data;
	writePtr++;

	if (writePtr >= BUFFER_SIZE)
	{
		writePtr = 0;
	}
}

uint8_t readBuffer (void)
{
	uint8_t data;
	data = rxBuffer [readPtr];
	readPtr++;
	
	if (readPtr >= BUFFER_SIZE)
	{	
		readPtr = 0;
	}
	return (data);
}

uint8_t bufferFull (void)
{ 
	uint8_t fullFlag;
	uint8_t tempPtr;
	
	fullFlag = FALSE;
	tempPtr = writePtr;

	if (tempPtr++ >= BUFFER_SIZE)
	{	
		tempPtr = 0;
	}
	if (tempPtr == readPtr)
	{	
		fullFlag = TRUE;
	}

	return (fullFlag);
}

ISR(USART0_TX_vect)
{
	txComplete = TRUE;
} 
	
ISR(USART0_RX_vect)
{  
	if (bufferFull() == FALSE)
	{	
		writeBuffer (UDR0);
	}
}

int main(void)
{
	uint8_t readByte;

	// Initialize transmission complete flag.
	txComplete = TRUE;

	//initialize USART_Init
	USART_Init(BAUD_57600_BPS);
	
 	//Enable Global Interupt
	sei(); 

	// loop forever
	while(1)
	{   
		if (bufferEmpty() == FALSE)
		{	
		//write byte to a EEPROM;location to place the byte;read byte from buffer.
	  		eeprom_write_byte(&addressWrite,readBuffer());
			
         //increament location to next character
			addressWrite++;
			
			if (txComplete == TRUE)
			{	
	     //read previous written character		
            addressRead = addressWrite;
			     
				addressRead--;	

				txComplete = FALSE;
				
           //read byte from EEPROM
				readByte = eeprom_read_byte(&addressWrite);
				
            //transmit to PC what EEPROM read
				transmitData(readByte);
			}
		}
	}
	return 0;	
}    

When i try this,it give me unknown data.i hope anybody can help me with this.Sorry for my bad english

Rgrd
Dahlia

Last Edited: Mon. Dec 7, 2009 - 03:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dahlia!

1) Rather than adding yet another subject to this thread, you would have been better off starting your own thread, and
2) I suspect no-one is going to bother to look at your code the way it iss presented right now. All indentation is lost as you have just pasted it as normal text. Do this: i)Click the edit button for your message, ii)mark the source code, iii) click the "Code" button, and finally iv) click the submit button. Your code should now be shown with all textual structure (as indentations etc) intact. Now people might gete interested in looking at your code.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

ok.i will do that.thanks..

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

Hi everyone!

I'm a newbie here, sorry for the dumb question...

I use EEPROM in avr-gcc to store data like this:

uint8_t EEMEM ee_num;
uint8_t EEMEM ee_string[16];

uint8_t num = 8;
uint8_t* sting = "testing";
eeprom_write_byte((void*)&ee_num, num);
eeprom_write_block((const void*)&string, (void*)&ee_string, 7);

uint8_t* readstr;

eeprom_read_block((void*)&readstr, (const void*)&ee_string, 7);
rprintf("String: \"");
rprintfStr(readstr);
rprintf("\"");
rprintfCRLF();

rprintf("Number: ");
rprintfNum(10, 3, FALSE, ' ',eeprom_read_byte((const void*)&ee_num));

My problem is that numeric data, that I write into the EEPROM, like the 'num' variable above, remains in the eeprom after resetting the device, however strings doesn't.

I write a string into the EEPROM, I can read it back, but after resetting the device, it simply disappears, reading it results in an empty string "".

Could you give me any hints on this? Thanks.

Peter

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

You need to read/write one more byte to EEPROM than there are characters in the string -- there's a hidden 0x00 byte at the end of each string which terminates it. Without the terminator, your string routines will become confused.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Ah:

(const void*)&string

This is casting the address of the string pointer to a const void pointer, rather than reinterpreting the pointer's destination type. You are writing the pointer value itself (plus a bunch of other garbage beyond it) to the EEPROM and reading it back, rather than the actual string contents like you desire.

Change the casts to remove the address operator:

(const void*)string

In the read and write calls.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
Ah:

(const void*)&string

This is casting the address of the string pointer to a const void pointer, rather than reinterpreting the pointer's destination type. You are writing the pointer value itself (plus a bunch of other garbage beyond it) to the EEPROM and reading it back, rather than the actual string contents like you desire.

Change the casts to remove the address operator:

(const void*)string

In the read and write calls.

- Dean :twisted:

Thanks for your great help, i've figured out the above code works great with static strings , regardless of what I write at the length field. (I even get the whole string back, if i set length smaller than the length of the string!)

Here's the full code I'm working on:

char *parts[15];
char *last;
char *token;
char **item = parts;

token = strtok_r(cmdlineGetArgStr(1), " ", &last);
while (token) {
	*(item++) = token;
	token = strtok_r(NULL, " ", &last);
}
*item = NULL; 

uint8_t* string = "hello";

eeprom_write_byte((void*)&ee_rdr1_len, strlen(parts[1]));
rprintf("Setting reader1 name to: ");
rprintfStr(parts[1]);
eeprom_write_block((const void*)&parts[1], (void*)&ee_rdr1_name, strlen(parts[1]));

//eeprom_write_byte((void*)&ee_rdr1_len, strlen(string));
//rprintf("Setting reader1 name to: ");
//rprintfStr(string);
//eeprom_write_block((const void*)&string, (void*)&ee_rdr1_name, strlen(string));

This is a command line parser, I'm getting a parameter, tokenize it along the spaces, and I'd like to store the second token into EEPROM. The 4 lines at the bottom work great, I can read the string back after reboot. The 4 lines above that, doesn't. I can read it back perfectly, but after a reboot, the string disappears.

Maybe the problem is that the token parts[1] is not zero-terminated?

Peter

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

Quote:
(I even get the whole string back, if i set length smaller than the length of the string!)

That is because you are initializing the string at the start of the routine - that way, even if you read back zero bytes, you still end up with the correct original string. If you try to copy the string into a separate buffer, you'll see that it will fail miserably. The EEPROM routines won't automatically NULL terminate your read strings -- it's assumed that you take care of that.

Note that this method:

uint8_t* string = "hello";

And this method:

uint8_t string[] = "hello";

Have slightly different meanings, but the latter is usually better from a code perspective (mostly because other buffers you declare with fixed sizes retain the same syntax).

If the strings are not null-terminated, strlen() will return an invalid result and you'll get garbage back.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:

If the strings are not null-terminated, strlen() will return an invalid result and you'll get garbage back.

- Dean :twisted:

Well, then it's clearly not the case here, because the value of ee_rdr1_len is always correct.

I wonder what's the difference between "parts[1]" and "string". From the perspective of the eeprom_write_block function, they're just pointers, I think.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
(const void*)&parts[1]

You're doing it again - remove the "&" sign. In this case, you've got a pointer-to-a-pointer-to-a-char, or interpreted in a different way, an array of strings. When you use the array notation parts[1], you are dereferencing the double pointer once, leaving you with a pointer to the desired string. You need to pass that pointer to the EEPROM functions directly rather than passing it's address.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:

(const void*)&parts[1]

You're doing it again - remove the "&" sign. In this case, you've got a pointer-to-a-pointer-to-a-char, or interpreted in a different way, an array of strings. When you use the array notation parts[1], you are dereferencing the double pointer once, leaving you with a pointer to the desired string. You need to pass that pointer to the EEPROM functions directly rather than passing it's address.

- Dean :twisted:

If I remove the & sign, it stops working, this way I can't read it back at all. If & is there, I can at least read it back until reboot.

Peter

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

The more I read, the less it makes sense. Your parts variable is an array of pointers to char - but you're not actually allocating space for the strings. What you want is something like:

char parts[15][15];

Which will make parts an array of arrays. What you have now is causing the controller to store many bytes into an unallocated space, which is very bad.

Take this example:

char* Test1 = "AAA";
char* Test2;

strcpy(Test2, Test1, strlen(Test1));

Here we have Test2, a raw pointer to unallocated space, and so *any* writes to it's destination will cause data corruption.

A corrected sample would be:

char Test1[] = "AAA";
char Test2[10];

strcpy(Test2, Test1, strlen(Test1));

Which forces Test2 to have a stack allocation of 10 bytes, so we can safely copy strings of up to 9 bytes (the tenth byte is the null terminator) without issue.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
The more I read, the less it makes sense. Your parts variable is an array of pointers to char - but you're not actually allocating space for the strings. What you want is something like:

char parts[15][15];

Which will make parts an array of arrays. What you have now is causing the controller to store many bytes into an unallocated space, which is very bad.

Take this example:

char* Test1 = "AAA";
char* Test2;

strcpy(Test2, Test1, strlen(Test1));

Here we have Test2, a raw pointer to unallocated space, and so *any* writes to it's destination will cause data corruption.

A corrected sample would be:

char Test1[] = "AAA";
char Test2[10];

strcpy(Test2, Test1, strlen(Test1));

Which forces Test2 to have a stack allocation of 10 bytes, so we can safely copy strings of up to 9 bytes (the tenth byte is the null terminator) without issue.

- Dean :twisted:

Dean, thank very much! I've corrected my code, and everything works perfectly.

regards,
Peter

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

Excellent guide.
The freaks owes you a bundle!

Pages