我在小型嵌入式設備重新設計(PID控制器)中解決的主要問題是設備參數存儲。我在這裏部分介紹的舊解決方案是節省空間的,但在添加新參數時仍然很笨拙。它是基於其不得不下面給出在示例像匹配第EEPROM地址的設備參數ID:小型嵌入式設備的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如下,我已經建立了使用我自己的參數類型(結構)項目:
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},
...
從本質上說,每一個參數都有它獨特的ID,這個ID必須匹配硬編碼的EEPROM的地址。由於參數大小不固定,我不能將參數ID本身用作EEPROM(或其他介質)地址。在上面的例子中的EEPROM組織爲16位字,但它不會在原則上(更多的空間被浪費了字符,所以我寧願8位機構在未來反正)關係
問題:
有沒有更好的方法來做到這一點?一些散列表,衆所周知的模式,類似問題的標準解決方案? EEPROM的尺寸現在更大,我不介意使用固定的參數大小(浪費32位布爾參數)以換取更優雅的解決方案。它看起來像固定大小的參數,我可以使用參數ID作爲地址。這種方法有明顯的缺點,我沒有看到?
我現在使用的是分佈式硬件(HMI,I/O和主控制器是分開的),我想使用所有設備都知道這個參數結構的結構,例如遠程I/O知道如何縮放輸入值,並且HMI知道如何顯示和格式化數據,全部僅基於參數ID。換句話說,我需要一個地方,所有的參數將被定義。
我做了我的Google調查,但對於那些不包含某些數據庫的小設備卻找不到什麼。我甚至想過一些XML定義會爲我的數據結構生成一些C代碼,但也許有一些更適合小型設備(高達512 K閃存,32 K RAM)的優雅解決方案?
謝謝,這是取消對特定存儲組織的依賴的好建議。我使用FreeRTOS,所以你所說的'eeprom'對象在我的情況下是一些接收具有參數ID和命令(存儲,獲取,檢查,備份...)的消息並處理存儲到EEPROM的任務,或者任何其他媒體。我可以擁有所有的參數結構和常量數據(默認,最小,最大...)存儲在這個不可變對象中,並且我應該確保任何需要關於內部數據組織特定的任務都應該詢問這個「參數」對象(或任務),無論它需要知道什麼。 – 2013-03-13 17:30:05
是的,有一個確定數據的版本是必須的,否則,如果在新的電路板上使用舊的EEPROM,或者在同一設備的固件更新期間,可能會發生各種各樣的壞事。舊版本只是用每個參數的'default'字段的默認值覆蓋數據,當我們想要保留客戶配置時,安全但耗時。該器件基於EPROM,因此無論如何都不會在操作過程中進行固件更新,這在今天被認爲是標準。 – 2013-03-13 17:40:38