Основная проблема, с которой я обращаюсь в небольшом встроенном редизайне устройства (ПИД-контроллер), - это хранение параметров устройства. Старое решение, которое я здесь частично представляю, было эффективным с точки зрения пространства, но неуклюжим для поддержания при добавлении новых параметров. Он был основан на параметре идентификатора устройства, который должен был соответствовать й адрес EEPROM, как в примере, приведены ниже:Структура параметров EEPROM для небольшого встроенного устройства
// 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
был жестко Каждый адрес, и следующий адрес был определен в зависимости от предыдущего размера данных (обратите внимание на неравномерность расстояние между адресами). Это было эффективно, поскольку хранилище данных EEPROM не было потрачено впустую, но было сложно развернуть без появления ошибок.
В других частях кода (т.е. HMI меню, для хранения данных ...) код будет использовать список параметров, соответствующий адреса только что, что-то вроде следующего:
// 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
...
Затем в коде, используя те параметры, например, в меню пользователя structrues приведены ниже, я построил предметы, используя собственный тип PARAM (структуру):
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;
Теперь список параметров строятся как массив структур:
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},
...
В сущности, каждый параметр имеет уникальный идентификатор и этот идентификатор должен совпадать с жестко запрограммированный адрес EEPROM. Поскольку параметры не были фиксированы по размеру, я не мог использовать сам ID параметра как EEPROM (или другой носитель). Организация EEPROM в приведенном выше примере была 16-битным словом, но это не имеет значения, в принципе (больше места тратятся на голец, так что я предпочел бы 8-разрядную организацию в будущем так или иначе)
вопроса:
Есть ли более элегантный способ сделать это? Некоторые хеш-таблицы, хорошо известный шаблон, стандартное решение для подобных проблем? Сейчас EEPROMS намного больше по размеру, и я бы не прочь использовать фиксированный размер параметра (теряя 32 бита для логического параметра) в обмен на более элегантное решение. Похоже, что с фиксированными параметрами размера я мог бы использовать ID параметра в качестве адреса. Есть ли очевидный недостаток в этом методе, который я не вижу?
Теперь я использую распределенные HW (HMI, I/O и главный контроллер), и я хотел бы использовать структуру, в которой все устройства знают об этой структуре параметров, так что, например, удаленный ввод-вывод знает, как масштабировать входные значения, и HMI знает, как отображать и форматировать данные, все они основаны только на идентификаторе параметра. Другими словами, мне нужно одно место, где будут определены все параметры.
Я сделал свое исследование Google, очень мало можно было найти для небольших устройств, не содержащих некоторых баз данных. Я даже думал о каком-то определении XML, которое создало бы некоторый C-код для моих структур данных, но, возможно, было какое-то изящное решение, более подходящее для небольших устройств (до 512 K Flash, 32 K RAM)?
Спасибо, это хорошее предложение, которое устраняет зависимость от конкретной организации хранения. Я использую FreeRTOS, поэтому то, что вы называете объектом «eeprom», было бы в моем случае некоторой задачей, получающей сообщения с параметром ID и командой (сохранение, получение, проверка, резервное копирование ...) и обращение с хранилищем в EEPROM (ы) или любых других носителях. Я мог бы иметь все структуры параметров с постоянными данными (по умолчанию, min, max ...), хранящиеся в этом неизменяемом объекте, и я должен убедиться, что любая задача, требующая специфики внутренней организации данных, должна спросить этот объект (или задачу) параметра, который он должен знать. –
И да, наличие версии для идентифицированных данных является обязательным, иначе всевозможные плохие вещи могут произойти, если какая-то старая EEPROM была использована на новой плате или во время обновления FW на том же устройстве. Старая версия просто переписывала данные со значениями по умолчанию из поля «по умолчанию» для каждого параметра, безопасным, но требующим много времени, когда мы хотели сохранить конфигурацию клиента. Устройство было основано на EPROM, поэтому во время операции не было обновлено прошивка во время работы, что сегодня считается стандартным. –