[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

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

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

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.

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

Noob needs a little help here. I read Dean's tut and am still getting compile errors I don't understand.

// 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

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

Why have you got a function declaration in the middle of that? There's already a declaration for eeprom_read_byte in and it is:

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:

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.

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

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.

	// 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
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

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

Just a side note, but

Disp = Disp++;

is overkill.

Disp++;

will do.

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

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

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 ( https://www.avrfreaks.net/index.p... ) and Interrupts ( https://www.avrfreaks.net/index.p... ) tutorials.

- Dean :twisted:

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

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

There's an interrupts tutorial, too. Microcontroller datasheets have an overview of flash memory mgmt and interrupts, as does avr-libc.

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

abcminiuser wrote:

(...)

At the moment, we now have access to the eeprom memory, via the routines now provided by eeprom.h. There are three main types of EEPROM access: byte, word and block. Each type has both a write and a read variant, for obvious reasons. The names of the routines exposed by our new headers are:

Quote:

(...)
� void eeprom_read_block (void *pointer_ram, const void *pointer_eeprom, size_t n)
� void eeprom_write_block (const void *pointer_ram, void *pointer_eeprom, size_t n)

(...)

I think you've got the two first arguments to eeprom_write_block in the wrong order.

From eeprom.h:

Quote:

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

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

I do indeed - I'll fix that up. Thanks!

- Dean :twisted:

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

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

any information on improving the EEPROM endurance? using data looping data structures?

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

liuroot wrote:
any information on improving the EEPROM endurance? using data looping data structures?

To improve endurance, its better to work not on the EEPROM directly.
Work only on a copy inside the SRAM and write back only if needed and changed.
E.g. on a user key press or undervoltage detection.

See also:

https://www.avrfreaks.net/index.p...

Peter

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

Quote:

any information on improving the EEPROM endurance? using data looping data structures?

NEVER loop on data from the EEPROM directly; it's slow for readbacks, and writing to it continuously will quickly destroy it. EEPROM has a write cycle lifetime of about 100,000 writes - whether this applies to each individual cell or an internal page of cells has been a matter of debate. Always read in from EEPROM into a RAM variable, and only write back once you know the value has changed (test it beforehand with a second read) and you are sure you absolutely need to retain the new value.

- Dean :twisted:

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

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

liuroot, If I understand you correctly, and the above two posts not, then yes, datalooping is good. (Never heard of this term before just now).

If you need to store 10 bytes in a 1024 byte eeprom, you could program the data into the first unused section of 10 bytes. Once you reach the end, you erase everything, and start over. You need one from the 2^80th states of the 10 bytes to signal: "not used yet". Using 0xff ... 0xff would be best. You can write your 10 bytes of data to the eeprom 10 million times this way.

The cost is of course that you'll have to read up to 99% of your eeprom at startup to find the area that holds your data. Or you'll have to do even more complex tricks than this. Maybe programming 0xff -> 0xfe doesn't require an erase, thus you might be able to store a 0-8 counter in a single byte, with just one erase cycle. Tricky, not guaranteed to work by the datasheet etc etc.

Anyway. I'm posting to say thanks Dean... Had me up and running in 15 minutes. :-)

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

Thank you so much . I like your tutorial!!!!!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
eeprom_read_word((uint16_t*)address);

Does this read the 16 bit data from EEPROM?

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

Yes "word"=16 bits.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
address=0x0004;
eeprom_read_word((uint16_t*)address++);

after address++ what is the value of pointer? is it 0006?

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

That depends on how you defined address.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
address=0x0004; 
word1=eeprom_read_word((uint16_t*)address++); 
word2=eeprom_read_word((uint16_t*)address++);
....

word1=data of address 0x0004
and what should be word2?
word2=data of address 0x0006?

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

I got it.....
if
uint16_t address=0x0004 then address++ is 0x0006.
if uint8_t address=0x0004 then address++ is 0x0005

Thank you

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

Quote:

uint16_t address=0x0004 then address++ is 0x0006.
if uint8_t address=0x0004 then address++ is 0x0005

No. ++ applied to an integer ALWAYS increments it by one.

It's when you use ++ with a POINTER that ++ increases it by the size of the type that the pointer points at. But these EEPROM "addresses" are really integers not pointers - that's why there's a typecast converting their interpretation to "pointer to unsigned 16 bit integer" before the value is passed to the e_r_w() function. So the ++ in this case will always increment by one.

If you want the increments to be by the number of bytes of the data type then declare 'address' as a pointer in the first place in which case the typecast will not be required:

uint16_t * address = (uint16_t *) 0x0006;
word1 = eeprom_read_word(address++);

Now 'address' points at location 0x0008.

All this does kind of raise the question of what on earth are you doing with absolute addressing of EEPROM anyway? You have heard of "EEMEM" and the "linker" have you?

If I write:

char c EEMEM;
uint16_t data[10] EEMEM;
char cram;
uint16_t data_word;

cram = eeprom_read_byte(c);
data_word = eeprom_read_word(&data[3]);

then that reads a character from eeprom and also the fourth word of the data array in eeprom. You and I don't know (and have no reason to need to know) where the linker has chosen to locate 'c' and data[3]. It could be 0x0006 or it could be 0x003C - but who cares - like named variables in RAM the linker, not you, decides where they will be located.

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

Can I use the EEMEM same for writing the variables in EEPROM without giving the address where to write??

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

Yes. You can start with default values if you want too:

uint8_t data[4] EEMEM = { 4, 91, 217, 38 };

for (i=0; i<4; i++) {
  eeprom_write_byte(&data[i], i);
}

If you build this then as well as a .hex you'll get a project.eep which can be programmed into EEPROM using ISP and will set the locations set aside for data[0]..data[3] to 4, 91, 217 and 38.

After the for() loop has been run the four locations will now contain 0, 1, 2 and 3

Cliff

PS It probably helps if you actually read the article at the start of this thread - all this is explained in better detail than I can manage.

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

IS there any way I write a block of word instead of block of bytes.

following is the code to write a block of bytes.

Quote:

void eeprom_write_block (void *pointer_eeprom, const void *pointer_ram, size_t n)

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

A block is a block is a block. Only difference is that your words each are double the size of a byte, so I'd think that you'd

eeprom_write_block (pointer_to_where_words_should_end_up_in_eeprom, pointer_to_where_words_are_in_ram, number_of_words*2) 

It might be overkill, but with a few nice manouvres, including use of sizeof(), we could probably get rid of the literal value 2 above.

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

You just answered your own question. Which bit of eeprom_write_block() don't you understand? Maybe an example helps:

#include 
#include 

typedef struct {
  int16_t n;
  char c;
  uint8_t array[10];
  uint32_t l;
} data_type;

data_type copy_in_EEPROM EEMEM;
data_type copy_in_RAM;

int main(void) {
  copy_in_RAM.n = -23456;
  copy_in_RAM.c = 'A';
  copy_in_RAM.l = 0xDEADBEEF;
  memset(copy_in_RAM.array, 0x5A, sizeof(copy_in_RAM.array));
  eeprom_write_block(©_in_RAM, ©_in_EEPROM, sizeof(copy_in_RAM));
  while(1);
}

After that has run the EEPROM will contain those data values I set in the struct{}

(-23456 = 0xA460, 'A' = 0x41, rest is obvious)

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	uint8_t localBlock[80];
			uint16_t size;
			uint16_t address;
 
			size = (USARTReceive() << 8);
			size |= USARTReceive();

			address = (USARTReceive() << 8);
			address |= USARTReceive();

			int i = 0; 
	        for(i = 0; i < size && i < sizeof(localBlock); i++)
	        {             
	           localBlock[i] = USARTReceive();
	        }

			eeprom_write_block((const void*)localBlock,(void*)address,size);

Above code will write 8 bit datas

	uint16_t localBlock[80];
			uint16_t size;
			uint16_t address;
 
			size = (USARTReceive() << 8);
			size |= USARTReceive();

			address = (USARTReceive() << 8);
			address |= USARTReceive();

			int i = 0; 
	        for(i = 0; i < size && i < sizeof(localBlock); i++)
	        {             
	           localBlock[i] = USARTReceive();
	        }

			eeprom_write_block((const void*)localBlock,(void*)address,size);

And above code will read 16 bit datas

Am I correct?

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

Quote:

And above code will read 16 bit datas

I mean write 16 bit data

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

Not quite, the size field in e_w_b() is always BYTES so assuming the 'size' that is transmitted and stored into the 'size' variable isn't really the "size" but rather the "number of data values about to follow" then in the uint16_t case you need to double the value as each entry in the array occupies 2 bytes, not 1.

To avoid confusing yourself and, more importantly, the NEXT reader of your code I'd reconsider whether 'size' is a good name to use here. I think I'd choose something like 'num_elements' and on the e_w_b() line I'd do something like:

         eeprom_write_block((const void*)localBlock,(void*)address, num_elements * sizeof(localblock[0])); 

BTW, once again, by having the distant source transmit 'address' as the EEPROM location you are pre-empting the linker so it now becomes your responsibility to position everything in EEPROM and make sure there are no overlaps and so on.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 eeprom_write_block((const void*)localBlock,(void*)address, num_elements * sizeof(localblock[0]));

I did not understand why we are using localblock[0].
sizeof(localblock[0])means the size of the first element of array?

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

Quote:

sizeof(localblock[0])means the size of the first element of array?

Exactly. If the array is uint32_t[] then that sizeof() is 4, for uint16_t it is 2 and for uint8_t it is 1. So this code automatically adapts to whatever width the array is defined as.

element[0] is just as good as element[n] to test for size but all arrays have element[0] but not all necessarily go up to n which is why I used [0]

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

Great tutorial. Thanks.

In this example

Quote:

#include  

void main(void) 
{ 
    uint8_t StringOfData[10]; 

    eeprom_read_block((void*)&StringOfData, (const void*)12, 10); 
}


shouldn't it be either

eeprom_read_block((void*)StringOfData, (const void*)12, 10); 

or

eeprom_read_block((void*)&StringOfData[0], (const void*)12, 10); 

?

There is a special place in Hell reserved for engineers whose code works the first time.

Last Edited: Thu. Sep 9, 2010 - 02:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

but how do i specify the location in EEPROM where I want my variables to be stored?

You don't. Have you ever wanted your variables in RAM at a specific address? If not then why is it important in EEPROM? In both cases the LINKER is presented with everything to be stored there and it picks a sensible layout that does not waste bytes.

You can over-ride positioning with linker scripts or --section-starts but what's the point?

If you want to you can forget the linker and named variables altogether and just do something like:

#define my_int ((void *)0)   // int my_int
#define my long ((void *)2)  // long my_long
#define my_array ((void *)6) // uchar my_array[10]
#define my char ((void *)16) // char my_char

...

eeprom_write_byte(my_char, 'A');
eeprom_write_word(my_int, 12345);
eeprom_write_dword(my_long, 0x12345678);
strcpy(ram_array, "Hello");
eeprom_write_block(ram_array, my_array, 10);

char c = eeprom_read_byte(my_char);
int n = eeprom_read_word(my_int);
...
etc.

Now you are responsible for the EEPROM layout but you have to be very cautious of errors. Say you wrote:

#define my_int ((void *)0)   // int my_int
#define my long ((void *)1)  // long my_long
#define my_array ((void *)6) // uchar my_array[10]
#define my char ((void *)16) // char my_char

Looks very similar doesn't it? But a write to 'my_long' will over-write the second byte of the int. The linker is far better at laying things out that you or I.

Another idea if you need a fixed layout is:

typedef struct {
 int my_int;
 long my_long;
 uchar my_array[10];
 char my_char;
} ee_vars_type;

ee_vars_type ee_var EEMEM;

eeprom_write_byte(&ee_var.my_char, 'A');
etc.

The only "unknown" position in this is the actual base address of ee_vars in EEMEM but if it's the only EEMEM variable (holding all the variables you want) it's a fair bet it's at 0x0000.

You can now add variables and maintain the layout as long as they are always added to the end of the struct{}

Cliff

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

Apologies for the cross post. You guys respond way too fast for me to keep up. I posted the location question in the GCC forum, and deleted it from this thread in time it took Clawson write this small essay of an answer. I really should learn to type faster ... :oops:

Anyhat, I only intend to have one variable declared in EEPROM which will define the layout of the entire EEPROM. Can i be guaranteed that this single variable will always be at address 0x00?

The reason I'm hammering on this is because IAR compiler tend to start my EEPROM variables at address 0x01 (how silly is that?) unless i specifically tell it otherwise, e.g.

TEeprom __eeprom stEeprom @EEPROM_START_ADDRESS;

There is a special place in Hell reserved for engineers whose code works the first time.

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

Quote:

Anyhat, I only intend to have one variable declared in EEPROM which will define the layout of the entire EEPROM. Can i be guaranteed that this single variable will always be at address 0x00?

Use the stuct{} method I showed above. By inspection I think you'll find that the linker does base the only EEPROM var at 0x0000 but to force it you could use:

typedef struct { 
 int my_int; 
 long my_long; 
 uchar my_array[10]; 
 char my_char; 
} ee_vars_type; 

ee_vars_type ee_var __attribute__((section(".myeeprom"))); 

then in the LDFLAGS use:

--section-start=.myeeprom=0x810000

which will have the same effect as using EEMEM but guarantees that 'ee_var' is located at 0x0000 in the EEPROM.

Quote:

this is because IAR compiler tend to start my EEPROM variables at address 0x01 (how silly is that?)

That actually sounds VERY smart to me. It's well known (if AVRs are operated without BOD) for EEPROM location 0x0000 to be corrupted as the power rails rise/fall. It's often suggested to avoid using location 0 and it sounds like IAR are doing this automatically.

With this in mind you may want to use 0x810001 in the --section-start above.

Cliff

PS I locked the cross post in GCC to avoid accidents. If you like I can split this last part of this thread off as a separate thread with a name similar to the GCC thread and move it there but I think the foregoing IS on topic for this thread in fact.

PPS to answer the question you asked in GCC you can use --section-start on code, RAM and EEPROM. The only differences are that code addresses start at 0x000000, RAM at 0x800000 and EEPROM at 0x810000 but in all you use __attribute__((section(".yourname"))) and then later --section-start=.yourname=0x??????

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

Thanks. I'll follow your example code above.

I got what I was looking for with regard to EEPROM, but am still curious as to whether I can specify a location for variables in PROGMEM. So maybe we can move the rest of the discussion to a more suited thread.

scrap that, you just answered my question, thanks.

PPPS type slower dammit! :P

There is a special place in Hell reserved for engineers whose code works the first time.

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

I know it's slightly off topic but anything in code flash by it's very nature is not going to be "variable" but if you are talking about constant arrays I don't see why it matters to you where the linker chooses to place them though the --section-start with __attribute__section() will do the trick but this time you are in even more dangerous territory as some of what goes into code flash MUST be positioned by the linker so you don't want to risk any overlaps (putting fixed data at the very end of the flash is usually pretty safe). But, unless this is about replacing pages of data with SPM I don't see any point in trying to preempt the linker.

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

This isn't important or anything, but doesn't EEPROM stand for "Electronically Erasable Programmable Read-Only Memory"?

The tutorial omits the "Programmable" part when explaining it. Not a big deal, of course.

Thanks again for the wonderful information!

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

Hello, how to include eeprom address like this

float EEMEM ILG = 0.166;

to header file. I need it use in multiple functions in different C files.

If I define eeprom address like other variable, I get error like this:
menu.o:(.eeprom+0x0): multiple definition of `ILG'
main.o:(.eeprom+0x0): first defined here

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

The same rules applies as for all shared variables. You put the definition with the initial value in ONE of your .c files then in a shared .h file you put:

extern float EEMEM ILG;

There's a tutorial article about "managing large projects" here that explains this.

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

thanks my mistake, I tried define it with assigned value.

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

Thank you...............

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

Hello,

Nice tutorial. I just wanted to point out that casting an array or a pointer to a void * is completely unnecessary. Conversion from any data pointer to void * is implicit in both C and C++. Also taking an address of an array is not necessary either, array's name itself is a pointer to it's first element when used in a value context, which is what we want to pass to functions like eeprom_read_block.

So this:

abcminiuser wrote:

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

Can be rewritten like this:

eeprom_read_block(SRAMstring, NonVolatileString, 10)

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

Jason,

Good points. I'm actually aware of both of them (and was back when I wrote this!) but as they say, clarity is the key. While unnecessary, the added syntax makes it clear to the reader that the memory address of those variables are being passed to the function. If they substitute a uint64_t there instead of the character array, this way the code won't break with a cryptic error.

Cheers!
- Dean :twisted:

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

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

Hi Dean,

abcminiuser wrote:
Jason,

Good points. I'm actually aware of both of them (and was back when I wrote this!) but as they say, clarity is the key. While unnecessary, the added syntax makes it clear to the reader that the memory address of those variables are being passed to the function.


I'll have to disagree with you here, I think code is a lot clearer without casts which I consider unnecessary clutter in this case.

However aesthetics are not the only reason why I prefer the code I typed, it's actually more robust and maintainable code (read further).

abcminiuser wrote:
If they substitute a uint64_t there instead of the character array, this way the code won't break with a cryptic error.

Instead it will break at runtime. If you change the type of the variable from char array to uint64_t cast will make the code silently compile and cause buffer overflow because you would be reading 10 bytes into an 8 byte variable. Without a cast the code will fail to compile, giving you a chance to catch the bug and fix it.

Furthermore, the reason why I prefer string instead of &string is because it's more consistent and maintainable, and less error prone.

Consider what will happen if you pass &string and change the type of the variable from an array to a pointer. The code would again compile without errors and fail at runtime. I believe someone was actually bitten by this earlier in this thread.

By passing string instead code will work with both arrays and pointers.

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

hello,

i'm trying to write and read to/from eeprom but i have a few issues. i'm using a m64, avr studio4. 1st, when i compile i get these warnings but not sure how to correct them

Quote:

../eeprom.c:60: warning: pointer targets in passing argument 1 of '__eewr_word_m64' differ in signedness
../eeprom.c:61: warning: pointer targets in passing argument 1 of '__eewr_word_m64' differ in signedness
../eeprom.c:62: warning: passing argument 2 of '__eewr_block_m64' discards qualifiers from pointer target type
../eeprom.c:72: warning: pointer targets in passing argument 1 of '__eerd_word_m64' differ in signedness
../eeprom.c:73: warning: pointer targets in passing argument 1 of '__eerd_word_m64' differ in signedness

and then the result of my code is (sending it wirelessly to my laptop)

Quote:
234560

when it should be
Quote:

123456789

the code is shown below. THANK YOU!!

#define F_CPU 7372800UL									
#define BAUDRATE 19200		
#define UBRR_VALUE ((F_CPU/(BAUDRATE*16UL)) - 1)

#include 
#include 
#include 
#include  
#include 		
#include 
#include 
#include 
#include 
#include 					
#include 

#define true 1 
#define false 0 

unsigned int EEMEM eeEtot;
unsigned int EEMEM eeEs_month;
double EEMEM eeEw_month;

unsigned int Etot, Es_month;
double Ew_month; 

char Etot_str[5];
char Es_m_str[4];
char Ew_m_str[4];
char TxData[13]; 

void USART_init()
{	
	UBRR0L = UBRR_VALUE;				
	UBRR0H = (UBRR_VALUE >> 8);			
	UCSR0B |= (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);	
}

void USART_TX(unsigned int byt)				
{	
	while(!(UCSR0A & (1 << UDRE0))){}	
	UDR0 = byt;
}

int main(void)				
{
	USART_init();	
	int j;
	
	while (1)
	{
		Etot = 12;
		Es_month = 3456;
		Ew_month = 789;
		
		// WRITE
		cli();
		/*60*/eeprom_write_word(&eeEtot, Etot);
		/*61*/eeprom_write_word(&eeEs_month, Es_month);
		/*62*/eeprom_write_block((const void*)&Ew_month, (void*)&eeEw_month, sizeof(double));

		sei();

		Etot = 0;
		Es_month = 0;
		Ew_month = 0;
		_delay_ms(500);

		// READ
		cli();
		/*72*/Etot = eeprom_read_word(&eeEtot);
		/*73*/Es_month = eeprom_read_word(&eeEs_month);
		eeprom_read_block((void*)&Ew_month,(const void*)&eeEw_month, sizeof(double));
		sei();
		
		// Tx
		itoa((int)Etot,Etot_str,10);				
		itoa((int)Es_month,Es_m_str,10);			
		itoa((int)Ew_month,Ew_m_str,10);		
			
		for (j=0; j<13; j++)					
		{	TxData[j]=0;	}

		for(j=0; j<5; j++)				
		{	TxData[j] = Etot_str[j];	}	
		for(j=0; j<4; j++)				
		{	TxData[j + 5] = Es_m_str[j];	}
		for(j=0; j<4; j++)				
		{	TxData[j + 9] = Ew_m_str[j];	}

		for (j=0; j<13; j++)
		{	USART_TX(TxData[j]);	}

		_delay_ms(2000);
	}

return 0;
}

EDIT: code corrected and warnings eliminated

Last Edited: Thu. Feb 10, 2011 - 04:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This thread is not for diagnosing EEPROM usage problems. It is for discussing the article in the first post.

Moderator.

PS As for your warnings. Take for example:

/*60*/eeprom_write_word(&eeEtot, Etot);

where:

int EEMEM eeEtot; 
int Etot, Es_month; 

Now look at the prototype in eeprom.h:

void eeprom_write_word (uint16_t *__p, uint16_t __value);

uint16_t is "unsigned int". When you use just "int" then by default it is interpreted as "signed int".

THAT is why you get a signedness warning. Either cast or use the right type.

Also consider:

      /*62*/eeprom_write_block((void*)&eeEw_month,(const void*)&Ew_month, sizeof(double));

and the prototype in eeprom.h

void eeprom_write_block (const void *__src, void *__dst, size_t __n);

It's warning about param2 which is the "void * __dst" in the prototype and which you cast as "(const void*)". The warning is about the loss of the const qualifier.

Pages