4

小さな組み込みデバイスの再設計(PIDコントローラー)で私が取り組んでいる主な問題は、デバイスパラメーターの保存です。ここで部分的に提示した古いソリューションはスペース効率が良かったのですが、新しいパラメーターが追加されたときに維持するのが面倒でした。これは、以下の例のように、EEPROMアドレスと一致する必要があるデバイスパラメータIDに基づいていました。

// 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

...                        

次に、これらのパラメーターを使用するコードで、たとえば、以下に示すユーザーメニュー構造で、独自の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},

..。

基本的に、すべてのパラメータには一意のIDがあり、このIDはハードコードされたEEPROMアドレスと一致する必要がありました。パラメータのサイズが固定されていないため、パラメータID自体をEEPROM(または他のメディア)アドレスとして使用できませんでした。上記の例のEEPROM構成は16ビットワードでしたが、原則として問題ではありません(文字のためにより多くのスペースが無駄になるため、将来的には8ビット構成を使用することをお勧めします)

質問:

これを行うためのよりエレガントな方法はありますか?いくつかのハッシュテーブル、よく知られているパターン、同様の問題の標準的な解決策?EEPROMのサイズは現在はるかに大きくなっています。より洗練されたソリューションと引き換えに、固定パラメーターサイズ(ブールパラメーターに32ビットを浪費)を使用してもかまいません。固定サイズのパラメータのように見えますが、パラメータIDをアドレスとして使用できます。この方法には、私には見えない明らかな欠点がありますか?

現在、分散型HW(HMI、I / O、メインコントローラーが分離されている)を使用していますが、すべてのデバイスがこのパラメーター構造を認識している構造を使用して、たとえばリモートI/Oがスケーリング方法を認識できるようにします。入力値、およびHMIは、すべてパラメーターIDのみに基づいてデータを表示およびフォーマットする方法を知っています。つまり、すべてのパラメーターが定義される単一の場所が必要です。

私はGoogleの調査を行いましたが、一部のデータベースを含まない小型デバイスではほとんど見つかりませんでした。データ構造のCコードを生成するXML定義についても考えていましたが、小さなデバイス(最大512 Kフラッシュ、32 K RAM)に適した洗練されたソリューションがあったのではないでしょうか。

4

3 に答える 3

2

変更やプロセッサ間の互換性について心配していない場合は、RAMとEEPROMの間で構造体をコピーするだけで、RAMコピーの個々のメンバーにのみアクセスできます。

EEPROMで直接個々のメンバーに明示的にアクセスしたい場合は、コンパイラの構造体と既知のパッキングルールから定義のリストをコンパイルするツールを比較的簡単に作成することもできます。

于 2013-03-12T16:16:06.890 に答える
2

これが私がすることです。

EEPROMに入れたい変数を使って構造体のtypedefを作成します。

あなたの例を使用すると、次のようになります。

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

構造がEEPROMのどこに保存されるかをマークするオフセット定義を作成するよりも。

そして、その型へのポインタを追加して、ダミーオブジェクトとして使用します。

#define EEPROM_OFFSET 0
eeprom_st_t *dummy;

offsetofを使用して、次のように必要な特定の変数のオフセットを取得するよりも:

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

よりエレガントにするために、eeprom書き込みルーチンもマクロに変換します。

于 2013-04-01T16:09:22.913 に答える
1

これが実際にあなたが持っているものよりも優れているかどうかはわかりませんが、ここにアイデアがあります。メンテナンスを容易にするために、EEPROMアドレスの知識を「eeprom」オブジェクトにカプセル化することを検討してください。現在、パラメータオブジェクトがあり、各インスタンスはそのデータが物理EEPROMのどこに保存されているかを認識しています。パラメータオブジェクトにEEPROMの知識がない場合は、おそらく保守が容易になります。代わりに、別のeepromオブジェクトが、物理EEPROMとパラメータオブジェクトインスタンス間のインターフェイスを担当していました。

また、EEPROMに保存されているデータにEEPROMデータのバージョン番号を追加することを検討してください。デバイスのファームウェアが更新され、EEPROMデータの形式が変更された場合、このバージョン番号により、新しいファームウェアが古いバージョンのEEPROMデータを認識して変換できるようになります。

于 2013-03-13T15:28:19.720 に答える