Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
_Petya_
PostPosted: Jan 16, 2010 - 05:14 AM
Newbie


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
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Jan 16, 2010 - 05:16 AM
Moderator


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 Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
abcminiuser
PostPosted: Jan 16, 2010 - 05:49 AM
Moderator


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 Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
_Petya_
PostPosted: Jan 16, 2010 - 06:30 AM
Newbie


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 Twisted Evil


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
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Jan 16, 2010 - 06:36 AM
Moderator


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 Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
_Petya_
PostPosted: Jan 16, 2010 - 06:44 AM
Newbie


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 Twisted Evil


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
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Jan 16, 2010 - 07:03 AM
Moderator


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 Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
_Petya_
PostPosted: Jan 16, 2010 - 07:10 AM
Newbie


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 Twisted Evil


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
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Jan 16, 2010 - 11:49 AM
Moderator


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 Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
_Petya_
PostPosted: Jan 22, 2010 - 01:29 AM
Newbie


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 Twisted Evil



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

regards,
Peter
 
 View user's profile Send private message  
Reply with quote Back to top
Internetwarrior
PostPosted: Jan 29, 2010 - 05:28 PM
Newbie


Joined: May 06, 2009
Posts: 12
Location: Stockholm, Sweden

Excellent guide.
The freaks owes you a bundle!
 
 View user's profile Send private message  
Reply with quote Back to top
smileymicros
PostPosted: Jan 29, 2010 - 08:29 PM
Raving lunatic


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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Internetwarrior
PostPosted: Jan 31, 2010 - 06:16 PM
Newbie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
59zoocat
PostPosted: Mar 11, 2010 - 04:45 PM
Newbie


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
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Mar 11, 2010 - 04:53 PM
10k+ Postman


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.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
59zoocat
PostPosted: Mar 11, 2010 - 06:46 PM
Newbie


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
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Mar 11, 2010 - 09:27 PM
10k+ Postman


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

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Mar 12, 2010 - 07:36 AM
10k+ Postman


Joined: Mar 27, 2002
Posts: 18542
Location: Lund, Sweden

Just a side note, but
Code:
Disp = Disp++;

is overkill.

Code:
Disp++;

will do.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
raghu00832
PostPosted: Apr 23, 2010 - 03:24 AM
Wannabe


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
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
abcminiuser
PostPosted: Apr 23, 2010 - 03:32 AM
Moderator


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

Quote:

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.


Look at my PROGMEM ( http://www.avrfreaks.net/index.php?name ... mp;t=38003 ) and Interrupts ( http://www.avrfreaks.net/index.php?name ... mp;t=89843 ) tutorials.

- Dean Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits