Access EEMEM variables from multiple c files

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

Hi. I'm working on a project where I need to store some variables in EEPROM. My project structure looks like this:

 

global.h - global variables, counters, flags used by interrupts and the main loop, pin aliases

global.c - extern definitions

init.h - function declaration for initializing functions: init_interrupts, int_lcd, init_variables_from_eeprom*

init.c - implementation

interrupt.c - interrupt handlers

main.c - main loop

 

The main() looks like this:

int main()
{
    init_lcd();
    init_interrupts();
    init_variables_from_eeprom();
    
    sei();
    
    while(1)
    {
        ...
    }
}

So in global.h I have some variables which are used in main.c, init.c and interrupt.c.

 

extern volatile uint8_t EEMEM my_eeprom_variable;

I have a second variable:

volatile uin8_t my_volatile;

Both of these variables are incremented in the same code snippet, one after other, but when I print them on LCD, the my_volatile increments and the my_eeprom_variable remains 0;

 

What I have done wrong?

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How are you writing and reading the EEPROM? You haven't told us the most important bits of information.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

You are doing everything correctly.   i.e. declaring extern volatile and #including this information in each source file.

 

So if you have a problem,   it sounds very interesting.   Zip up your complete project i.e. .C + .H + .CPROJ files and attach to a message.

 

David.

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

So in global.h I have some variables which are used in main.c, init.c and interrupt.c.

Just checking but the extern in the header does have ONE instantiation in some C file doesn't it?

 

(I guess it must have or you would be getting a link error).

 

EDIT:

Both of these variables are incremented in the same code snippet

Just a minute  - that does not make sense. You cannot just:

int EEMEM n = 37;

while(1) {
    n++;
}

if that's what you are talking about? You would need something like this:

int EEMEM n = 37;
int n_in_RAM;

while(1) {
    n_in_RAM == eeprom_read_word(&n);
    n_in_RAM++;
    eeprom_write_wrod(&n, n_in_RAM);
    // and to see it:
    LCD_print_var(n_in_RAM);
}

Last Edited: Wed. Feb 4, 2015 - 09:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My eemem variable is written immediately after increment using this command: eeprom_write_word(&my_eeprom_variable, my_eeprom_variable);

If I declare everything in main.c, it works. I can read from eeprom using var = eeprom_read_word(&var). I write back with eeprom_write_word(&var, var). And it works because after a restart, the var has the correct value.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My eemem variable is written immediately after increment using this command: eeprom_write_word(&my_eeprom_variable, my_eeprom_variable);

But you cannot do that - &my_eeprom_variable is nothing more than an address in the EEPROM chip. You cannot use it like a "real" RAm based variable. You Have to read what is at the address of my_eeprom_variable into something that can be updated, then update it, then write that cached copy back again.

 

All that:

volatile uint8_t EEMEM my_eeprom_variable;

is asking the linker to "set aside 8 bits in the EEPROM for me please and let me refer to that location 0x1234 as "my_eeprom_variable" or whatever". Look again about what I said of "n" in my example above. You cannot do things like "n++". Instead you must eeprom_read_*() the contents of the location into something that can be updated (using code underneath eeprom_read*() that knows about EEAR, EEDR and so on). Once the item is in registers or RAM it can be operated on. When the update is finished it must then be explicitly written back.

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

The write to eeprom will take a relatively long period of time. (ms) so you need to make sure the write has completed before reading. 

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

I will check tomorrow and I will post the results :)

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

The write to eeprom will take a relatively long period of time. (ms) so you need to make sure the write has completed before reading. 

As he is accessing using the eeprom_read_*() and eeprom_write_*() routines in AVR-LibC is this really a concern. I thought the code within specifically waited for readiness?

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

Hi. I made the changes. Here is the relevant code snippet:

// global.h
extern uint16_t EEMEM my_eevar_address;
extern volatile uint16_t my_eevar;
// global.c

uint16_t EEMEM my_eevar_address = 0;
volatile uint16_t my_eevar = 0;
// reading from EEPROM
my_eevar = eeprom_read_word(&my_eevar_address);

// writing to eeprom if required
eeprom_update_word(&my_eevar_address, my_eevar);

