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

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

For an updated version of this tutorial in PDF format, please see this page of my website.

My second tutorial. This tutorial will centre around GCC's handling of data stored into EEPROM memory.

What is the EEPROM memory and why would I use it?

Most of the AVRs in Atmel's product line contain at least some internal EEPROM memory. EEPROM, short for Electronically Erasable Read-Only memory, is a form of non-volatile memory with a reasonably long lifespan. Because it is non-volatile, it will retain its information during periods of no AVR power and thus is a great place for storing sparingly changing data such as device parameters.

The AVR internal EEPROM memory has a limited lifespan of 100,000 writes - reads are unlimited.

How is is accessed?

The AVR's internal EEPROM is accessed via special registers inside the AVR, which control the address to be written to (EEPROM uses byte addressing), the data to be written (or the data which has been read) as well as the flags to instruct the EEPROM controller to perform a write or a read.

The C language does not have any standards mandating how memory other than a single flat model (SRAM in AVRs) is accessed or addressed. Because of this, just like storing data into program memory via your program, every compiler has a unique implementation based on what the author believed was the most logical system.

This tutorial will focus on the GCC compiler.

Using the AVRLibC EEPROM library routines:

The AVRLibC, included with WinAVR, contains prebuilt library routines for EEPROM access and manipulation. Before we can make use of those routines, we need to include the eeprom library header:

#include 

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:
uint8_t eeprom_read_byte (const uint8_t *addr)
void eeprom_write_byte (uint8_t *addr, uint8_t value)
uint16_t eeprom_read_word (const uint16_t *addr)
void eeprom_write_word (uint16_t *addr, uint16_t value)
void eeprom_read_block (void *pointer_ram, const void *pointer_eeprom, size_t n)
void eeprom_write_block (void *pointer_eeprom, const void *pointer_ram, size_t n)

In AVR-GCC, a word is two bytes while a block is an arbitrary number of bytes which you supply (think string buffers).

To start, lets try a simple example and try to read a single byte of EEPROM memory, let's say at location 46. Our code might look like:

#include 

void main(void)
{
    uint8_t ByteOfData;

    ByteOfData = eeprom_read_byte((uint8_t*)46);
}

This will read out location 46 of the EEPROM and put it into our new variable named "ByteOfData". How does it work? Firstly, we declare our byte variable, which I'm sure you're familiar with:

uint8_t ByteOfData;

Now, we then call our eeprom_read_byte routine, which expects a pointer to a byte in the EEPROM space. We're working with a constant and known address value of 46, so we add in the typecast to transform that number 46 into a pointer for the eeprom_read_byte function.

EEPROM words can be written and read in much the same way, except they require a pointer to an int:

#include 

void main(void)
{
    uint16_t WordOfData;

    WordOfData = eeprom_read_word((uint16_t*)46);
}

But what about larger datatypes, or strings? This is where the block commands come in.

EEPROM Block Access

The block commands of the AVRLibC eeprom.h header differ from the word and byte functions shown above. Unlike it's smaller cousins, the block commands are both routines which return nothing, and thus operate on a variable you supply internally. Let's look at the function declaration for the block reading command.

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

Wow! It looks hard, but in practise it's not. It may be using a concept you're not familiar with though, void-type pointers.

Normal pointers specify the size of the data type in their declaration (or typecast), for example "uint8_t*" is a pointer to an unsigned byte and "int16_t*" is a pointer to a signed int. But "void" is not a data type, so what does it mean?

Void pointers are useful when the exact type of the data being pointed to is not known by a function, or is unimportant. A Void is mearly a pointer to a memory location, with the data stored at that location being important but the type of data stored at that location not being important. Why is a void pointer used?

Using a void pointer means that the block read/write functions can deal with any datatype you like, since all the function does is copy data from one address space to another. The function doesn't care what data it copies, only that it copies the correct number of bytes.

Ok, that's enough of a derail - back to our function. The block commands expect a void pointer to a RAM location, a void pointer to an EEPROM location and a value specifying the number of bytes to be written or read from the buffers. In our first example let's try to read out 10 bytes of memory starting from EEPROM address 12 into a string.

#include 

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

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

Again, looks hard doesn't it! But it isn't; let's break down the arguments we're sending to the eeprom_read_block function.

(void*)&StringOfData

The first argument is the RAM pointer. The eeprom_read_block routine modifes our RAM buffer and so the pointer type it's expecting is a void pointer. Our buffer is of the type uint8_t, so we need to typecast its address to the necessary void type.

(const void*)12

Reading the EEPROM won't change it, so the second parameter is a pointer of the type const void. We're currently using a fixed constant as an address, so we need to typecast that constant to a pointer just like with the byte and word reading examples.

10

Obviously, this the number of bytes to be read. We're using a constant but a variable could be supplied instead.

The block write function is used in the same manner, except for the write routine the RAM pointer is of the type const void and the EEPROM is just a plain void pointer.

Using the EEMEM attribute

You should now know how to read and write to the EEPROM using fixed addresses. But that's not very practical! In a large application, maintaining all the fixed addresses is an absolute nightmare at best. But there's a solution - the EEMEM attribute.

Defined by the same eeprom.h header, the EEMEM attribute instructs the GCC compiler to assign your variable into the EEPROM address space, instead of the SRAM address space like a normal variable. For example, we could allocate addresses for several variables, like so:

#include 

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

Although the compiler allocates addresses to your EEMEM variables, it cannot directly access them. For example, the following code will NOT function as intended:

#include 

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

int main(void)
{
    uint8_t SRAMchar;

    SRAMchar = NonVolatileChar;
}

That code will instead assign the RAM variable "SRAMchar" to whatever is located in SRAM at the address of "NonVolatileChar", and so the result will be incorrect. Like variables stored in program memory (see tutorial here) we need to still use the eeprom read and write routines discussed above.

So lets try to fix our broken code. We're trying to read out a single byte of memory. Let's try to use the eeprom_read_byte routine.

#include 

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

int main(void)
{
    uint8_t SRAMchar;

    SRAMchar = eeprom_read_byte(&NonVolatileChar);
}

It works! Why don't we try to read out the other variables while we're at it. Our second variable is an int, so we need the eeprom_read_word routine:

#include 

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

int main(void)
{
    uint8_t  SRAMchar;
    uint16_t SRAMint;

    SRAMchar = eeprom_read_byte(&NonVolatileChar);
    SRAMint  = eeprom_read_word(&NonVolatileInt);
}

And our last one is a string, so we'll have to use the block read command:

#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);
}

Setting initial values

Finally, I'll discuss the issue of setting an initial value to your EEPROM variables.

Upon compilation of your program with the default makefile, GCC will output two Intel-HEX format files. One will be your .HEX which contains your program data (and which is loaded into your AVR's memory), and the other will be a .EEP file. The .EEP file contains the default EEPROM values, which you can load into your AVR via your programmer's EEPROM programming functions.

To set a default EEPROM value in GCC, simply assign a value to your EEMEM variable, like thus:

uint8_t EEMEM SomeVariable = 12;

Note that if the EEP file isn't loaded, your program will most likely run with the entire EEPROM being set to 0xFF, or at least unknown data. You should devise a way to ensure that no problems will arise via some sort of protection scheme (eg, loading in several values and checking against known start values).

For an updated version of this tutorial in PDF format, please see this page of my website.

- Dean :twisted:

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

Last Edited: Sat. Feb 4, 2012 - 02:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks again Dean! You've picked another great topic that I've been wanting to learn.

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

abcminiuser wrote:
My second tutorial. This tutorial will center around GCC's handling of data stored into EEPROM memory.

Do you plan to publish a pdf version of this fine tutorial?

Pop

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

pop48m wrote:
abcminiuser wrote:
My second tutorial. This tutorial will center around GCC's handling of data stored into EEPROM memory.

Do you plan to publish a pdf version of this fine tutorial?

Pop

Oh yes, I forgot! :oops: I've added a PDF version to the original post. Cheers!

- Dean :twisted:

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

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

I usually provide a default value in my code in case the eeprom wasn't loaded.
IE:

uint8_t setting;

setting = eeprom_read_byte(EEPROM_SETTING_OFFSET);
if (setting == 0xff) {
setting = DEFAULT;

This assumes that unprogrammed eeprom contains all ones.

I sometimes also check the value read against some range of legal
values just in case the eeprom contains an illegal value that would cause
a program malfunction, and if so use a defined default setting. You could
also write the default setting into the eeprom if the value contained was
unset or out of range, though this isn't usually necessary since the correct
value is now in sram.

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

Minor mistake, but I found that:

SRAMint = eeprom_read_byte(&NonVolatileInt);

should be:

SRAMint = eeprom_read_word(&NonVolatileInt);

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

Darn it! There's always something I manage to miss :P. Thanks Brian, I've updated my OP.

- Dean :twisted:

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

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

thanks Dean. that helped me heaps. ur great at explaing things. :)

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

Hi,

Not sure why, but as soon as I put this line in my code

#include  

AVR Studio locks up on me when I Build. I am using a STK500 with a Tiny13. Any ideas?

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

You need to install the latest service packs and plugin updates. You can get SP2 from the Atmel website (SP3 is currently in beta), which should include the GCC plugin update to prevent the freezes.

- Dean :twisted:

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

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

Thanks Dean, that fixed it. Excellent tutorial.

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

Dean,

***
EEPROM words can be written and read in much the same way, except they require a pointer to an int:

	   
#include  

void main(void) 
{ 
uint16_t WordOfData; 

WordOfData = eeprom_read_byte((uint16_t*)46); 
}	 

***
Shouldn't this be:
WordOfData = eeprom_read_word((uint16_t*)46);

I am trying to port AVR101 to AVR-GCC. I thought I'd use the eeprom.h functions as, from what I understand, they do not require interrupts to be disabled. Functions written in C (for some compliers), evidently can exceed "a 4 cycle limit" and you must disable interrupts for correct operation.

In , eeprom_read_byte and eeprom_write_byte use a u08 pointer for the address. Doesn't this mean you can only access 256 bytes in the EEPROM? What about the remaining 1024-256 = 968 EEPROM bytes in a ATmega32?

The functions in AVR101 originally used u16 pointers so I cast them to u08 pointers to get the code to compile without warnings, when using the functions.

I am aware of other forum messages saying that this AVR101 only works for values up to 255, which is OK by me . . . at this stage!

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

Davef,

Damnit, yet another typo. That should indeed be read_word - I'll go fix that now. Amazing that anyone still bothers to read my tutorials with all these silly mistakes!

Quote:
In , eeprom_read_byte and eeprom_write_byte use a u08 pointer for the address. Doesn't this mean you can only access 256 bytes in the EEPROM? What about the remaining 1024-256 = 968 EEPROM bytes in a ATmega32?

That tripped me up too at the start. In AVR-GCC, all pointers are an int in size, two bytes. The pointer typecast only specifies what type of data the pointer is pointing to, not the pointer's size. A (uint8_t*), (uint16_t*) and (uint32_t*) are all identical in size, they just point to increasingly larger datatypes.

For example, (uint8_t*)0x1234 is a pointer two bytes in size, pointing to a byte at the location 0x1234.

This is a problem when dealing with pointers to the AVR's flash memory, since in some AVRs this overflows an int. Because of this, pointers to flash addresses are still two bytes in size but the pointer is word rather than byte aligned, so an increase of one in the pointer's address corresponds to a jump of two bytes.

If I can think of enough material for this I might make it into a short tutorial since it seems to be such a prevalent issue.

- Dean :twisted:

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

Last Edited: Sun. Jul 9, 2006 - 12:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Many thanks for this excellent tutorial!

Cemo

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

abcminiuser wrote:
This is a problem when dealing with pointers to the AVR's flash memory, since in some AVRs this overflows an int. Because of this, pointers to flash addresses are still two bytes in size but the pointer is word rather than byte aligned, so an increase of one in the pointer's address corresponds to a jump of two bytes.

Almost. Pointers to functions in WinAVR point to word-aligned addresses. For one thing, this allows binary compatibility with the AVR architecture's IJMP instruction, which internally expects a word-aligned pointer.

It also coincidentally allows 16-bit function pointers to reach any address in any AVR up to and including the ATmega128/1280/1281.

But pointers to data in flash is still byte-aligned. This is quite necessary, otherwise storing data in flash would become an extremely wasteful proposition... a 10-character string would require 20 bytes of Flash storage, with the high-order of every Flash word being wasted.

As a result, the decision has been made that all PROGMEM data should be placed as low in Flash memory as possible. Specifically, it goes immediately after the interrupt vectors and before any other executable code. That way, unless you have almost 64 kB of PROGMEM variables, you'll be sure not to overflow the 16-bit data pointer size.

This means that data pointers to Flash can reach any address in ann AVR up to the ATmega64x.

Access to data living above the 64 kB mark on devices that have it, needs to jump through special hoops.

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

I stand corrected - I had no idea that GCC aligned the two flash sections differently. Cheers.

- Dean :twisted:

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

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

Dean,

Just remember until someone actually writes something down in black and white there would be NO opportunity to find errors!

Until I read the tutorial I had never used the internal EEPROM in my ATmega32. An hour or two and I got things working up to the stage you got to in the tutorial, so I think that makes a pretty good tutorial.

I had read something about pointers being 2 bytes, but couldn't find the reference. Probably wouldn't have helped anyhow!

So, AVR pointers can point to 65536 bytes, ints or longs of information.

Thank you for clearing that one up.

Regards,
davef

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

65536 bytes, (or any 8-bit data types)
32768 ints, (or any 16-bit data types)
16384 longs, (or any 32-bit data types)
or any combination of the above.

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

Oops, thank you for the correction.

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

I would like to contribute to this wonderful tutorial by adding two examples about writing/reading doubles and structures from Eeprom for newbies like me :)

double EEMEM EEVar;
double a;

void WriteDoubleToEeprom(double x){
   eeprom_write_block((const void*)&x, (void*)&EEVar, sizeof(double));
}

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

for example, we can easily write any double value to eeprom by

WriteDoubleToEeprom(123.45);

and then read it back by

a=ReadDoubleFromEeprom();

******************************

As for the structures, suppose we have structure ST like this:

typedef struct SStructure{
    int8_t varintbyte;
    int16_t varintword;
    double vardouble;
    char varchar[10];
}ST;

we have to define a EEPROM variable of ST type

ST EEMEM EEStruct;

and also an ordinary variable of type ST

ST mystructure;

Now, suppose we initiate mystructure like this:

mystructure.varintbyte=10;
mystructure.varintword=516;
mystructure.vardouble=123.45;
strcpy(mystructure.varchar,"Share it");

We can write the whole structure to eeprom without bothering with the size, individual members, etc. by calling this function:

void WriteStructureToEeprom(ST a){
   eeprom_write_block((const void*)&a, (void*)&EEStruct, sizeof(ST));
}

like this: (note the sizeof(ST) statement in the function)

WriteStructureToEeprom(mystructure);

and read the whole structure back from eeprom by calling this function:

ST ReadStructureFromEeprom(void){
   ST temp;
   eeprom_read_block((void*)&temp, (const void*)&EEStruct, sizeof(ST));
   return(temp);
}

like this:

mystructure=ReadStructureFromEeprom();

Cheers,

Cemo

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

Excellent, but do remember that the "double" data type does not exist in GCC at present. It is currently a direct alias to the "float" data type.

- Dean :twisted:

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

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

Yes, you are right. The reason why I habitually use double instead of float is that printf produces a warning when printing a float variable with %f format specifier.

I mean, something like this

float a;
printf("Variable a = %3.2f",a);

causes the compiler warning

warning: double format, float arg (arg 2)

I think the correct way, as you pointed out, is to use the float data type and cast it to double when using it with printf in order to get rid of the compiler warning.

printf("Variable a = %3.2f",(double)a);

So, one can replace all the double's with float's in the examples in my previous post.

Cheers,

Cemo

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

The only thing I'd say to writing structures out to EEPROM like this is that it implies that you have the RAM space available to create an image of what's in EEPROM. On the other hand, if you have a large number of say, configuration variables that you want to store in EEPROM, storing them individually or dereferencing each element of a structure seems like it could save some RAM... Can't say from direct experience, but that's how it looks to me.

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

I personally have all my EEPROM variables inside a single stuct, named EEPROMVars. This allows me to very easily tell which variables are in EEPROM or not, and has not discernable effect on performance. I only read out variables one at a time which means that no extra RAM is wasted.

- Dean :twisted:

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

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

excellent tutorial!
one problem - I can't seem to use the EEMEM directive.
I get compile errors.

yes, I'm including the eeprom.h
and yes, basic eeprom read and write commands are working if I don't use the EEMEM directive.

I thought I had the latest version of winavr and avrstudio installed, but maybe not? can you please specify what install version of winavr you wrote this code for? (and avr-libc if necessary?) thanks!

this might also come in handy in a couple of years if newer versions of winavr don't work with this syntax.

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

I am using AVRLibC 1.4.4 for my tutorial, and WinAVR 20060421 - the latest released version.

If the EEMEM macro is not present in your EEPROM.h header file, then you can add it manually:

#define EEMEM __attribute__((section(".eeprom")))

In my current header file it is also available under the macro name "EEPROM". I'm not sure if this was present in earlier versions - does changing the "EEMEM" instances to "EEPROM" fix your problem?

- Dean :twisted:

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

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

cool!

yeah.. turns out I had winavr20050215 installed.
:oops:
I should probably read these forums more often so I'm up to date with everything that happens.

the earlier eeprom.h doesn't have a #define for EEPROM either.
adding the define for EEMEM in the main code listing worked.

I downloaded the new winavr now.
will have a bash at installing it next.

thankyou so much!

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

Many Many Many thanks to you! I was dreading learning how to access the eeprom, and you summed it up just right. I was able to figgure it out in just an hour or two! Again, thank you so much!

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

No problem - glad I could help!

Best of luck to you in your projects.

- Dean :twisted:

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

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

16 bit address but 8 bit data

For example I want to read the byte at address 260 of my EEPROM (ATMega8515).

Now I can't use (uint8_t *) as an address pointer as address is not in 8-bits.

Also I can't eeprom_read_word because it treats the address as two addresses for two 8-bit data locations.

I need a function like this
eeprom_read_byte ( (uint16_t *) 260 )

Please tell me how to work around this problem. You can reply me at my e-mail address
aliasgherman@yahoo.com

Thankyou,

Ali Asgher Mansoor Habiby[/b]

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

Quote:

You can reply me at my e-mail address
aliasgherman@yahoo.com

I could, but I wont. If you have a question I'll answer it here so I can help others. Whether or not you'll put in the effort to read what I have gone through the effort of writing is up to you.

Writing and reading ints is easy, if you read the entire tutorial ;). What you're after is the eeprom_*_word routines:

Quote:
Our second variable is an int, so we need the eeprom_read_word routine:

#include 

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

int main(void)
{
    uint8_t  SRAMchar;
    uint16_t SRAMint;

    SRAMchar = eeprom_read_byte(&NonVolatileChar);
    SRAMint  = eeprom_read_word(&NonVolatileInt);
}

- Dean :twisted:

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

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

Thanks for the tutorial.

Does anyone know how i can get the compile to create a eeprom file of initial values if i use fixed address?

I know this code works:

uint8_t EEMEM SomeVariable = 12;

but i need to use fixed address as i need to preserve EEPROM values after a flash update (via bootloader). aka if i use the above code i can not guarantee that the compile will pick the same location in the new firmware version.

Let me know if i need to explain..

Cheers

Murray

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

Murray,

Maybe put all your EEPROM variables into a single struct{} so you can determine the positioning. For example:

typedef struct {
 int fred;
 char c;
 long bob ;
} struct_t;

struct_t EEMEM my_struct = { 37, 'a', 12345678 };

produces a .eep file:

:070000002500614E61BC0008
:00000001FF

in which the 37 (0x25), 'a' (0x61) and 12345678 (0xBC614E) are in known positions.

Cliff

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

Yeaaaa Dean!!!! “The Author”
Looks like 1/3 of the memory chapter of a very good book, to the rest of us at least!
Reads like you’re an author, looks like you took the time to write like an author, seems like your eager audience is awaiting your next chapter, to me.
Consider this life observation of two men with the same life span.
Both lifelong labors create only one indicial functioning commercial program that sells 500k copies to its intended vertical market.
But one man is much richer from the same effort in life. Why?
Because, becoming rich is accruing a different state of mine.
While they were both working on the project only one was selling every part he could during development. He wrote several books (from his notes) on each topic he learned throughout his life. He sold several new small programs. Created from the various parts of the main program like the same multi-media routines for family photo albums. He sold his programs security registration routines to other programmers including the other man in this story.
When no one was willing to pay the price he cut the price in half not stopping at zero like most in life would. No, he would give it away for free! PLUS $15.00 shipping and handling and made several thousand dollars from a program no one would pay for.

Warning, becoming rich is accruing a marketing state of mine.
It would be so easy to turn these Tut’s into a very good book that will produce extra income for life with the same labor.

Sorry it’s my job to aspire people to become rich, as they might then be able to loan me some money.
Cheers,
John

Resistance is futile…… You will be compiled!

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

Cheers John!

I sadly don't have the talent to write a book of my own - Smokey possesses what I do not. I've become quite adept at writing specific tutorials, but I just can't seem to "glue" them all together into one big book.

Nice story - selling the parts is a good idea! Now, if only I received a $1 donation for each reader of each of my tutorials ;).

I don't mind giving away my time for free for the community. If I'm doing specific work for an individual/company I do charge (a modest amount), but since I have a choice to contribute here, contributing is its own reward.

- Dean :twisted:

EDIT: And I also enjoy all the kudos writing stuff for free brings! ;)

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

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

abcminiuser wrote:
Cheers John!

I sadly don't have the talent to write a book of my own - Smokey possesses what I do not. I've become quite adept at writing specific tutorials, but I just can't seem to "glue" them all together into one big book.
...


Ask the forum if they agree with your assessment of not having the talent. Currently you are transitioning different subjects within your Tut’s. You will learn how to transition chapters too. Plus, are you kidding most the stuff I have to read they just say “Now I need to change the subject because you need to know this next”. Not much of a transition required with technical writing as the author is just boring me with his transition story anyway. I just want to know how.
All I'm saying is consider saving your work and make plans to turn it into a revenue stream in your future. You will get better (more confidence is all you need now) and continue learning and it will become easier with each Tut you write. Self-doubt is the biggest excuse people use not to try. (and laziness – You don’t have that!)
It doesn’t have to be much of a book to make you money. Remember Chernobyl nuclear plant accident. Within the week a 30 page ‘book’ was written on how to protect your family from fallout. The author had wanted to know how to protect is family and then sold his labor as a 30 page ‘book’ for $12.00 with a money back guarantee. He placed adds in a small weekly local publications in cities within 100 miles of every nuclear plant in the US. $2,100 in advertising made $18,000 in 5 months. Here’s a secret American’s won’t take the time to return anything for less than $15.00 to $25.00.
It’s planning and marketing that makes you easy money in life. Not perfect writing.
Cheers,
John

Resistance is futile…… You will be compiled!

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

Hi Dean,

thanks for this great tutorial, it helped a lot.

I think I found another small bug:

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

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

Shouldn't it be like this?

    uint8_t  SRAMstring[10];   

Kind regards,
Alex

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

Yes it should, good catch. I'll correct that now.

- Dean :twisted:

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

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

@abcminiuser:

You mentioned that you just read out the variables from EEPROM as you need them. I have some data that I am storing in the EEPROM, and I was debating whether to read it out as I need it, or read it into variables in RAM.

The data is being used in a loop that's running maybe every 100 ms or so. I'm not running out of SRAM or anything, but I also don't mind the 4 clock cycle delay caused by reading from the EEPROM. Anyone have any suggestions as to which way is better/more appropriate?

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

It's entirely up to you, if neither the increased RAM nor the increased fetch time is a problem. Reading from EEPROM does not wear out its lifespan, so whichever method you choose will be the one you deem most appropriate.

- Dean :twisted:

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

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

An 8-bit character is stored at address 600 in eeprom. How to read it ?

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

My first example shows how:

eeprom_read_byte((uint8_t*)600);

- Dean :twisted:

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

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

Thankyou very much.

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

Hello!!!

I've tried this helpfull tutorial and it has given me some points of view. If you look example AtMega128 manual, you can see a sample code of how to use eeprom. I won't work.

I switched to this tutorial and now I can Write and Read data to EEPROM. BUT. If I make a program which saves data in to eeprom and then afterwards reads it... works fine...

When I comment the line in the code where I write the data and read it afterwards, it won't work. It just doesn't simply save the data in the eeprom.

If anybody has a some sort of solution for this. I just insert new program in to the flash.

Or my eeprom might be corrupted, cause I've ran non-L version of AtMega128 on 3,3 volts.

Or is it just me :)

Still waiting for my final developement board :)

Best regards
Juha

SW Design Engineer
Finland

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

Sounds like you probably need to set the EESAVE fuse in your AVR. Otherwise each time you put a new program into it the EEPROM is being wiped when the "chip erase" is performed.

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

clawson wrote:
Sounds like you probably need to set the EESAVE fuse in your AVR. Otherwise each time you put a new program into it the EEPROM is being wiped when the "chip erase" is performed.

I just love the power of forums!!! Thank you! Never thought that would do the trick. I double checked the fuse bits and there it is! :)

Have nice weekend, you propably saved it for 47,34% :D

SW Design Engineer
Finland

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

I have a problem writing to EEPROM of atmega8515. I get a string send to parceInput but when i read the EEPROM all I get is ( ¼ ) that. If I put something in the EEPROM using EEMEM, reading it works just fine. Someone please explain how to write a string to EEPROM in one function and read it in another.
Here is the code I tried

uint8_t  EEMEM EEPROMString[10]; // this is before main()
void parceInput(char s[])
{
	if ((s[0] == 'r') && (s[1] == 'e') && (s[2] == 'a') && (s[3] == 'd'))
	{
		sendString("Reading EEPROM \r\n");
		readEEPROM(); // read whats in eeprom

	}
	else
	{
		sendString("Saving to EEPROM : ");
		sendString(s);
		sendChar('\r');
		sendChar('\n');
		eeprom_is_ready(); // dont know what this is, says in EEPROM.h to use it.
		eeprom_write_block((const void*)&s, (void*)&EEPROMString, 10);// write a string to the EEPROM 
	}

}

// readEEPROM
void readEEPROM()
{

eeprom_is_ready();
uint8_t  SRAMstring[10];
eeprom_read_block((void*)&SRAMstring, (const void*)&EEPROMString, 10); // read whats in EEPROM
sendString((char *)SRAMstring);

}

Thanks in advance.

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

hello!

Have you tried like this?

void parceInput(char s[]) -->
void parceInput(char s*)

SW Design Engineer
Finland

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

bitfox,

But those are identical? (an array name is a pointer to a block of bytes of that array type)

Cliff

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

It doesn't build if I use void parceInput(char s*)

../atmega8515EEPROM.c:24: error: expected ';', ',' or ')' before '*' token

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

That's because he got it wrong. He meant:

void parceInput(char *s)

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

I still get that weird looking character. I started a new topic in the GCC forum, it seemed more appropriate. Please, if u have any suggestions, post there.

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

char array[10];

'&array' is a pointer to the /address/ of the first element.
'array' is a pointer to the first element.

So you need to remove the '&'

Author of simavr - Follow me on twitter : @buserror

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

I think noone has mentioned it before in this thread, so I'll do it :)

Quote:
6.5.5. Preventing EEPROM Data Corruption

Well, guess what? Simple usage of the target’s internal EEPROM will lead to spurious and frequent data corruption. The problem is caused mainly on the device’s power down and power fluctuation, and it’s a universal problem that happens with any “unprotected” kind of processor.

When power is cut (or provided) from a device, it actually decays “slowly” and not instantaneously, mainly because of capacitors in the circuit. There’s a point where power is starting to be insufficient for the target, and it starts to behave strangely, like jumping around and executing random pieces of code. That’s when parts of your own EEPROM write routines get executed and corrupt your data! It may also happen that power is turned off right in the middle of a write sequence, leaving your data inconsistent.

In cases of power fluctuation, known as “brown-out”, the problem process is similar. Power goes down to a point that makes the device malfunction, even though not completely off.

The solution to prevent EEPROM data corruption is 2-fold:

    1) The device needs protection against the “power fluctuation” – a brown-out detector, which puts and keeps the device in RESET while the power level is below a threshold considered as “minimum safe value” for the device. A normal power-down can be considered a special case of brownout, in which power doesn’t come back.

    2) Even with brownout protection, data can still get corrupted, because the device can be turned off or reset while a large (more than 1 byte) data structure is being written in the EEPROM. So, you need software protection or redundancy, by using some kind of checksum to validate your data, and optionally multiple data copies so that you don’t loose all of it (in case data is critical).

Many AVRs have a built-in brownout detector that can be enabled. It can be done when programming the device's fuses. There are also external chips that do brown-out detection and have other features as well (like a watchdog, some bytes of FLASH, etc). These devices connect to the uC’s RESET pin.

Embedded Dreams
One day, knowledge will replace money.

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

Dear Dean,
still there are fellows (like me) meeting your *excellent* tutorial just now. I have read all the discussion followed it, too, but did not get answer to my special question:
Is there any way of storing my EEPROM variable at a specific address? I need to force an array to be stored on page boundary. Can I do it?
Cheers, Istvan

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

Istvan,

I can't see why you'd need that - the EEPROM is not paged, so there are no performance benefits, nor any indication of how big a "page" is, since it's byte-addressed.

However, you could do one of two things. First, you could just use a pointer to your desired address:

eeprom_read_byte((uint8_t*)600);

Which would present problems if your other automatic variables overlap that area. The second way is to use GCC's sections to create a new EEPROM section at the desired location - there are some threads about this which you can search out. However, while the section method will allow you to create automatic variables in the new EEPROM section, you'll still run into problems if your normal EEPROM variables overlap.

- 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,
thanks for the fast response.
My program implements an extremely fast assembly loop (only 9 statements) to generate a stored waveform. To be fast enough, this loop handles only the lower part of a pointer, so my data must be aligned to some address represented as 0x...00 (I meant a page is an area of 256 locations starting at nn00 and ending at 00ff).
Is there any way of direct alinging?
Thanks, Istvan

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

The EEPROM is byte addressed, so no pages. You could I suppose just change the lower byte address every loop cycle and increment the upper address once every 256 cycles, but that would produce some jitter in your waveform due to the extra cycles being used on the 256'th iteration. Better to just use an extra cycle and load the full address.

- Dean :twisted:

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

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

