AVR-GCC: eeprom_read_byte() — why a pointer as argument?

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

eeprom_read_byte() is called with an argument that has to be a pointer. I would imagine this function should take the address argument and assign it to EEARH and EEARL. Then why does it need to be a pointer?

 

This topic has a solution.
Last Edited: Fri. Sep 29, 2017 - 10:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If it was a fixed address then how would you store something like a string.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

It is a pointer because EERAH and EERAL hold the address of the byte to be read or written. A pointer is just an address, isn't it? So, what you put in EERAH and EERAL IS a pointer.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Fri. Sep 29, 2017 - 07:03 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

paragliding wrote:
Then why does it need to be a pointer?
Have you read the manual or tutorials? The way you use EEPROM with avr-gcc is like this:

#include <avr/eeprom.h>

// variables located in EEPROM
int EEMEM n;
char EEMEM c;

int main(void) {
    eeprom_update_byte(&c, 'A');
    eeprom_update_word(&n, 12345);
}

How could those operate if the first parameter to the functions were not defined as a pointer? It would be like:

#include <stdint.h> 

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

void 	eeprom_update_byte (uint16_t address_offset, uint8_t __value);
void 	eeprom_update_word (uint16_t address_offset, uint16_t __value);

// variables located in EEPROM
int EEMEM n;
char EEMEM c;

int main(void) {
    eeprom_update_byte(&c, 'A');
    eeprom_update_word(&n, 12345);
}

which when built says:

avr.c:12:24: warning: passing argument 1 of 'eeprom_update_byte' makes integer from pointer without a cast [-Wint-conversion]
     eeprom_update_byte(&c, 'A');
                        ^
avr.c:5:7: note: expected 'uint16_t {aka unsigned int}' but argument is of type 'char *'
 void  eeprom_update_byte (uint16_t address_offset, uint8_t __value);
       ^
avr.c:13:24: warning: passing argument 1 of 'eeprom_update_word' makes integer from pointer without a cast [-Wint-conversion]
     eeprom_update_word(&n, 12345U);
                        ^
avr.c:6:7: note: expected 'uint16_t {aka unsigned int}' but argument is of type 'int *'
 void  eeprom_update_word (uint16_t address_offset, uint16_t __value);
       ^

So that is why the address is not just a number. To clear such warnings the user would then be required to do something like:

int main(void) {
    eeprom_update_byte((uint16_t)&c, 'A');
    eeprom_update_word((uint16_t)&n, 12345U);
}

which is awfully messy though I suppose it does allow for:

int main(void) {
    eeprom_update_byte(234, 'A');
    eeprom_update_word(456, 12345U);
}

but do you really want to have to decide a physical location for every variable in EEPROM and make sure they don't overlap? (you don't do that for variables in RAM!). As it stands you need to do the opposite:

int main(void) {
    eeprom_update_byte((void *)234, 'A');
    eeprom_update_word((void *)456, 12345U);
}

so on those unusual location when you decide the addresses you have to cast the numbers up to be pointers.

Last Edited: Fri. Sep 29, 2017 - 08:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you, Brian, ka7ehk, and clawson! I understand that any memory address to be read or written to is a pointer in the general sense. However, my question is specifically about pointer syntax in C. If I understand correctly, pointer syntax is designed for RAM access, so instead of using instructions like LDS/STS you could simply write *p, and let the compiler translate it as dictated by the architecture. However, EEPROM isn't RAM, and it's not in the same address space (well, at least on attinyX5). It seems to me using pointer syntax for EEPROM addressing is dangerous. For example, if eep (an EERPOM address) were declared as a pointer, and we tried *eep = 0xFF;, the result would be potentially disastrous since the compiler would write 0xFF to RAM.

 

