EEPROM parameter structure for small embedded devices

The main problem that I deal with in a small integrated device redesign (PID controller) is the storage of device parameters. The old solution, which I partially present here, was space-efficient, but awkward to maintain when adding new parameters. It was based on the identifier of the device parameter, which must match the EEPROM address, as in the example below:

// EEPROM variable addresses #define EE_CRC 0 // EEPROM CRC-16 value #define EE_PROCESS_BIAS 1 // FLOAT, -100.00 - 100.00 U #define EE_SETPOINT_VALUE 3 // FLOAT, -9999 - 9999.9 #define EE_SETPOINT_BIAS 5 // CHAR, -100 - 100 U #define EE_PID_USED 6 // BYTE, 1 - 3 #define EE_OUTPUT_ACTION 7 // LIST, DIRE/OBRNU #define EE_OUTPUT_TYPE 8 // LIST, GRIJA/MOTOR #define EE_PROCESS_BIAS2 9 // FLOAT, -100.00 - 100.00 U #define EE_SETPOINT_VALUE2 11 // FLOAT, -9999 - 9999.9 #define EE_SETPOINT_BIAS2 13 // CHAR, -100 - 100 U #define EE_PID_USED2 14 // BYTE, 1 - 3 #define EE_OUTPUT_ACTION2 15 // LIST, DIRE/OBRNU #define EE_OUTPUT_TYPE2 16 // LIST, GRIJA/MOTOR #define EE_LINOUT_CALIB_ZERO 17 // FLOAT, -100.0 - 100.0 #define EE_LINOUT_CALIB_GAIN 19 // FLOAT, -2.0 - 2.0 

Each address was hard-coded, and the next address was determined depending on the previous data size (note the uneven distance between the addresses). This was effective because the EEPROM data store was not wasted, but it was difficult to deploy without errors.

In other parts of the code (i.e. the HMI menu, data storage ...), the code will use a list of parameters corresponding to the addresses just indicated, something like the following:

 // Parameter identification, NEVER USE 0 (zero) as ID since it NULL // Sequence is not important, but MUST be same as in setparam structure #define ID_ENTER_PASSWORD_OPER 1 #define ID_ENTER_PASSWORD_PROGRAM 2 #define ID_ENTER_PASSWORD_CONFIG 3 #define ID_ENTER_PASSWORD_CALIB 4 #define ID_ENTER_PASSWORD_TEST 5 #define ID_ENTER_PASSWORD_TREGU 6 #define ID_PROCESS_BIAS 7 #define ID_SETPOINT_VALUE 8 #define ID_SETPOINT_BIAS 9 #define ID_PID_USED 10 #define ID_OUTPUT_ACTION 11 #define ID_OUTPUT_TYPE 12 #define ID_PROCESS_BIAS2 13 ... 

