Saving the values in EEPROM

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

Hi,

 

I've been working on a project where I need to program a clock, read the values from a sensor, and create an alarm clock. I also need to save the values Alarm values in EEPROM, in case there's a reboot or the power goes out. I think I am done with almost all of it, but I just need to program something in the beginning so the program knows when to load the values from my CPP file.

 

My current approach is I create a count variable and have it increment. If the value is greater than 1, it loads the value. I am not sure if it would work, because I also initialize the value with 0 at the beginning and that's kind of bugging me right now. I'd be grateful if any of you could have a look at my "main" and let me know if it looks okay? If not, how else could I do it? Thank You in advance

 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include "screen.h"
#include "DigiPort.h"
#include "Timer.h"

static void ticker_clock(void);
static void ticker_sensor(void);

// Initializations
Timer16 ticker (TC1, ticker_clock);
Timer16 ticker2 (TC2, ticker_sensor);
DigiPortRaw keys (PK, SET_IN_PORT);

// Count Variable for EEPROM
uint8_t count = 0;

Clock TheClock;
Sensor TheSensor;

int main(void)
{
	sei ();

	TheSensor.headline();

	eeprom_update_byte((uint8_t *) 1, count);				// Write the number stored in count in Memory cell 1
	count = eeprom_read_byte((uint8_t *) 1);				// Read the number stored in Memory cell and store in count	

	if(count >= 1) {
		TheClock.show();									// It's only purpose is it show the alarm values saved in eeprom
		TheSensor.show();
	}

	count++;
	ticker.start_ms(1000);
	ticker2.start_ms(5000);

    while (1)
    {
		switch(keys.read_raw()) {
			case 0b00000001:
				TheClock.set_time(1);
				break;
			case 0b00000010:
				TheClock.set_time(2);
				break;
			case 0b00000100:
				TheClock.set_time(3);
				break;
			case 0b00001000:
				TheClock.set_time(4);
				break;
			case 0b00010000:
				TheClock.set_time(5);
				break;
			case 0b00100000:
				TheSensor.set_temp_alarm();
				break;
			case 0b01000000:
				TheSensor.convert_unit();
				break;
			case 0b10000000:
				TheSensor.clear_temp_alarm();
				TheClock.clear_alarm();
				break;
		}

    }
}

void ticker_clock() { TheClock.callback();}
void ticker_sensor() { TheSensor.get_measurement();}

 

Last Edited: Sun. Dec 12, 2021 - 11:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The design doesn't seem to make a lot of sense right now. Yes you might want to write an initial value to the EEPROM but not EVERY rime the program starts. You should start by reading what's there first. Only if it appears to be "uninitialized" (which often means it's found to hold 0XFF (the value in "erased" EEPROM) might you then want to write an initial value.

 

But maybe consider

 

a) using a "cookie" to prove validity

b) preseeding EEPROM using an EEP file 

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

clawson wrote:

The design doesn't seem to make a lot of sense right now. Yes you might want to write an initial value to the EEPROM but not EVERY rime the program starts. You should start by reading what's there first. Only if it appears to be "uninitialized" (which often means it's found to hold 0XFF (the value in "erased" EEPROM) might you then want to write an initial value.

 

But maybe consider

 

a) using a "cookie" to prove validity

b) preseeding EEPROM using an EEP file 

 

Thank You! I'm probably gonna have to do a little reading on the EEP file, but I'll look it up right away. As far as initializing it only when it hasn't been done yet is concerned, if I am not mistaken something like this would work too:

int main(void)
{
	sei ();
	
	TheSensor.headline();

	
	count = eeprom_read_byte((uint8_t *) 1);	// Read the number stored in Memory cell and store in count	
	
	if (count == 0xFF) count = 0;			// Check if count has been initialized yet. If not, initialize it with 0
	
	if(count >= 1) {
		TheClock.show();		        // It's only purpose is it show the alarm values saved in EEPROM
		TheSensor.show();
	}
	
	count++;						// Increment count so the if statement is fullfilled the next time around
	eeprom_update_byte((uint8_t *) 1, count);		// Write the number stored in count in Memory cell 1
	
	ticker.start_ms(1000);
	ticker2.start_ms(5000);
    

 

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

I couldn't easily write code when I replied last night as I was using a mobile. The point about EEP is this:

#include <avr/eeprom.h>

int EEMEM eeCookie = 12345;
unsigned long eeVar = 0xBABEFACE;
unsigned long varToUse;

int main(void) {
    if (eeprom_read_word(&eeCookie) != 12345) {
        // eeprom has not been programmed/written yet
        eeprom_update_dword(&eevar, 0xBABEFACE);
        eeprom_update_word(&eeCookie, 0xBABEFACE);
        varToUse = 0xBABEFACE;
    }
    else {
        varToUse = eeprom_read_dword(&eeVar);
    }
    // make use of eeVar, if it changes...
    eeprom_update_dword(&eeVar, varToUse);
}

So you build this program and when the building is complete you will find you have files called progname.hex (as you are probably already familiar with) and also progname.eep. The latter contains those initial values (12345 and 0xBABEFACE) for the EEPROM locations labelled eeVar and eeCookie.

 

Now you have a choice. You could use your ISP/JATG/UPDI/whatever programmer to program BOTH the .hex file into the flash and the .eep file in to the EEPROM (in fact the .eep file is also HEX format but, by convention, it's extension is changed to "eep" to remind the user it is EEPROM data not program code).

 

If you do program both files then when this program starts the EEPROM location called eeCookie will already contain 12345. So the program starts, does that first read of "eeCookie" and finds it has 12345 this shows the EEPROM must already have been programmed. So it is valid to simply do an eeprom_read_dword() (in this example) to read the real data and know that because the 12345 was already in place the actual eeVar must also hold something valid.

 

If, however you did not program the AVR with the .eep file (into the EEPROM) but only programmed the .hex file then (unless you have set the EESAVE fuse!) during the programming of the .hex the programer software will have issued a "chip erase" and that will not only have wiped all the flash back to 0xFFFF but all the EEPROM will be erased to 0xFF too. So the value in the eeCookie location will be 0xFFFF and, interpreted as "int" this will appear to be -1 (If you observe it as decimal). -1 is not 12345 so when the program starts it will read the eeCookie location and find that it does not contain 12345. That means the EEPROM has not been used or set up in any way yet. So this time it will perform the conditional code. It will write an initial value (0xBABEFACE) to the eeVar variable and it will then validate the EEPROM by writing a recognisable 12345 to the eeCookie location.

 

Next time the AVR starts up it will read eeCookie again. This time it will be 12345 (whether things were initialised by EEP or because that original setup stuff ran last time). So the code will not do the "initial setup" stuff again but will just go on to read eeVar and use whatever it finds there.

 

Later on something might happen to change the value that should be held in eeVar. So then the code would do an eeprom_update_dword() to write it's new value.

 

So this program should have all the bases covered whether you want to use EEP to pre-seed the EEPROM with valid data or whether you want to let the program itself set stuff up at runtime.

 

If you did choose to use the EEP route then you may not want or need the code that checks the cookie and writes initial values as it wastes program space and, in theory, would never execute anyway.

 

You might also choose to put all the EEPROM variables together in a single struct (to guarantee their layout) and in this case you could just add the "cookie" as one of the struct members.

 

Oh and above I touched on EESAVE. The fact is that if you don't use it (it is inactive by default) then each time you program the .hex, during the "chip erase" that this involves, the EEPROM will also be erased back to all 0xFF every time. If, during development, you want to keep rewriting the .hex but maintain what has previously been written in EEPROM then you may want to activate EESAVE.

 

Oh and you probably spotted this already but, unlike you with code such as:

eeprom_read_byte((uint8_t *) 1)

I use:

EEMEM type var;

...

    something = eeprom_read_<width>(&var);

so I'm not choosing "where should "var" be located - I think I'll put it at location 1". Instead, just like you do with variables in RAM, I am letting the linker choose a location for "var". I mean you don't usually use RAM variables in the fashion:

int main(void) {
    *(int *)0x107 = 12345;
}

you use something like:

int count;

int main(void) {
    count = 12345;
}

and the linker, if it chooses, might decide to put "count" at 0x107 but you never get your hands dirty worrying about this. So why do the same thing for EEPROM? The danger, if not using named and linker placed variable in EEPROM of doing stuff liike:

eeprom_update_dword(3, 0xDEADBEEF);
eeprom_update_byte(6, 'A');

is that I missed the fact that location 6 overlaps the 3,4,5,6 locations of the first write - in effect, if you start to do the linker's job you can easily make mistakes about the location of stuff in EEPROM.

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

Thank You so much for the help!! I modified my code a little, and I think it should work now. I'll test it in the lab on Friday. and I'll definitely keep EEP files in mind the next time! :)