| Author |
Message |
|
|
Posted: Jan 16, 2010 - 05:14 AM |
|

Joined: Jul 23, 2009
Posts: 17
|
|
Hi everyone!
I'm a newbie here, sorry for the dumb question...
I use EEPROM in avr-gcc to store data like this:
Code:
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 |
|
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 05:16 AM |
|


Joined: Jan 23, 2004
Posts: 9823
Location: Trondheim, Norway
|
|
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  |
_________________ Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 05:49 AM |
|


Joined: Jan 23, 2004
Posts: 9823
Location: Trondheim, Norway
|
|
Ah:
Code:
(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:
Code:
(const void*)string
In the read and write calls.
- Dean  |
_________________ Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 06:30 AM |
|

Joined: Jul 23, 2009
Posts: 17
|
|
|
abcminiuser wrote:
Ah:
Code:
(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:
Code:
(const void*)string
In the read and write calls.
- Dean
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:
Code:
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 |
|
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 06:36 AM |
|


Joined: Jan 23, 2004
Posts: 9823
Location: Trondheim, Norway
|
|
|
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:
Code:
uint8_t* string = "hello";
And this method:
Code:
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  |
_________________ Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 06:44 AM |
|

Joined: Jul 23, 2009
Posts: 17
|
|
|
abcminiuser wrote:
If the strings are not null-terminated, strlen() will return an invalid result and you'll get garbage back.
- Dean
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 |
|
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 07:03 AM |
|


Joined: Jan 23, 2004
Posts: 9823
Location: Trondheim, Norway
|
|
|
Code:
(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  |
_________________ Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 07:10 AM |
|

Joined: Jul 23, 2009
Posts: 17
|
|
|
abcminiuser wrote:
Code:
(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
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 |
|
|
| |
|
|
|
|
|
Posted: Jan 16, 2010 - 11:49 AM |
|


Joined: Jan 23, 2004
Posts: 9823
Location: Trondheim, Norway
|
|
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:
Code:
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:
Code:
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:
Code:
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  |
_________________ Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
|
| |
|
|
|
|
|
Posted: Jan 22, 2010 - 01:29 AM |
|

Joined: Jul 23, 2009
Posts: 17
|
|
|
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:
Code:
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:
Code:
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:
Code:
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
Dean, thank very much! I've corrected my code, and everything works perfectly.
regards,
Peter |
|
|
| |
|
|
|
|
|
Posted: Jan 29, 2010 - 05:28 PM |
|

Joined: May 06, 2009
Posts: 12
Location: Stockholm, Sweden
|
|
Excellent guide.
The freaks owes you a bundle! |
|
|
| |
|
|
|
|
|
Posted: Jan 29, 2010 - 08:29 PM |
|


Joined: Nov 17, 2004
Posts: 6137
Location: Great Smokey Mountains.
|
|
|
Internetwarrior wrote:
Excellent guide.
The freaks owes you a bundle!
Dean has a website:
http://www.fourwalledcubicle.com/
With a donate button for 'bundles' should you be interested.
Smiley |
_________________ FREE TUTORIAL: 'Quick Start Guide for Using the WinAVR C Compiler with ATMEL's AVR Butterfly' AVAILABLE AT: http://www.smileymicros.com
|
| |
|
|
|
|
|
Posted: Jan 31, 2010 - 06:16 PM |
|

Joined: May 06, 2009
Posts: 12
Location: Stockholm, Sweden
|
|
Just a note concerning the endurance of E/W cycles.
Each E/W only affects the lifetime of the particular cell/byte.
For instance, you can write to cell address 0 ~100k times, then write to cell address 1 ~100k times.
But just as been said before, this is just a matter of probability, where the expected lifespan is at least 100k E/W. Of course this differs and when using multiple cells for storing a variable(int or large variable) the variation in lifetime is also affected to the worse.
For instance, the probability of getting a "bad" cell is at least doubled and this "bad" cell alone decides the lifetime of the variable.
In short, a double (4 cells) is 4 time more likely to have a shorter lifespan than a uint8_t (1 cell).
This is just a simplification, the distribution is probably a Gaussian distribution in reality.
However, when using block writes the E/W cycles are less likely to fail due to lifetime issues compared to single cell writes of same size.
For instance, a struct written as a block containing 4 bytes are less likely to fail than 4 single byte writes. |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 04:45 PM |
|

Joined: Jan 18, 2010
Posts: 2
|
|
Noob needs a little help here. I read Dean's tut and am still getting compile errors I don't understand.
Code:
// Start code snippet
uint8_t Disp;
Disp = Disp++;
void eeprom_write_byte ((uint8_t*)Disp);
Disp = eeprom_read_byte(const Disp);
// end snippet
errors are:
warning: operation on 'Disp' may be undefined
error: expected declaration specifiers or '...' before '(' token
error: conflicting types for 'eeprom_write_byte'
note: an argument type that has a default promotion can't match an empty parameter name list declaration
c:/winavr-20080411/lib/gcc/../../avr/include/avr/eeprom.h:236: error: previous definition of 'eeprom_write_byte' was here |
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 04:53 PM |
|


Joined: Jul 18, 2005
Posts: 62266
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
Why have you got a function declaration in the middle of that? There's already a declaration for eeprom_read_byte in <avr/eeprom.h> and it is:
Code:
void eeprom_write_byte (uint8_t *__p, uint8_t __value);
then you are trying to change that with a malformed parameter type (don't cast in a declaration) and one parameter missing
Try simply something like:
Code:
uint8_t EEMEM my_var_in_EEPROM;
uint8_t Disp = 0;
while (Disp+1) {
Disp = Disp++;
eeprom_write_byte (&my_var_in_EEPROM, Disp);
Disp = eeprom_read_byte(&my_var_in_EEPROM);
}
That gives you a location in EEMEM (EEPROM) to hold values and keeps you "Disp" counting variable in RAM, writing it after each update to the EEPROM variable you defined then reading it back. |
_________________
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 06:46 PM |
|

Joined: Jan 18, 2010
Posts: 2
|
|
Thanks Clawson for the quick reply. I changed a few things as you recommended but probably need to go back and review tut on pointers. I'm still getting plenty of warnings and errors. Here's a bigger chunk to show you what my intentions are.
Code:
// check for startup mode and run or goto Calib *******************************
while(1) {
if ((PINC & (1<<PC4)) == 0) // read buttons
SelBut = ON;
if ((PINC & (1<<PC5)) == 0)
EntBut = ON;
if ((SelBut == ON) && (EntBut == ON)) {// Goto Calibration
Calib();
SelBut = OFF;
EntBut = OFF;
}
else if ((SelBut == ON) && (EntBut == OFF)) {// Choose Display Menu
uint8_t Disp = 0;
while (SelBut == ON) {
Menu(Disp);
if (EntBut == ON) {
eprom_write_byte (&eeprom_var,Disp);
}
Disp = Disp++;
}
SelBut = OFF;
EntBut = OFF;
}
else {// Do the work and return
Disp = eeprom_read_byte(&eeprom_var);
GetSens();
Menu(Disp);
}
}
return 0;
} // End Program
|
|
|
| |
|
|
|
|
|
Posted: Mar 11, 2010 - 09:27 PM |
|


Joined: Jul 18, 2005
Posts: 62266
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
I'm still getting plenty of warnings and errors.
That'd be the interesting thing to see and because each warning/error includes the source file line number some indication of which lines it's referring to would be good too |
_________________
|
| |
|
|
|
|
|
Posted: Mar 12, 2010 - 07:36 AM |
|


Joined: Mar 27, 2002
Posts: 18542
Location: Lund, Sweden
|
|
Just a side note, but
Code:
Disp = Disp++;
is overkill.
Code:
Disp++;
will do. |
|
|
| |
|
|
|
|
|
Posted: Apr 23, 2010 - 03:24 AM |
|

Joined: Mar 18, 2010
Posts: 52
|
|
HI abcminiuser
This is really a good stuff.I want similar directive for flash.
i also want to know the syntax of defining interrupts in avr-gcc. |
_________________ Thanks & Regards,
P.Raghuveer
Software developer
09553926573
Hyderabad
India
|
| |
|
|
|
|
|
Posted: Apr 23, 2010 - 03:32 AM |
|


Joined: Jan 23, 2004
Posts: 9823
Location: Trondheim, Norway
|
|
|
|
|
|
|