Is the volatile qualifier required for my_eevar_address? I bet it isn't because if it's there I get warnings (warning: passing argument 1 of ‘__eeupd_word_m8’ discards ‘volatile’ qualifier from pointer target type [enabled by default]), but if I remove, the code still works. I mean the my_eevar is saved and restored correctly. Anyway thanks for help :)

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

Is the volatile qualifier required for my_eevar_address?

No because you will make an access using eeprom_access*() routines and they will force an access so the "volatile" is implied.

 

The interface to routines such as eeprom_read_word() and eeprom_update_word() is:

uint16_t 	eeprom_read_word (const uint16_t *__p) __ATTR_PURE__
void 	eeprom_write_word (uint16_t *__p, uint16_t __value)

The pointers and values there are not "volatile" so if you pass volatile you are bound to be warned about it being discarded.

 

While completely pointless you could still use volatile then simply typecast it away as you pass things to these functions but that rather negates the reason for it being there in the first place.

 

Talking of which what made you think these things needed to be "volatile" anyway? You would only usually use it for something that is potentially accessed in two separate paths of execution.

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

It seems to me that you've misunderstood EEMEM, based on your choice of variable names:

uint16_t EEMEM my_eevar_address = 0;
volatile uint16_t my_eevar = 0;

Yes this fine, and the way in which you are accessing both variables is correct, but it belies what I think may be a misunderstanding.

 

What you have called my_eevar_address is not an address at all.  It is in fact a variable in EEPROM.

 

When you call a function like:

eeprom_read_word(&foo);

... you are passing the address of foo, but you are doing by way of the unary pointer operator '&'.  So foo is the variable, but &foo is the address of that variable.  In the case of a variable in EEPROM as declared by EEMEM, that address is an EEPROM address.

 

Names more in keeping with the nature of these variables might be:

// global.h
extern uint16_t EEMEM my_eevar;
extern volatile uint16_t my_eevar_sram_copy;
// global.c

uint16_t EEMEM my_eevar = 0;
volatile uint16_t my_eevar_sram_copy = 0;
// reading from EEPROM
my_eevar_sram_copy = eeprom_read_word(&my_eevar);

// writing to eeprom if required
eeprom_update_word(&my_eevar, my_eevar_sram_copy);

Another thing to keep in mind is that when you initialize an EEMEM variable the compiler places that initialisation into a section called .eeprom.  In fact, that's all EEMEM itself actually does:

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

After building your code, that section can be carved out of the .elf file into .hex file and programmed into the EEPROM memory of your target, in the same way in which the code itself is carved out of the .elf file into a .hex file and programmed into the flash memory of your target.  It's important to know that unless you program the EEPROM memory, any initialisation of EEMEM variables will not be performed.  Changes to EEMEM variables through the use of the eeprom_write_*() and eeprom_update_*() functions will be unaffected.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Joey,

 

I think it was probably my bad explanation in post #6 (ironically the "solution") that has lead to his understanding. The point I was trying to make is that when you:

int EEMEM e_foo;

you never use it in the traditional variable sense of things. You don't do things like:

e_foo = 51;
//or
if (e_foo > 17) ...

so you never assign or retrieve a value from it using normal operators. So the only real reason you defined it "like" a variable is that you want the linker to give an address to some named location and (because of "EEMEM") the address space it is allocated from is EEPROM. Then in use it's only ever the address of it that is used to eeprom_update_word() or eeprom_read_word().

 

So in that sense it's only being used for its address, not like a "normal" variable.

 

But once again I probably haven't done a very good job of getting that point across and the temptation might be to misread this to think it's "holding" address information whereas it's very existence is what "holds" its address and that's all the eeprom*() routines are really interested in.

 

(I appear to be digging an increasingly large hole!).

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

I named it "address" because in my understanding, it substitutes the address parameter which normally should be specified by the programmer.

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

I thought it is required because it is required for normal variables as well to share them across different functions. First I thought if I omit the volatile qualifier, the eemem variables would point to sifferent locations in every functions hence normal variables would do that without volatile. Silly me.

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

szabolcsx wrote:
I thought it is required because it is required for normal variables as well to share them across different functions. First I thought if I omit the volatile qualifier, the eemem variables would point to sifferent locations in every functions hence normal variables would do that without volatile.
volatile is generally used when a variable is shared between an ISR and an interruptable portion the main program.

extern assists in the sharing of global variables between files.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?