0

Arduino PROGMEM に格納されている構造体から文字列を読み取りたい:

struct commandCode {
   int code;
   const char *name;
};

const PROGMEM commandCode commands[LAST_COMMAND] =  {
   { CMD_DEMO, "DEMO" } ,
   { CMD_STOP, "STOP"} ,
   { CMD_FORWARD, "FORWARD"},
   { CMD_BACKWARD, "BACKWARD"},
   { CMD_TURN_LEFT, "TURN LEFT"},
   { CMD_TURN_RIGHT, "TURN RIGHT"},
   { CMD_WAIT, "WAIT"},
   { CMD_WAIT_DONE, "WAIT DONE"},
};

このコードは文字列をうまく出力します:

void CommandCodes::show() {
    Serial.print(LAST_COMMAND);
    Serial.println(" Comands Defined:");
    for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
        CommandCodes::commandCode cmd = commands[i];
        showCommand(cmd);
    }
}

void CommandCodes::showCommand(commandCode cmd) {
    if (cmd.code > FIRST_COMMAND) {
        Serial.print(F("["));
        Serial.print(cmd.code);
        Serial.print(F("] "));
        Serial.println(cmd.name);
    }

}

このコードは、プログラムを爆撃して再起動します。

const char* CommandCodes::name(int code) {
    for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
        CommandCodes::commandCode cmd = commands[i];
        if (cmd.code == code) {
            return cmd.name;
        }
    }
    return NULL;
}

ポインターを返すコードは何cmd.nameですか?

4

1 に答える 1

0

構造体には文字列データではなくポインターのみが含まれているため、文字列は引き続き RAM に格納されます。

また、データにアクセスするときに PROGMEM から読み取っていません。特定の状況で機能しているという事実は単なる運ですが、それでも正しくありません。

すべてのデータを PROGMEM に配置するには、構造体内に領域を割り当てる必要があります。最大の文字列は 11 文字 + null なので、長さを 12 にすることができます。

struct commandCode {
  int code;
  const char name[12];
};

const commandCode commands[] PROGMEM = {
  { CMD_DEMO, "DEMO" } ,
  { CMD_STOP, "STOP"} ,
  { CMD_FORWARD, "FORWARD"},
  { CMD_BACKWARD, "BACKWARD"},
  { CMD_TURN_LEFT, "TURN LEFT"},
  { CMD_TURN_RIGHT, "TURN RIGHT"},
  { CMD_WAIT, "WAIT"},
  { CMD_WAIT_DONE, "WAIT DONE"}
}

各構造体の内部は PROGMEM にあるため、特別な関数を使用してそれらを読み取る必要があります。それらを直接読み取ることはできません。

これはまた、あなたが行ったようにアイテムをコピーできないことを意味します: CommandCodes::commandCode cmd = commands[i];

しかし、参照を使用することはできます。 const commandCode &cmd = commands[i];

ただし、上で述べたように、参照される構造体の要素には適切にアクセスする必要があります。

整数の場合は、 を使用する必要がありますpgm_read_wordF()文字列については、Serial クラスがフラッシュ文字列を処理するときに (マクロを使用する場合と同様に)、それを印刷するようにトリックすることができます。これは、ポインタを にキャストすることで実行できますconst __FlashStringHelper*

これは、各パーツに適切にアクセスする方法を示す作業スケッチです。それをテストして、私がやったことを試してみてください。いくつか質問があると思いますので、この回答のコメントに追加してください。回答を更新します。

struct commandCode {
  int code;
  const char name[12];
};

enum COMMANDS{
  CMD_DEMO,
  CMD_STOP,
  CMD_FORWARD,
  CMD_BACKWARD,
  CMD_TURN_LEFT,
  CMD_TURN_RIGHT,
  CMD_WAIT,
  CMD_WAIT_DONE,
};

const commandCode commands[] PROGMEM = {
  { CMD_DEMO, "DEMO" } ,
  { CMD_STOP, "STOP"} ,
  { CMD_FORWARD, "FORWARD"},
  { CMD_BACKWARD, "BACKWARD"},
  { CMD_TURN_LEFT, "TURN LEFT"},
  { CMD_TURN_RIGHT, "TURN RIGHT"},
  { CMD_WAIT, "WAIT"},
  { CMD_WAIT_DONE, "WAIT DONE"}
};

#define FIRST_COMMAND 0
#define LAST_COMMAND sizeof(commands) / sizeof(*commands)
#define FSH (const __FlashStringHelper*)  //A helper to allow printing the PROGMEM strings.

void show() {
    for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
        const commandCode &cmd = commands[i];
        showCommand(cmd);
    }
}

void showCommand(const commandCode &cmd) {
    if ( pgm_read_word( &cmd.code ) > FIRST_COMMAND) {
        Serial.print(F("["));
        Serial.print( pgm_read_word( &cmd.code ) );
        Serial.print(F("] "));
        Serial.println( FSH( cmd.name ) );
    }
}

const char* name(int code) {
    for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
        const commandCode &cmd = commands[i];
        if (pgm_read_word(&cmd.code) == code) {
            return cmd.name; //As cmd.name resolves to a pointer it can be passed back as is.
            //However to use the 'pointed to data' it will have to be accessed properly.
        }
    }
    return NULL;
}

void setup() {
  Serial.begin(9600);

  Serial.println("Show test");
  show();

  Serial.println("Name test");
  for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
    Serial.println( FSH( name(i) ) );
  }
  Serial.println("Done");
}

void loop() {}
于 2016-06-13T12:39:48.833 に答える