Alternatively it should be possible to put a named section into EEPROM space done in a similar way to the existing .eeprom at present and with a --section-start passed to the linker.

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

abcminiuser wrote:
The EEPROM is byte addressed, so no pages. You could I suppose just change the lower byte address every loop cycle and increment the upper address once every 256 cycles, but that would produce some jitter in your waveform due to the extra cycles being used on the 256'th iteration. Better to just use an extra cycle and load the full address.

- Dean :twisted:

I'm afraid my English is too bad to describe my wish.
I know EEPROM is byte addressed, but... e.g. in Intel386 assembly, if I want to fit a table to a 16 bit boundary (i.e. the lowest 4 bits of the address are 0 and the others are anything else) it is called a "paragraph". Similarly, if the lowest 8 bits are 0, then this is a "page" boundary.
Due to the upper address byte is intended to be eliminated, I have to care the table to be fit to proper address. So I am looking for the way to build something like this:

ldi zh,high table
clr zl
...
...
...
.eeprom
.org 0x100
table:
.byte x, y, z, etc

Now I have solved my original question by mixing with assembly, but I am still interested whether it can be solved in pure C?
Thanks, Istvan

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

Yes it can - use a named section and place it with the linker (like I said above)

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

clawson wrote:
Yes it can - use a named section and place it with the linker (like I said above)

Could you please drop me a link where I could read about the details? I'm a bit afraid of editing makefile... :(
Thanks forward,
Istvan

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

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

I have found it useful to have two stacks in eeprom. One at 0x4 with increasing address and one at 0x1FF (on my mega16) and two stack pointers at 0x0 resp 0x2. I can store linked lists this way, vey useful.

One thing though, the data sheet says the mega16 has 512 bytes of EEPROM but it seems it is 513 bytes as i can address 0x0 -> 0x200, can anyone confirm this or is this where my bug is lying? I am absolutely positive that i can address 0x200 but i was thinking it might be reserved or something like that.

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

Hi,

Thank you for your excellent tutorial!
I am using the STK500 with the ATMega8515 chip and programming in C using AVR studio with GCC compiler.

When you use the eeprom_write_byte / eeprom_read_byte commands in your program, can the assembled .hex file just be loaded into the flash memory and will that be sufficient to store my data into the eeprom?

If I run my code as is it simulates fine on the AVR simulator, however, when I load the file onto the the chip, the output is entirely different.

The code basically cycles through an input string and displays the corresponding values in PORT B (LED's)

#include 
#include 
#include 
#include 

#define F_CPU 1000000UL 	//1 Mz

int main(void)
{
	DDRB = 0x3f;	//set 0-5 LED's to output
	char tex[20] = "ABCDE";
	int i=0;

	eeprom_write_byte((uint8_t*)65, 0x01);	//A
	eeprom_write_byte((uint8_t*)66, 0x03);	//B
	eeprom_write_byte((uint8_t*)67, 0x09);	//C
	eeprom_write_byte((uint8_t*)68, 0x19);	//D
	eeprom_write_byte((uint8_t*)69, 0x11);	//E
	
	for (;;)
	{ 
		while (tex[i] != '\0')
		{
			uint8_t byteofdata= eeprom_read_byte((uint8_t*)tex[i]);	

			
			PORTB = byteofdata;
		
			_delay_ms(2000);
			_delay_ms(2000);
			i++;	
		}
		i = 0;
	}
	return(0); 
}

Thanks!

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

Yes, just loading the HEX into the chip would be sufficient in your case -- if you move to using the EEMEM attribute in the future with initialized values, you'll also need to program in the generated .EEP file into your chip.

In your case, you've got a problem. Your write code is fine, and should result in the letters A, B, C, D and E being stored to consecutive EEPROM addresses. However, your read loop reads in bytes from EEPROM, but at the address of your "tex" array, which is located in SRAM. Since the location of the tex array will almost definitely be different to your hard-coded EEPROM addresses (of 65 through 69), the program will output different values to the LEDs.

Also, unless you are using the latest WinAVR/avrlib-c distribution, the _delay_ms() macro has a limit of only about 20ms -- check the user manual.

The "correct" code would be:

#include 
#include 
#include 
#include 

#define F_CPU 1000000UL    // 1 MHz

char texEEPROM[20] EEMEM;  // Create a variable located in the EEPROM space

int main(void)
{
   char    tex[20] = "ABCDE";
   uint8_t texByte;

   DDRB = 0x3f;   //set 0-5 LED's to output

   // Copy the "tex" array to the EEPROM, at the location of the texEEPROM array
   for (texByte = 0; texByte < sizeof(tex); texByte++)
      eeprom_write_byte(&texEEPROM[texByte], tex[texByte]);
   
   for (;;)
   {
      uint8_t ReadByte;

      texByte = 0;

      // Read out the texEEPROM array from EEPROM and display on LEDs
      do
      {
         ReadByte = eeprom_read_byte(&texEEPROM[texByte]);
 
         PORTB = ReadByte;

         for (uint8_t DLoops = 0; DLoops < 100; DLoops++)
            _delay_ms(20);
      } while (ReadByte != '\0');
   }

   return 0;
}

- Dean :twisted:

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

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

Thanks Dean,

However, I am trying to do something a little different. Basically, when the string shows A, B, C... I want the LED's (PORT B) to specifically be 0x01, 0x03, 0x09...as seen in the write_byte command (these values must be hardcoded).
So in order to do this, I hardcoded these values in the eeprom locations corresponding to the ASCII values of A,B,C...
So when i=1, tex[i] = 65 (ascii value of 'A')
Next I read eeprom_read_byte((uint8_t*)65) which should be 0x01 right?

When I run this code in the AVR simulator I get 0x01 in PORT B, but when I load the code onto the chip itself, the LED's on the STK500, don't display 0x01.

Thanks in advance!

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

Most likely then your _delay_ms function isn't working as expected. Unless you have the absolute latest library version the maximum ms delay value is around 20ms. Try replacing your calls to _delay_ms with the code:

for (uint8_t DLoops = 0; DLoops < 100; DLoops++)
            _delay_ms(20);

And ensure you compile with the right F_CPU CPU speed value - if the AVR is out of the box, it will be running off its internal 1MHz RC oscillator and not your external clock/crystal.

- Dean :twisted:

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

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

Great tutorial! For anyone else who had trouble finding information on (at compile time) retrieving the size of the EEPROM (for example, you want to start storing things at the end, rather than the beginning), the constant for the last address in the EEPROM is

E2END

There are corresponding constants for Flash and other sizes, allowing you to write more portable code. I think, based on other information I found, that when using the IAR compiler, the equivalent definition is __EEPROM_SIZE__
Good luck!

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

hi, thx for the tutorial!!!

i'm working on my first project in C on a ATmega8 (actually it's a porting of an asm project in C).
i've got some problems when i read a data from the eeprom (i can't understend where i'm wrong)

