XMEGA EEPROM Read and Write - Page 1
Forum section: electronics / general

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 05:17:48 am
content: I've read and Googled, but the answer still eludes me. Using C (Atmel Studio) with an XMEGA, I'm wanting simply read and write the EEPROM using constant addresses.The C function eeprom_write_byte takes two parameters; the address to write and the value to write at that address.But the address parameter is of type uint8_t, so how can it work with more than 256 EE locations? I'm obviously missing something here.

author: edavid
author_rank: Super Contributor
timestamp: September 15, 2013, 05:39:54 am
content: The address parameter is apointerto a uint8_t.

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 05:46:38 am
content: OK, but what if I want to write to a specific EE address (such as 0x140), not at an address derived from another variable?

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 06:01:59 am
content: Also, since the pointer is to an uint8_t type, doesn't that imply that the value pointed to can only be in the 0.255 value range?

author: andete
author_rank: Contributor
timestamp: September 15, 2013, 06:06:42 am
content: you cast your address to a pointer:something like this:int address = 0x140;uint8_t value = 32;eeprom_write_byte((uint8_t *) address, value);

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 06:15:21 am
content: Sounds like I will need to either;1) Wrap the ee_read_byte and ee_write_byte functions so I can easily call them with with literal addresses2) Ditch the ee library and write my own functions

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 06:33:17 am
content: So you're saying that the data type that the pointerpoints tois not limited to an uint8_t type?Also, is it theaddress of the pointerthat's limited to the 0.255 range?

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 08:10:27 am
content: OK, this is what I ended up with:Code:struct sSettings{uint8_t PresetNo;// More settings to go here};struct sSettings Settings;Code:void EE_Load_Settings(void){_Bool UpdateRequired = 0;uint16_t Address = 0x200;// Read the whole structure into RAMeeprom_read_block((void*)&Settings,(void*) Address,sizeof(Settings));// Check validity of Preset Noif((Settings.PresetNo > 9)){Settings.PresetNo = 0;UpdateRequired = 1;}// Check to see if we need to write any fixes back outif(UpdateRequired==1)EE_Save_Settings();}void EE_Save_Settings(void){uint16_t Address = 0x200;eeprom_update_block((const void*)&Settings,(void*) Address,sizeof(Settings));}

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 09:05:02 am
content: I understand how pointers work, but my earlier understanding was that the pointer was to a 8 bit variable. This means that the value returned by the pointer could only be an 8 bit value.Then it seemed that the pointer itself was 8 bits in size, which would limit its addressing range.The fact that the function has byte in its name only means that the value read from / written to the selected EE location is an 8 bit value. That makes perfect sense. The confusion for me was how the function could address more than 256 bytes of EE via a pointer that seemed to be limited.I did look at the ASF and to be honest didn't like the way it hid so much. Coming from an assembler code background I like seeing the nuts and bolts of the code.

author: bingo600
author_rank: Super Contributor
timestamp: September 15, 2013, 09:16:51 am
content: But you said it your self ....The "Object the pointer points at" is uint8_t or "8-bit" ...The pointer (aka address/offset/index) is not , so the pointer could easily be a 16-bit value (index) , "pointing to/on a 8-bit value".Code:uint8_t *pointer;pointer = 0x300; //Pointer points at the value @ 0x300 (This one just loads the pointer address)*pointer = 0x100 //Sets the (uint8_t) value @ pointer to 0x100; (This one changes the value that pointer points at)/Bingo

author: Psi
author_rank: Super Contributor
timestamp: September 15, 2013, 09:28:53 am
content: I think you're missing the key thing about declaring pointers and what is actually happening.// here you're declaring a simple variable foo and the uint8_t means foo can store 8bits;uint8_t Foo;// here the * means this is a pointer called Foo. (keep in mind that the uint8_t here means something quite different)uint8_t *FooFoo can be refereed to in two ways.Foo = The memory address number*Foo = The contents of that memory address (that's where the type comes in).The pointer variable Foo is always whatever size is needed on your CPU to store an address in memory. I think they're 16bit on AVR but don't quote me. So it doesn't matter if you go "uint16_t *Foo;" or "uint64_t *Foo;" Foo is always the same length variable because it only ever needs to contain a memory address that points to the start of something.(I'm probably going to explain this next bit a little wrong from a low level perspective but it may help explain the concept....)You can think of the type part (uint8_t in this example) as a message to the C compiler to expect an 8bit number at the Foo memory address.This becomes important when you think about larger variables.Say you want a pointer to a uint32_t which is of course 4 bytes long.uint32_t *BarBar is only a single 16bit memory address. It points to the memory for the first byte of this 4 byte number.The type (uint32_t) helps the compiler out by letting it know what you're going to be doing with *Bar.If you increment Bar by 1 the address is incremented by 4.Bar++; //jump forward 4 bytes because bar is uint32_t and that is a 4 byte type.You'd be stupid to do that here since that's unknown memory but if you had an array of uint32_t you could correctly step through memory in increments of 4.

author: bingo600
author_rank: Super Contributor
timestamp: September 15, 2013, 09:29:26 am
content: Hmmm ... PSI beat me to it :-)Quote from: bingo600 on September 15, 2013, 09:16:51 amCode:In pseudo asm ....X= 16bit index regA= 8bit accum.LD X,#0x300 //Init pointerLD A,#0x100ST ,AThe confusion comes from us old asm guyzz in why should the compiler know what the pointer points at .......But that makes sense here .....Code:uint8_t *pointer;pointer = 0x300; //Pointer points at the value @ 0x300 (This one just loads the pointer address)*pointer++ = 0x100 //Sets the (uint8_t) value @ pointer to 0x100; (This one changes the value that pointer points at , and increments the pointer to point at the next element)Code:LD X,#0x300 //Init pointerLD A,#0x100 //ST ,A //Store A , where X pointsINC X // Point @ next element.But what of the element pointed at was a 16bit ??Then the compiler had to doINC XINC XOrADD X,#2In "C" Notation ....ADD X,"sizeof element pointed at".That's why you have to tell the C compiler what the pointer is pointing at/Bingo

author: bingo600
author_rank: Super Contributor
timestamp: September 15, 2013, 09:40:01 am
content: Quote from: Psi on September 15, 2013, 09:28:53 amNaahhh ....The above would actually increment the *Bar memory location by 1 , not change the pointer (Bar) it self/Bingo

author: Psi
author_rank: Super Contributor
timestamp: September 15, 2013, 09:44:44 am
content: Isn't that what i said?Maybe i should reword it from "next memory location" to "next location in memory" ?(You can see why people say pointers are confusing)

author: bingo600
author_rank: Super Contributor
timestamp: September 15, 2013, 10:01:17 am
content: Quote from: Psi on September 15, 2013, 09:44:44 amBut it doesn't do anything "next" it just increments "location"*Bar = *Bar + 1 "==>" (*Bar)++ not Bar++QuoteYuppp , especially for asm programmers that doesn't expect the C-Compiler to handle the pointer arithmatik automatically ... Witch is excactly why you need to tell the compiler what the objecttype is that the pointer is pointing at./Bingo

author: Psi
author_rank: Super Contributor
timestamp: September 15, 2013, 10:02:41 am
content: Yeah, "Next" was a bad word to use.It could mean next <type> or next number

author: baljemmett
author_rank: Unknown
timestamp: September 15, 2013, 11:42:18 am
content: Quote from: David_AVD on September 15, 2013, 06:15:21 amYou might want to take a quick read ofhttp://www.avrfreaks.net/?name=PNphpBB2&file=viewtopic&t=38417which shows some examples, including how you can use literal addresses by casting the constant as appropriate.

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 11:49:25 am
content: For EE access, there is an address (where the EE data is held) and the data (the data at that EE address).In the world of asm, you'd set the address high and low bytes, then do the read or write.My confusion about the pointer is in relation the the function:Code:eeprom_write_byte((uint8_t *) address, value);To me, the uint8_t could mean one of two things;1) The address pointer is 8 bits in width, but that would mean you could only have 256 pointers (assuming they pointed to 8-bit data types)2) The value returned by the address pointer is 8 bits in width, but that would mean the effective EE address could only access EE locations 0.255Obviously neither of those is true. The value ultimately used as the EE address is actually 16 bits.I'm sure it's just a case of moving from asm (and Delphi on PC) to C being in some ways be worse than starting from scratch. Some things in C seem so bat shit crazy compared to other languages.

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 11:51:38 am
content: Quote from: baljemmett on September 15, 2013, 11:42:18 amI saw those examples, but they have one glaring problem; they only deal with literals that are within the 8-bit data size. For someone new to C, examples showing larger address ranges would be better.

