[SDCC] Storing your configuration in EEPROM

In a previous post I have documented how to write to and read from EEPROM in the PIC 18F2550.

For the new revision of CapToolKit I wanted to store all user-modifiable configuration variables in EEPROM. The user can change these settings over a plain-text serial interface. Typing "S↵" should store everything to EEPROM while "R↵" should restore all variables to the values stored in the EEPROM.
Here's what I've done (many variables removed for brevity). Im sure there are many similar or better solutions for this task – if you happen to know one please let me know.

// config.c
// (c) 2008 Raphael Wimmer
// licensed under GPL v2 or later

// do not include "configuration.h"
#include "eeprom.h"
#include  <stdio.h>

#ifndef STORAGE
        #define STORAGE 24
        // keep this also in sync with config.h!
        // of course we could use all 256 bytes of EEPROM.
        // but we also have to keep the whole array in RAM
#endif // STORAGE

union {
        struct {
                unsigned int  device_id;
                volatile unsigned char use_leds;
                volatile unsigned char bm_led_state;
                volatile unsigned char bm_led_blink;
                volatile unsigned int drift_offset[8];
        }; // SUM: 21 bytes
        struct {
                unsigned char eeprom_data[STORAGE];
        };
} cfg;

void save_cfg(){
        unsigned int i;

        printf("Saving config to EEPROM:rn");
        for (i=0; i < STORAGE; i++){
                printf(".");
                ee_write_byte(i, &cfg.eeprom_data[i]);
        }
        printf(" donern");
}

void restore_cfg(){
        unsigned int i;

        printf("Restoring config from EEPROM:rn");
        for (i=0; i < STORAGE; i++){
                printf(".");
                ee_read_byte(i, &cfg.eeprom_data[i]);
        }
        printf(" donern");
}

And the header file:

// config.h
// (c) 2008 Raphael Wimmer
// licensed under GPL v2 or later
#ifndef _CONFIG_H
#define _CONFIG_H

#ifndef STORAGE
        #define STORAGE 24
        // keep this also in sync with config.c!
        // of course we could use all 256 bytes of EEPROM.
        // but we also have to keep the whole array in RAM
#endif // STORAGE

extern union {
        struct {
                unsigned int  device_id;
                volatile unsigned char use_leds;
                volatile unsigned char bm_led_state;
                volatile unsigned char bm_led_blink;
                volatile unsigned int drift_offset[8];
        };
        struct {
                unsigned char eeprom_data[STORAGE];
        };
} cfg;

void save_cfg();
void restore_cfg();

#endif // _CONFIG_H

As you can see all configuration variables are stored in a struct cfg. Thanks to union this struct occupies the same space in memory as the struct eeprom_data. Instead of writing every variable to a specified EEPROM location I write the whole eeprom_data array. Restoring the variables is done the same way.
Be aware that the union definition in config.c and config.h is different:
The keyword extern in the header file tells the compiler that this union will be available from some other object file (i.e. config.o) when linking.

Advantages:
[*] no need to assign EEPROM positions manually
[*] no danger of assigning the same EEPROM byte twice.
[*] easily extensible
[*] storing/restoring works either completely or not at all – this avoids stupid bugs.

Disadvantages:
[*] array size should be adapted to your needs. you usually don't need 256 bytes of config data – and you usually don't want to spend 25% of your RAM for it.
[*] config.c and config.h have to be synchronized each time you add a variable
[*] no simple way to store/restore single variables

One caveat: C does not guarantee that the members of a struct in memory are in the same order as in your code. You can not expect that the first byte the struct occupies is also (part of) the first variable in your struct.
AFAIK, SDCC does order the variables in memory in the order you wrote them. But don't expect it or any other C compiler to behave this way.
For my example this caveat does not apply. I do not care in which order the variables are in memory. All this code requires is that the variables stay at the same place in memory all the time. Lacking a MMU the PIC does not have any other choice.

If you want to define EEPROM contents in your hex-file:

typedef unsigned char eeprom;
__code eeprom __at 0x2100 __EEPROM[] = { 0xAA, 0x1E, 0x00, 0x01, 0x0A, 0x64, 0x1E, 0x01 };

Thanks to arkadi for this tip!

Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: