EEPROM variable reset to default value after MCU restart

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

Hey all. I have a problem where the variable stored in EEPROM does not retain its value across MCU restarts.

I will post my code below hoping you could spot my error.

(I am testing the program with proteus emulator and giving it initial EEPROM file as well if that is relevant. meaning I haven't programmed the actual chip )

MCU = mega32a

F_CPU = 16Mhz

 

/*
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <avr/eeprom.h>

#include "lcd.h"
#define DEFAULT_THRESHOLD 22


volatile char lcd_int_temp[8];
volatile uint8_t temperature;

uint8_t EEMEM threshold_eeprom = DEFAULT_THRESHOLD;
//volatile uint8_t threshold = 22;

uint8_t ADC_TO_TEMP(void);
void handleRelay(void);
void handleLCD(void);
void handleKeys(void);
uint8_t getThreshold(void);
void setThreshold(uint8_t threshold);

int main(void)
{

    DDRB = 0x00;
    PORTB = 0x07; // enable internal pull up on pb0 pb1 pb2

    DDRD = 0xFF; // Set PORTD as output
    PORTD = 0x0F;
   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescaler to 128 - 125KHz sample rate @ 16MHz
   ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
   // No MUX values needed to be changed to use ADC0
  //    ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading
   ADCSRA |= (1 << ADATE);  // Set ADC to Free-Running Mode
   ADCSRA |= (1 << ADEN);  // Enable ADC
   ADCSRA |= (1 << ADIE);  // Enable ADC Interrupt
   sei();   // Enable Global Interrupts
   ADCSRA |= (1 << ADSC);  // Start A2D Conversions

    // Insert code
    lcd_init(LCD_DISP_ON_CURSOR);


    while(1){

            handleKeys();
            handleRelay();
            //_delay_ms(10);
            //lcd_clrscr();
            lcd_gotoxy(0,0);
            //_delay_ms(5);
            handleLCD();
            //_delay_ms(10);

    }

    return 0;
}

ISR(ADC_vect, ISR_BLOCK){
    memset(&lcd_int_temp, 0, sizeof(lcd_int_temp));
    temperature = ADC_TO_TEMP();
    itoa(temperature,lcd_int_temp,10);

}

uint8_t ADC_TO_TEMP(){
    float temp_var = (ADCW * 100 ) / 204.8;
    return (uint8_t) temp_var;
}

void handleLCD(){
    char threshold_string[5];
    //_delay_ms(3);
    lcd_puts("Temp:   ");
    _delay_ms(3);
    lcd_gotoxy(6,0);
    lcd_puts(lcd_int_temp);
    _delay_ms(3);
    lcd_gotoxy(0,1);
    lcd_puts("Threshold:   ");
    lcd_gotoxy(10,1);
    _delay_ms(3);
    itoa(getThreshold(),threshold_string,10);
//    itoa((uint8_t)eeprom_read_byte(threshold_eeprom),threshold_string,10);
    //itoa(threshold,threshold_string,10);
    lcd_puts(threshold_string);
}
void handleRelay(){
    // If temp is higher than threshold turn of the heater
    // If temp is lower than the threshold turn on the heater
    //uint8_t threshold = (uint8_t) eeprom_read_byte(&threshold_eeprom);
    uint8_t threshold = getThreshold();
    if (temperature < threshold){
            PORTD = 0xF0;
    }else{
        PORTD = 0x0F;
    }
}

void handleKeys(){
    uint8_t keys = PINB;
    uint8_t temporary_threshold = getThreshold();
    if ((keys & (1 << PB0)) == 0){
        // increment threshold
        temporary_threshold += 1 ;
    }
    if ((keys & (1 << PB1)) == 0){
        // decrement threshold
        temporary_threshold -= 1 ;
    }
    if (temporary_threshold != getThreshold()){
            setThreshold(temporary_threshold);
    }

}// end of handleKeys

uint8_t getThreshold(){
    eeprom_busy_wait();
    uint8_t threshold = eeprom_read_byte(&threshold_eeprom);
    if (threshold != 0xFF){ // if eeprom cant be read...
        return threshold;
    } else {
        return DEFAULT_THRESHOLD;
    }

}// end of getThreshold

void setThreshold(uint8_t threshold){
    eeprom_busy_wait();
    eeprom_update_byte(&threshold_eeprom, threshold);
}

 

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

mr0099 wrote:
meaning I haven't programmed the actual chip
Well my first advice would be to come back after you have - this could well be a simulator usage problem.
mr0099 wrote:

uint8_t EEMEM threshold_eeprom = DEFAULT_THRESHOLD;

But just checking - you do realise what this line does do you? All that is going to happen is that when you build this code the compiler will create a variable in the ".eeprom" section and assign 22 to it. But that doesn't yet get the 22 value into the AVR. Even if you build this code and then program the programname.hex file into the AVR it *still* won't have putt 22 into the EEPROM. Only as a result of the fact that the build will also create a programname.eep file can you get the 22 into the EEPROM. When you ISP/JTAG the .hex file into the flash you also have to ISP/JTAG the .EEP file into the EEPROM of the AVR. Finally that ensures the 22 is now in the place where the program code may attempt to read it. But also know this. If you program the .EEP file and then the .HEX (in that order) then the EEP will put 22 there but when you program the HEX it will do a chip erase (all locations including EEPROM) returned to 0xFF values and then the program flash is programmed but in the process anything in the EEPROM was wiped. So you need to make sure the EESAVE fuse is set so that a chip erase does not wipe the EEPROM. (otherwise each time you change the program the EEPROM will be wiped).

 

So were you aware of all this. Have you arranged that during Proteus' simulation of device programming that it will be putting the .EEP file contents into the EEPROM part of the simulated AVR?

 

One common technique (which can avoid the need to EEP file all together) is to have the code in main() start by reading some key location in EEPROM. If it reads 0XFF (and that location is not normally expected to have 0xFF as a valid value) then you can deduce that the EEPROM is in the unprogrammed state so you simply have the code do eeprom_write*()s to write the actual defaults (like 22) so then you don't need separate initialiaztion - this also gets around the need for EESAVE (o some extent).

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

Thank you for your help.

Well, the simulator problem you mentioned could be the cause as it doesn't support .eep file per se so I used avr-objcopy to convert it to .bin format so proteus could understand it.

The exact command I used :

avr-objcopy -I ihex -O binary TempRelay.eep TempRelayEEPROM.bin

Maybe there has been some problem with that?

Yes, I do realise what the line would do. Also, As you can see I have a getThreshold() function which would read from EEPROM and if the value was 0xFF meaning the EEPROM was not programmed it returns the predefined value of DEFAULT_THRESHOLD else it returns the value read from EEPROM. (more sanity check could be used by I think for now it is good.)

 

Would you mind taking a quick look over the code and maybe spot some silly mistake of mine I'm not aware of?

P.S: As you suggested I'm going to build a simple prototype to try it on the actual chip. But I might need a few tweaks to as I don't have a 16Mhz crystal at the moment.( Convert to 8Mhz crystal ) 

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

More tips... never use eeprom location 0 because it can corrupt at bootup on legacy AVR, and always enable BOD.

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

12oclocker wrote:
More tips... never use EEPROM location 0 because it can corrupt at bootup on legacy AVR, and always enable BOD.

Hi. Thanks for the tips :)  about the first tip do I need to do anything or is avr/eeprom.h handling it automatically?

 

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

clawson wrote:

mr0099 wrote:
meaning I haven't programmed the actual chip
Well my first advice would be to come back after you have - this could well be a simulator usage problem.
mr0099 wrote:

uint8_t EEMEM threshold_eeprom = DEFAULT_THRESHOLD;

But just checking - you do realise what this line does do you? All that is going to happen is that when you build this code the compiler will create a variable in the ".eeprom" section and assign 22 to it. But that doesn't yet get the 22 value into the AVR. Even if you build this code and then program the programname.hex file into the AVR it *still* won't have putt 22 into the EEPROM. Only as a result of the fact that the build will also create a programname.eep file can you get the 22 into the EEPROM. When you ISP/JTAG the .hex file into the flash you also have to ISP/JTAG the .EEP file into the EEPROM of the AVR. Finally that ensures the 22 is now in the place where the program code may attempt to read it. But also know this. If you program the .EEP file and then the .HEX (in that order) then the EEP will put 22 there but when you program the HEX it will do a chip erase (all locations including EEPROM) returned to 0xFF values and then the program flash is programmed but in the process anything in the EEPROM was wiped. So you need to make sure the EESAVE fuse is set so that a chip erase does not wipe the EEPROM. (otherwise each time you change the program the EEPROM will be wiped).

 

So were you aware of all this. Have you arranged that during Proteus' simulation of device programming that it will be putting the .EEP file contents into the EEPROM part of the simulated AVR?

 

One common technique (which can avoid the need to EEP file all together) is to have the code in main() start by reading some key location in EEPROM. If it reads 0XFF (and that location is not normally expected to have 0xFF as a valid value) then you can deduce that the EEPROM is in the unprogrammed state so you simply have the code do eeprom_write*()s to write the actual defaults (like 22) so then you don't need separate initialiaztion - this also gets around the need for EESAVE (o some extent).

OK :) So building the prototype I can see that fortunately the EEPROM is working perfectly well and the problem was the simulation software.

however, I ran into a new problem Button Debouncing... I did a quick search and saw people were referring to "Dani's debounce code" quite a lot. could someone point some useful links to me regarding that?

 

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

mr0099 wrote:
I can see that fortunately the EEPROM is working perfectly well and the problem was the simulation software.

I seriously doubt that the authors of Proteus "forgot" to implement Programming of the EEPROM Area. I'm sure it's just a case of reading the manual.

 

You've received a very moderate response compared to the poor freak here: https://www.avrfreaks.net/forum/proteus-8-not-showing-eeprom-initialized-variables-using-eseg

 

Reading that thread - it seems Labcenter didn't do a good job of this feature.

 

  1. They didn't read the EEPROM contents out of the ELF file.
  2. They didn't implement the EESAVE fuse.

 

Instead you must manually load the EEPROM (as binary) from disk.

 

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

N.Winterbottom wrote:

mr0099 wrote:
I can see that fortunately the EEPROM is working perfectly well and the problem was the simulation software.

I seriously doubt that the authors of Proteus "forgot" to implement Programming of the EEPROM Area. I'm sure it's just a case of reading the manual.

 

You've received a very moderate response compared to the poor freak here: https://www.avrfreaks.net/forum/proteus-8-not-showing-eeprom-initialized-variables-using-eseg

 

Reading that thread - it seems Labcenter didn't do a good job of this feature.

 

  1. They didn't read the EEPROM contents out of the ELF file.
  2. They didn't implement the EESAVE fuse.

 

Instead you must manually load the EEPROM (as binary) from disk.

 

 

Yes, thank Eminem I wasn't that guy. Anyway, I programmed the AVR and it turned out to be OK after all :)  ( other than the button bouncing which I used a library to fix)