author: baljemmett
author_rank: Unknown
timestamp: September 15, 2013, 12:26:00 pm
content: Quote from: David_AVD on September 15, 2013, 11:49:25 amNo, you're right with the second one -- the value the pointer points to is indeed 8 bits in width. However, you're making the mistake (quite reasonably) of thinking that the value it points to is going to be used as the EE address -- it isn't. The actual valueof the pointer, i.e. the address it represents, is what's going to be used as the EE address -- so it's actually a pointerintothe EEPROM address space (literally a pointer to the 8-bit value in the EEPROM), with 0 being the first byte of EEPROM, etc.Quote from: David_AVD on September 15, 2013, 11:49:25 amTo be honest, even as a seasoned C programmer I think your confusion is understandable -- I would usually expect the 'address' parameter of an EEPROM read/write routine to be an integral type, not a pointer to an object of the transaction size. However, from a quick glance at some of the library documentation (not having used any AVR chips myself), I get the impression that the EEPROM is actually mapped into the processor's address space, so using pointers may make some sense.I still think it's a trifle weird, though, as surely you can't actually use them as pointers (otherwise why do you need the library functions?) It does let you do things like struct { uint8_t foo; unit16_t bar; } *p = 0x42; eeprom_write_word(&p->bar, 0x4242); though, I suppose.Quote from: David_AVD on September 15, 2013, 11:51:38 amYes, there is that -- I don't know how large the EEPROM is on 'common' AVRs. Some of the popular PICs that people choose to get started with have <= 256 bytes, for instance, so perhaps people writing tutorial material stick to smaller addresses to make sure it all fits -- or they just pick numbers off the top of their head

author: madires
author_rank: Super Contributor
timestamp: September 15, 2013, 12:33:05 pm
content: Quote from: David_AVD on September 15, 2013, 11:49:25 amIf you like to write a 8 bit value to 0x0140:Code:eeprom_write_byte((uint8_t *) 0x0140, value);The "(uint8_t *)" is necessary to keep the compiler happy. And it's a good coding style because it reminds the reader what the function expects for input.

author: Psi
author_rank: Super Contributor
timestamp: September 15, 2013, 01:09:17 pm
content: At it's core a pointer is just a number.Usually the number is used to interact with ram but in this case the function uses it to point to an eeprom location.I think the function does it that way because eeprom is kinda like ram.Basically the function just want a number that means the eeprom location from 0 to 2048 or however much eeprom you have.But it wants this number in the form of a pointer because that is how the person who wrote the function decide to do it.The fact that the pointer is of type uint8_t is only because he had to tell the compiler something about this pointer as it's a function parameter (or he could have used a void pointer (pointer to unknown type)).It makes more sense to call it uint8_t because the function is about writing a byte.It doesn't play a roll in whats going on other than that.

author: dannyf
author_rank: Super Contributor
timestamp: September 15, 2013, 01:10:19 pm
content: The key to understand this all is to recognize that the address pointer, (uint8_t *), is a multi-byte type,After that, everything is clear.

author: David_AVD
author_rank: Super Contributor
timestamp: September 15, 2013, 09:38:31 pm
content: Quote from: baljemmett on September 15, 2013, 12:26:00 pmAccording to the help docs, the EE address value (0x00..0x7FF) is shifted up into the 0x2000..0x27FF address space by the EE library. I understand that. The pointer itself and the value pointed to must both be 16-bit values though as otherwise you could never have enough range to access all of the EE space.Anyway, I *think* I have a handle on it now. I'll have to read up and see how to read back the EE data into Atmel Studio so I can confirm that the data is being stored where I want.Some of you are probably wondering why I care exactly where the data is stored in the EE. I need to allocate the areas used for the settings in such a way that they will persist during a firmware upgrade. There will also be other generic read/write EE routines that are used for configuration via a PC application, so I need to know that existing data addresses won't change if more are added with later firmware revisions.There are 10 preset modes of operation and I am leaving space so the structure for each mode (currently 22 bytes) can grow in the future to a max of 32 bytes. There is 2K of EE space on the chip I'm using so I may space them out even more (64 bytes for example) to be sure.As usual, I'd like to thank you guys for being patient with me and taking the time to answer my questions.

