The main issue I am addressing in the small embedded device redesign (PID controller) is the device parameters storage. The old solution I partially present here was space efficient, but clumsy to maintain when new parameters were added. It was based on the device parameter ID that had to match th EEPROM address like in an example given 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
Every address was hardcoded, and the next address was defined depending on the previous data size (note the uneven spacing between addresses). It was efficient as no EEPROM data storage was wasted, but diffcult to expand without introducing bugs.
In other parts of the code (i.e. HMI menus, data storage...) the code would use parameter list matching the addresses just given, something like the following:
// Parameter identification, NEVER USE 0 (zero) as ID since it's 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 those parameters, for example in the user menu structrues given below, I have built items 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's media)
};
typedef struct param PARAM;
Now the list of parameters is built as 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, every parameter has it's unique ID, and this ID had to match the hardcoded EEPROM address. Since the parameters were not fixed in size, I could not use the parameter ID itself as an EEPROM (or other media) address. The EEPROM organization in the example above was 16-bit word, but it does not matter in principle (more space is wasted for chars so I would prefer 8-bit organization in the future anyway)
The question:
Is there a more elegant way to do this? Some hash table, well known pattern, standard solution for similar problems? EEPROMS are much larger in size now, and I would not mind using the fixed parameter size (wasting 32 bits for boolean parameter) in exchange for more elegant solution. It looks like with fixed size parameters, I could use the parameter ID as the address. Is there an obvious downside in this method that I do not see?
I am now using the distributed HW (HMI, I/O and main controller are separated), and I would like to use the structure in which all devices know about this parameter structure, so that for example remote I/O knows how to scale input values, and the HMI knows how to display and format data, all based only on the parameter ID. I other words, I need single place where all parameters would be defined.
I did my Google research, very little could be found for small devices not icluding some data bases. I was even thinking about some XML definition which would generate some C code for my data structures, but maybe there was some elegant solution more appropriate for small devices (up to 512 K Flash, 32 K RAM)?