So, going back to eeprom_read_byte(), what if I defined a similar function as follows (it's actually straight from ATtinyX5 datasheet):

unsigned char EEPROM_read(unsigned char ucAddress) {
    while(EECR & (1<<EEPE));    // wait for completion of previous write
    EEAR = ucAddress;           // specify eeprom address
    EECR |= (1<<EERE);          // start reading
    return EEDR;                // return data
}

Then I could call EEPROM_read with a plain address variable without using pointer syntax at all. It seems simple, intuitive and robust (no accidental *ucAddress assignments which would corrupt RAM!). Then why is eeprom_read_byte() written in a way that requires a C pointer?

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

paragliding wrote:
 If I understand correctly, pointer syntax is designed for RAM access

Not at all.

 

The 'C' programming language has no idea about different types of memory - RAM, ROM, whatever.

 

 

 

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

Well, ok, but as I said earlier, wouldn't *eep = 0xFF; be disastrous since the compiler would write this to RAM instead of EEPROM? Maybe it's just avr-gcc, but I'd assume if pointers were truly universal (i.e. applicable to different address spaces), then the compiler should be able to automatically translate the assignment using EEAR, EEDR, EECR as dictated by the memory type.

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

Read #4 again - note that the variables are specially qualified to indicate that they refer to EEPROM ...

 

EDIT

 

RTFM: http://www.atmel.com/webdoc/avrlibcreferencemanual/group__avr__eeprom_1ga79a42ec6c6c8bbbe6e34ed57a52aac59.html

 

EDIT 2

 

Also: http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

Last Edited: Fri. Sep 29, 2017 - 05:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ooooh! 

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

Sorry, I was getting it all wrong!  

Let's check if I'm starting to understand. If I do

char EEMEM *eep;

and then

*eep = 0xFF;

Would that be understood by avr-gcc properly? Is it actually how eeprom_read_byte() defined? Sorry, I haven't been able to find the source code for the function. I looked at eeprom.h but got totally lost.

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

Pointers hold the address of things. As Andy says there's nothing says only RAM. Most systems have the concept of writable memory, fixed memory and I/O. In some systems they are subsets of one large address space (PCs and Cortex A ARM). While in a Harvard architecture device such things may be in individual address spaces. But they all have the potential to need pointers. For AVR that may be RAM, flash, EEPROM, lockbits, fuses, chipID, calibration, etc but all may require some indexing. It's simply how you dereference those pointers that differs.

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

BTW I already told you how to use the existing eeprom_*() functions with integers not pointers if you can't get the concept of linker assigned addresses. Just cast your integers to void *. If this gets tedious define a shim (either preprocessor macro or static inline function) to wrap the existing functions with such a cast so you don't have to type it every time.

Last Edited: Fri. Sep 29, 2017 - 05:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't have a problem with pointers per se. I'm just trying to understand how exactly eeprom_read_byte() uses the pointer given to it in the argument. I think the problem is that I don't understand how eeprom_read_byte() is defined at all.

Edit: thanks for bearing with me! I feel that I'm getting really close to figuring it all out thanks to your help!

Last Edited: Fri. Sep 29, 2017 - 05:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You do know it's all open source?
.
EDIT and all data in a computer is just numbers (ultimately just the number 0 or 1) that includes all integers, floats, characters, pointers and whatever else so 1234 could be an integer number for the number of beans in a can, or an address of something in RAM pr EEPROM or whatever. Whether it finally turns into beans or object address is simply a question of interpretation at the point of use. Perhaps you use it in LD as a RAM address or in LPM as a flash address or EEARH/L as an EEPROM address or perhaps you put it in a couple of machine registers and subtract 40 as you just ate a big spoonful of beans!

Last Edited: Fri. Sep 29, 2017 - 06:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I looked at avr/eeprom.h. I found this:

 

#elif defined (__AVR_ATtiny85__)
# define _EEPROM_SUFFIX _tn85

#define _EEPROM_CONCAT1(s1, s2)     s1 ## s2
#define _EEPROM_CONCAT2(s1, s2)     _EEPROM_CONCAT1 (s1, s2)

#define eeprom_read_byte      _EEPROM_CONCAT2 (__eerd_byte, _EEPROM_SUFFIX)
 

So eeprom_read_byte becomes __eerd_byte_tn85__ in my case. But I don't understand what it is and where to look for it. So there goes my quest for the source code.

edit: typo

 

Last Edited: Fri. Sep 29, 2017 - 06:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have an out of date compiler. It no longer does EEPROM like that. And the .S files that implement it are on the AVR LIBC site.

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

I guess I haven't updated crosspack in while. I'll take a look at the avr libc webstie. Thanks for the pointer!

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

paragliding wrote:
So there goes my quest for the source code.

???  How many instructions could it be?  Make a tiny test program, and look at the generated code.  With my toolchain:

                __EEPROMRDB:
006634 99f9      	SBIC EECR,EEWE
006635 cffe      	RJMP __EEPROMRDB
006636 93ff      	PUSH R31
006637 b7ff      	IN   R31,SREG
006638 94f8      	CLI
006639 bda1      	OUT  EEARL,R26
00663a bdb2      	OUT  EEARH,R27
00663b 9af8      	SBI  EECR,EERE
00663c b5e0      	IN   R30,EEDR
00663d bfff      	OUT  SREG,R31
00663e 91ff      	POP  R31
00663f 9508      	RET
 

...which looks very much like the code in datasheets.  Then examine the calling sequence, to see how your C source is used.  The above is certainly not onerous if you really want to dig into it.  And if your eyes widen at a fragment like the above, then just use the API.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Well, that's how it compiles, but I wanted to know why the avr-gcc routine wanted a C pointer specifically. As I said earlier, I could simply use the following function in which the address variable isn't declared as a pointer

unsigned char EEPROM_read(unsigned char ucAddress) {
    while(EECR & (1<<EEPE));    // wait for completion of previous write
    EEAR = ucAddress;           // specify eeprom address
    EECR |= (1<<EERE);          // start reading
    return EEDR;                // return data
}

So I thought that maybe there was something fancy going on in the avr-gcc routine that really needed the compiler to know it was a pointer (such as for pointer dereferencing). I started digging in libc source per clawson's suggestion, and I found the following (I removed the conditionals and left only what works for attiny85 specifically):

#define    ret_lo    r24
#define    ret_hi    r25

ENTRY    eeprom_read_byte

1:  sbic    _SFR_IO_ADDR (EECR), EEWE
    rjmp    1b
    out    _SFR_IO_ADDR (EEARH), r25
    out    _SFR_IO_ADDR (EEARL), r24
    sbi    _SFR_IO_ADDR (EECR), EERE
    clr    ret_hi
    in     ret_lo, _SFR_IO_ADDR (EEDR)
    ret

Which isn't that different from the C function above except this function expects the eeprom address in registers r24 and 25. I haven't found any code that puts the function argument into these registers, so I'm thinking that  maybe that's where the pointer declaration comes into play (i.e. lib c reserves these two registers for the eeprom pointer). However, it's possible I am wrong about this. Again, what really bothers me is that eeprom_read_byte() requires a pointer in the argument, but it doesn't seem to use any of the syntax associated with pointers in C (like assignment via dereferencing).

Last Edited: Fri. Sep 29, 2017 - 09:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update: I overlooked something clawson said:

but do you really want to have to decide a physical location for every variable in EEPROM and make sure they don't overlap? (you don't do that for variables in RAM!).

With automatic variable address assignment in EEPROM there is indeed no other way but to use pointers. Now it makes sense to me. Sorry about the confusion — I always manage eeprom storage manually, so it didn't even occur to me there was another way. That's why it went over my head when I first read clawson's response.

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

Glad you finally got it!
.
I would suggest switching to using linker assigned addresses. It does not make mistakes about overlapping / collocating variables, which is the danger of doing it manually.

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

Thank you. I'll give it a try at some point. I do it manually primarily to spread out wear. But then my brain will likely fail long before the EEPROM with its 100k erase cycles...

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

The only way to mitigate wear is to have several copies of the same thing. Just manually locating does not help. You can create multiple copies using linker allocation.