从Arduino PROGMEM中的struct读取字符串

时间:2016-06-13 05:05:52

标签: arduino progmem

我想从存储在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的指针的代码是什么?

1 个答案:

答案 0 :(得分: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_word。对于字符串,您可以欺骗Serial类,因为它处理闪存字符串(比如使用F()宏的地方)。这可以通过将指针强制转换为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() {}