Then, in code using these parameters, for example, in the following user menu structures, I created elements using my own PARAM type (structure):

 struct param { // Parametar decription WORD ParamID; // Unique parameter ID, never use zero value BYTE ParamType; // Parametar type char Lower[EDITSIZE]; // Lowest value string char Upper[EDITSIZE]; // Highest value string char Default[EDITSIZE]; // Default value string BYTE ParamAddr; // Parametar address (in it media) }; typedef struct param PARAM; 

Now the list of parameters is built as an array of structures:

 PARAM code setparam[] = { {NULL, NULL, NULL, NULL, NULL, NULL}, // ID 0 doesn't exist {ID_ENTER_PASSWORD_OPER, T_PASS, "0", "9999", "0", NULL}, {ID_ENTER_PASSWORD_PROGRAM, T_PASS, "0", "9999", "0", NULL}, {ID_ENTER_PASSWORD_CONFIG, T_PASS, "0", "9999", "0", NULL}, {ID_ENTER_PASSWORD_CALIB, T_PASS, "0", "9999", "0", NULL}, {ID_ENTER_PASSWORD_TEST, T_PASS, "0", "9999", "0", NULL}, {ID_ENTER_PASSWORD_TREGU, T_PASS, "0", "9999", "0", NULL}, {ID_PROCESS_BIAS, T_FLOAT, "-100.0", "100.0", "0", EE_PROCESS_BIAS}, {ID_SETPOINT_VALUE, T_FLOAT, "-999", "9999", "0.0", EE_SETPOINT_VALUE}, {ID_SETPOINT_BIAS, T_CHAR, "-100", "100", "0", EE_SETPOINT_BIAS}, {ID_PID_USED, T_BYTE, "1", "3", "1", EE_PID_USED}, {ID_OUTPUT_ACTION, T_LIST, "0", "1", "dIrE", EE_OUTPUT_ACTION}, {ID_OUTPUT_TYPE, T_LIST, "0", "1", "GrIJA", EE_OUTPUT_TYPE}, {ID_PROCESS_BIAS2, T_FLOAT, "-100.0", "100.0", "0", EE_PROCESS_BIAS2}, 

...

In essence, each parameter has a unique identifier, and this identifier must correspond to the hard-coded EEPROM address. Since the parameters were not fixed in size, I could not use the parameter ID itself as an EEPROM (or other medium). The EEPROM organization in the above example was a 16-bit word, but it doesn’t matter (more space is wasted on characters, so anyway, I would prefer an 8-bit organization in the future)

Question:

Is there a more elegant way to do this? Some hash tables, a well-known template, a standard solution for such problems? Now EEPROMS is much larger in size, and I would not mind using a fixed parameter size (losing 32 bits for a logical parameter) in exchange for a more elegant solution. It looks like with fixed size parameters, I could use the parameter ID as an address. Is there an obvious flaw in this method that I don't see?

Now I use distributed HW (HMI, I / O and the main controller are separate), and I would like to use a structure in which all devices know about this parameter structure, so for example, remote I / O knows how to scale the input values, and HMI knows how to display and format data, all of them are based only on the identifier of the parameter. In other words, I need one place where all parameters will be defined.

I did my research on Google, very little could be found for small devices that do not contain some databases. I even thought of some XML definition that would create some C code for my data structures, but maybe there was some elegant solution more suitable for small devices (up to 512K Flash, 32K RAM)?

+4
source share
3 answers

I'm not sure if this is really better than yours, but here is the idea. For ease of maintenance, consider encapsulating knowledge of EEPROM addresses in an eeprom object. Right now you have a parameter object, and each instance knows where its data is stored in the physical EEPROM. It might be easier to maintain if the parameter object did not know the EEPROM. And instead, a separate eeprom object was responsible for the interaction between the physical EEPROM and the parameter object instances.

Also, consider adding the version number for the EEPROM data to the data stored in the EEPROM. If the device firmware is updated and the EEPROM data format is changed, this version number allows the new firmware to recognize and convert the old version of the EEPROM data.

+1
source

If you are not worried about compatibility between changes or processors, you can simply copy the structure between RAM and EEPROM and only access individual members of the RAM copy.

You could also relatively easily create a tool that compiles a list of definitions from the structure and known packaging rules of your compiler if you want to make explicit access to individual members directly in the EEPROM.

+2
source

Here is what I would do.

I would create a typedef structure with the variables that you want to have in the EEPROM.

Using your example, it will look something like this:

 typedef struct eeprom_st { float process_biass; float setpoint_value; char setpoint_bias; .... } eeprom_st_t; 

Than I would create an offset definition to mark where the structure should be stored in the EEPROM.

And I would add a pointer to this type to use it as a dummy object:

 #define EEPROM_OFFSET 0 eeprom_st_t *dummy; 

What will I use offsetof to get the offset of a particular variable, I need like this:

 eeprom_write( my_setpoint_bias, EEPROM_OFFSET + offsetof(eeprom_st_t,setpoint_bias), sizeoff(dummy->setpoint_bias)); 

To make it more elegant, I would include the eeprom routine in the macro.

+2
source

Source: https://habr.com/ru/post/1468743/


All Articles