:cry:
the piece of code is:

Quote:

#include
#include
#include

void readEeprom(void)
{
uint8_t eepromData;
...

//read idCube
eeprom_busy_wait();
eepromData = eeprom_read_byte((uint8_t*)6);
if (eepromData == 0xff)
idCube = idCubeDefault; (*)
else
idCube = eepromData;
...
}

and i get the following error:

Quote:

../MicrelCube3_2.c:352: error: expected expression before '=' token

on the line with the (*)

i just can't figure out what's wrong...
thx,
b.

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

You need to declare idCube, the compiler is complaining that it doesn't know what "idCube" is. Try sticking:

uint8_t idCube;

At the start of the program (after "uint8_t eepromData;").

Also, a suggestion. Try using the ternary operator for the assignment to clean up the code:

idCube = (eepromData == 0xff) ? idCubeDefault : eepromData;

- Dean :twisted:

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

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

Hi,
i've defined idCube, in my header, but because of the source project in asm i had definitions like:
#deine idCube = 1
and that '=' caused all my troubles...
thx for the help,
b.

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

That means that idCube is a constant token, not a variable. You can't assign values to a macro, only variables.

- Dean :twisted:

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

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

Dean,
How does one initialize an eeprom array?

uint8_t EEMEM SomeVariable = 12; works fine for a single byte..., but what if it is SomeVariable[10] and one wishes to initialize a particular element or two?

Thanks,
John

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

You can initialize the EEPROM array just like a normal C array:

uint8_t EEArray[5] EEMEM = {1, 2, 3, 4, 5};

- Dean :twisted:

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

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

Egads man!..., don't you ever sleep?!

That, of course does the trick for the array.

Thanks, again
John

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

Sl...eep? I'll have to go look that up in the dictionary ;).

Glad I could help.

- Dean :twisted:

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

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

Hi everyone. I've got stuck on this EEPROM problem. I am using an ATmega169 and JTAGICE as debugger/programmer. I just want to verify that i am succesfully writing a data to the EEPROM. So I make a code to write and then read the data.

#include 
void main(void)
{
   uint8_t mbyte;
   uint8_t rbyte;

   for(mbyte=0;mybte<100;mbyte++)
      eeprom_write_byte((uint8_t *) mbyte,mbyte);
      //just write data 00 to 99 at address 00-99
   for(mbyte=0;mbyte<100;mbyte++)
      rbyte=eeprom_read_byte((uint8_t *)mbyte);
      //read data from address 00-99
   while(1);//endless loop
}

This code works pretty well and I confirm it during debugging and step by step execution. But when i tried to comment the portion of eeprom writing, and then run the debugger, it seems that all data i read are all 0xFF(at all address). Is there something wrong?.. Hope someone could help me on this.Thank you so much, more power!..
jasperman

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

Each time you start to debug it erases the chip and downloads the code. During the chip erase the EEPROM will also be wiped. To avoid that try setting the EESAVE fuse.

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

Quote:

Each time you start to debug it erases the chip and downloads the code. During the chip erase the EEPROM will also be wiped. To avoid that try setting the EESAVE fuse.

I already try that, using the "Preserve EEPROM during Chip erase", but i still get the same result.. Is there anything else i forgot about settings?..

jasperman

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

Hi guys!

I have already figured out with my problem on the EEPROM. I decided to share it here so that it can help also others with troubles the same as mine..

First is you must enable the "Preserve eeprom during programming" Fuse bit.

Second, since I am using a JTAG ICE, you must enable also the "Preserve eeprom during programming" in the JTAG settings. This is what i mislooked.

JTAG settings can be achieve on the "Debug" menu of the AVR studio, click on the "JTAG ICE options". It will open you a new window with options on the "Connections", "Debug", "Breakpoints" and "Status". Under the "Debug" options, you must enable the "Preserve eeprom during programming".

You must take note that even though you enable the "preserve eeprom" fuse bits but did not enable the "preserve eeprom" on the JTAG settings, then your eeprom will always be erased everytime you start the JTAG ICE.

Hope this might help others with the same problem as mine.

jasperman

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

So I'm trying to use the eeprom to store the mac and IP address.

Not knowing the structure of the eeprom hex file, I let GCC do it by declaring the values using the EEMEM attribute:

uint8_t EEMEM mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24};
uint8_t EEMEM myip[4] = {192,168,2,101};

I took the eeprom file that it generated and renamed it so it wouldn't get overwritten the next time I compiled. The file looks like this by the way in a hex editor:

3A 31 30 30 30 30 30 30 30 35 34 35 35 35 38 31 30 30 30 32 34 43 30 41 38 30 32 36 35 46 46 46 46 46 46 46 46 46 46 46 46 46 32 0D 0A 3A 31 30 30 30 31 30 30 30

I make sure the fuse is set where the eeprom isn't overwritten anymore.

Now that I have my eeprom file I comment out the above lines and just make the variables empty arrays:

uint8_t mymac[6];
uint8_t myip[4];

I then try to read into the arrays:

eeprom_read_block((void*)&mymac, (const void*)0, 6);
eeprom_read_block((void*)&myip, (const void*)6, 4);

I get garbage out every time.

What's odd to me is that I don't see the hex mac address in the eeprom. In short, I know I'm doing it wrong but I feel that I've followed the tutorial pretty closely.

Anyone else use eeprom this way?

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

Quote:
I make sure the fuse is set where the eeprom isn't overwritten anymore.

Try reading the EEPROM contents back out to a different .hex after you have just programmed it. Then program the app code and read out the EEPROM yet again. Does the data appear to be intact across all steps? Also does your app code contain any form of eeprom_write_*() that might be corrupting the data?

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

I just read the eeprom after a programming and shows the same as above followed by a lot of 0x46's (FF's) surrounded by address fields.

Does it make sense how it writes those bytes? As an example, take the first byte of the mac address (0x54). In the eeprom, it appears to be in the two bytes 0x35, 0x34. Putting a "3" in front of each number makes no sense to me unless that's how the hex file structure is supposed to be for an eprom.

Also, I have no eeprom writing in my program, just reading in those two lines.

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

Well my program appears to be working since if I write a new eeprom file with a new ip address, the program switches to it.

I just don't understand why the eeprom file looks that way in a hex editor.

EDIT: All was/is working. I was looking at the wrong screen of my hexeditor.

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

I'm experimenting and building a dot matrix display, and want to store the font (max 16*16 px) inside the Atmegas eeprom. I need advice about how to store the pixel information.

The "problem" is that I don't want to store 16*16 bits for characters which don't need the size. As you all know, 'M' is much wider than '!'.. No need to store '!' 16px wide, right? My idea is to store a "size byte" (using upper/lower 4 bits for width/heigth) to each char which tells me how wide/high it is.

For example, let's say ' ! ' is 1 bit wide, 8 bits high (data byte 0xFD), and ' " ' is 3 bits wide, 8 bits high (data bytes 0xC0, 0x00, 0xC0).. And let's pretend another char is a 16*16 char. I can store the description byte telling me how wide/high they all are, but how do I store the actual data in the eeprom in custom size? How do I know where to read the data?

I hope you get the idea of what I'm looking for. Is it possible to do something like this? Suggestions?

Thank you in advance,
/D

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

daffy,

Just out of interest, why use the EEPROM and not simply const data in code flash or is the idea here that the font is some how user editable (but that's going to make it a nightmare if a small block of data needs to be replaced by a larger block!)

As for the "compression". You seem to have described a solution that works there anyway. Obviously to display character N you either need an index for the start locations of all the defintions or you start at the first character, read its width/height then skip that many bytes to the next width/height and do this N times to get to character N. If space allows perhaps an index is a better idea?

To prepare the data (assuming you already have it all defined as 16x16) I'd just put together a quick PC program to process this and output it in the "compressed" form. The advantage of this is that you can then later edit the 16x16 grids and just crank the handle to output a new dataset.

Cliff

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

edit: double post

Last Edited: Tue. Sep 16, 2008 - 09:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Just out of interest, why use the EEPROM and not simply const data in code flash or is the idea here that the font is some how user editable (but that's going to make it a nightmare if a small block of data needs to be replaced by a larger block!)

As you thought, my plan is to be able to write/create the font "outside" the avr, and insert it later. I'll write a small app where I can specify heigth/width per char, and then send it via rs232 to the display.

Quote:

As for the "compression". You seem to have described a solution that works there anyway. Obviously to display character N you either need an index for the start locations of all the defintions or you start at the first character, read its width/height then skip that many bytes to the next width/height and do this N times to get to character N. If space allows perhaps an index is a better idea?

Ah.. Do you mean I keep a separate list with eeprom start locations of every character? (And that location describes how many bytes the character consist of, followed by the data bytes?)

Edit: Yes, that must be what you mean.. But how do I figure out the start locations when storing the font in the first place?

Quote:

To prepare the data (assuming you already have it all defined as 16x16) I'd just put together a quick PC program to process this and output it in the "compressed" form.

Exactly my plan. Write a PC app in which I create the font, save the font to the display. Would be convenient =)

Quote:

The advantage of this is that you can then later edit the 16x16 grids and just crank the handle to output a new dataset.

I don't understand what you mean? (it's certainly something good though ;)

Thanks for the reply!
/D

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

My application uses 6 parameters which I want to be able to set by a 2x8 LCD and three buttons so I do not need my laptop. I use the following code:

uint16_t wtht;
wtht = eeprom_read_word((uint16_t*)46);
while (1)
{
lcd_clear();
lcd_gotoxy(0, 0);
lcd_string("WTHT ");
lcd_int(wtht);
lcd_gotoxy(0, 1);
lcd_string("- + NEXT");
delay_ms(50);
if ( (PINB & (1 << PB0)) == 0 )
{ --wtht; delay_ms(100); }
if ( (PINB & (1 << PB4)) == 0 )
{ ++wtht; delay_ms(100); }
if ( (PINB & (1 << PB5)) == 0 )
{ break; }
}
eeprom_write_word((uint16_t*)46,wtht);
delay_ms(500);

Works great and can be multiplied for all my other parameters.

I took the value 46 because it was in the tutorial, I tried to read the datasheet of the MEGA48 but do not understand how I can determine the numbers I am allowed to use. Can anyone explain?

Erik

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

Quote:

I took the value 46 because it was in the tutorial, I tried to read the datasheet of the MEGA48 but do not understand how I can determine the numbers I am allowed to use. Can anyone explain?

Yes. That is a totally arbitrary address in EEPROM that Dean chose:
abcminiuser wrote:
let's say at location 46

The addresses you are allowed to use are from zero to the size of the EEPROM minus one. The mega48 has 256 bytes of EEPROM so the legal addresses are 0 to 255.

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

Sometimes life is easier than expected,

As I use 16 bit parameters I have to skip one adress between every parameter, it works great, thanks for the reply

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

Hallo, I'm a really newbie at programming avr-controllers. At the moment i want to learn to work with the eeprom memory. But i have a big problem. After i have included avr/eeprom.h i get following error message:
Unknown EEPROM register(s) location
:(

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

Which AVR are you building for?

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

XMEGA128A1

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

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

Does this mean, that i can't work with eeprom.h on mega128a1

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

Nope but you can't use pre-written routines in the current avr-libc's eeprom.h so you'll have to hit the registers directly yourself.

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

Great Tutorial Thanks helped me when my AVR books couldn't.
Dean

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

Dean thanks for your tutorial!
but i think the max Endurance is
Endurance: 100,000 Write/Erase Cycles.

I love Digital
and you who involved in it!

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

Quite right Ali! The original AVRs had endurances of 10,000 writes for FLASH, and 100,000 for EEPROM. Later with the newer versions (MEGA and TINY) the FLASH endurance was extended to 100,000 also. Looks like I managed to mix the two up - the EEPROM lifespan was never extended.

- Dean :twisted:

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

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

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