author: Psi
author_rank: Super Contributor
timestamp: September 16, 2013, 03:22:55 am
content: The number located at a memory address stored in a pointer is always8bits if the memory is organised in bytes16bits if the memory is organised in wordsetc.

author: David_AVD
author_rank: Super Contributor
timestamp: September 16, 2013, 03:28:41 am
content: Quote from: Psi on September 16, 2013, 03:22:55 amSo you mean the contents of the pointer (not the address of the pointer) for the address argument in that EE function is only 8 bits ?EDIT: I assumed that the EE was organised as bytes.

author: Psi
author_rank: Super Contributor
timestamp: September 16, 2013, 03:30:31 am
content: I've never used an XMega so i dunno how it structures the memory and the eeprom.But a pointer always points to a single memory location be it 8bit, 16bit, 32bit etc.. depending on the hardwareeg,For a cpu that has memory/eeprom stored in bytes...uint32_t *MyPointer;When you use *MyPointer in code you're working with a 32bit number only because the compiler has itself taken care of reading/writing the next 3 bytes ahead of the pointer every time you interact with it.The pointer still only points to one byte, the first one.If the memory is stored in words it's the same except a single memory location is 16bit. So the compiler only needs to read ahead in memory by 1 to get all 32bits.And the pointer still only points to one unit/address, except this time its a word. The first word of the 32bits

author: Kremmen
author_rank: Super Contributor
timestamp: September 16, 2013, 06:55:58 am
content: Quote from: David_AVD on September 15, 2013, 05:17:48 amActually, as stated here already, the address parameter is a _pointer_ _to_ uint8_t. The address variable is of pointer type, not uint8_t.You can simply typecast a constant into a pointer. The EEPROM is mapped form 0 to EEPROM_SIZE-1 and the compiler/linker will handle any address translations for you.so you should be able to do this:Code:nvm_eeprom_write_byte( (eeprom_addr_t) 0, 0xaa);(assuming you used the Studio ASF NVM driver library). That will write the byte 0xaa at EEPROM address 0.

author: David_AVD
author_rank: Super Contributor
timestamp: September 16, 2013, 09:26:08 am
content: Quote from: Kremmen on September 16, 2013, 06:55:58 amI understand that the pointer is a 16 bit value that points to a memory location.When you say "the address parameter is a _pointer_ _to_ uint8_t.", are you saying that the value in the memory location pointed to is an 8 bit type?

author: Psi
author_rank: Super Contributor
timestamp: September 16, 2013, 10:00:22 am
content: Quote from: mojo-chan on September 16, 2013, 08:58:41 amops, sorry guys.I really should have refreshed my knowledge of pointers before posting instead of trying to remember.I've gone back and edited my original posts so they make sense.And sorry bingo600, i see you also noticed my mistake and i didn't quite realise what you were saying

author: dannyf
author_rank: Super Contributor
timestamp: September 16, 2013, 10:26:34 am
content: Quote

author: madires
author_rank: Super Contributor
timestamp: September 16, 2013, 10:36:52 am
content: Quote from: David_AVD on September 16, 2013, 09:26:08 amI think you got it :-)

author: westfw
author_rank: Super Contributor
timestamp: September 16, 2013, 04:28:17 pm
content: I think you're all making it much more complicated than it needs to be.eeprom_writes_byte() reads a byte into chunk of memory; therefore its destination argument is a pointer to a byte.There are similar eeprom_write_word() and eeprom_write_dword() that take pointers to uint_16 and uint_32.This way you can seamlessly do things like:Code:typedef struct chartdata_ { // chart recorder data record.uint32_t timestamp;uint8_t temperature;uint8_t humidity;} chartdata_t;chartdata_t *logfile = (chartdata_t *)(0); // logfile starts at the beginning of eeprom:eeprom_write_dword(&logfile->timestamp, now);eeprom_write_byte(&logfile->temperature, currenttemp);Yes, since eeprom and data have separate address spaces, you can screw yourself over by using the pointer to point to the wrong kind of memory.

author: David_AVD
author_rank: Super Contributor
timestamp: September 16, 2013, 09:12:24 pm
content: The function definitions still look weird to me, but I'm sure it will click totally one day. For now I'm happy knowing how to use them, even if I don't know how they operate internally.Where does one find the source for those functions? The header file is easy to find, but there doesn't appear to be a matching C file?

author: dannyf
author_rank: Super Contributor
timestamp: September 17, 2013, 11:08:52 am
content: AS is based on win-avr so you should be able to download the source if you google.

author: Psi
author_rank: Super Contributor
timestamp: September 17, 2013, 12:25:37 pm
content: There's no real need to search/download it, the library and source is in the AS folders somewhere.Searching for a copy online is probably faster though.Finding the core functions can be a bit tricky in avr-gcc, due to how they all fit together and provide support for all micros with a single h file.

author: dannyf
author_rank: Super Contributor
timestamp: September 17, 2013, 02:54:04 pm
content: QuoteEveryone does that - it makes the code a lot easier to port and understand.

author: David_AVD
author_rank: Super Contributor
timestamp: September 17, 2013, 09:13:54 pm
content: Quote from: mojo-chan on September 17, 2013, 01:34:14 pmI know how to get to the header file, but that doesn't show me the nuts and bolts of the actual implementation.

author: westfw
author_rank: Super Contributor
timestamp: September 18, 2013, 12:12:50 am
content: http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/libc/misc/eerd_byte.S?root=avr-libc&view=markupAs Psi said, it's rather obscured by the bits that allow the same source tree to support so many chips and sub-architectures. Note that avr-libc is not maintained by Atmel; it's an open source software thing (and they look a bit aggressively non-gnu as well, which is a good thing for embedded software.) I doubt that the source is included in AS. For Xmega, I think there are similar functions as part of "Atmel Software Foundation" (http://asf.atmel.com/docs/2.9.0/xmegaa/html/group__nvm__eeprom__group.html), and THAT source might be included as part of AS.Older versions of avr-libc (for instance, the version shipped with Arduino) actually implement eeprom_read_byte() in the .h file, as an inline function.QuoteAllow me to introduce y'all to the "ID Database" utility:http://www.gnu.org/software/idutils/manual/idutils.htmlThis is sort-of a bunch of those IDE "find implementation" capabilities in a zippy little command-line utility.So you pull your svn copy of avr-libc/trunk, connect to the top-level directory and say "mkid", and it builds a database of all the identifiers in all the source files in all the directories (recursively.) Then you can search and gets lists of files that contain the identifier, and etc. Combined with things like eTags, this is one of the things that prevents old-timers from getting excited about "modern IDEs."

author: happylex
author_rank: Contributor
timestamp: March 10, 2014, 07:59:39 pm
content: Quote from: Psi on September 16, 2013, 03:30:31 amOk, so the XMEGA A is capable of using 32 bit pointers, since this can access all my megabytes of external SDRAM:Code:uint32_t my_own_pointer= 0x00010000; // > 0xFFFF, and thus > 16 bit :-)uint8_t value = 5;*(uint8_t *)my_own_pointer = value;Great this works, but then there is the point mentioned by mojo-chan.Quote from: mojo-chan on September 16, 2013, 08:58:41 amYes I found out. How difficult can it be? There was a statement from AVR that 16 bit pointers will not be supported till GCC 4.7. Now I am already using 4.8.2 and still it uses 16 bit pointers.I couldn't find the source indeed, or else I would have changed the uint16_t into uint32_t. (Or am I missing something complex here?)So I should write my own allocate() bla bla bla? I am usin three arrays to divide my SDRAM in sectors. Structures can then access the arrays to find a free sector. The speed of my software is going down a lot this way! And also my flash memory and valuable real life time. Moreover, I am not using my RAM efficiently with free memory in every sector.How do I solve the speed and efficiency issues? It must be possible, since the custom '32 bit pointer' works.Edit: Here is an old version of the AVR-LIBC with a malloc.c I could addapthttp://download.savannah.gnu.org/releases/avr-libc/However, if I use my custom pointer in an existing function it converts it to 16 bit.