在AVR-GCC的PROGMEM中清洁整洁的字符串表

时间:2013-09-27 09:48:53

标签: c string macros avr avr-gcc

我正在寻找一种方法在PROGMEM中为AVR项目干净地定义一个字符串数组。我有一个命令行处理器,需要一个命令字符串列表。

在AVR架构上执行此操作的传统方法是分别定义每个字符串,然后是指向这些字符串的指针数组。这非常冗长和丑陋:

typedef struct 
{
    PGM_P   str;        // pointer to command string
    uint8_t str_len;    // length of command string
    uint8_t id;         // CLI_COM_* ID number
} CLI_COMMAND_t;

const char CLI_STR_TEMP[]   PROGMEM = "TEMP";
const char CLI_STR_POWER[]  PROGMEM = "POWER";
...

const CLI_COMMAND_t cli_cmd_table[] = { { CLI_STR_TEMP,     sizeof(CLI_STR_TEMP),   CLI_COM_TEMP },
                                        { CLI_STR_POWER,    sizeof(CLI_STR_POWER),  CLI_COM_POWER },
                                        ...
                                      };

(CLI_COM_ *是枚举的标记,但可以用函数指针或其他东西替换)

使用宏来定义字符串并构建表格可以减少这种混乱,例如:

#define FLASH_STRING(NAME...)    const char CLI_STR_ ## NAME [] PORGMEM = #NAME;
#define FSTR(NAME...)            { CLI_STR_ ## NAME, sizeof(CLI_STR_ ## NAME), CLI_COM_ ## NAME) }

FLASH_STRING(TEMP);
FLASH_STRING(POWER);

CLI_COMMAND_t cli_cmd_table[] = { FSTR(TEMP), FSTR(POWER) };

(未经测试,顺便说一句,但应该没问题)

但是,我想只定义我的所有字符串一次,并且有一个宏生成单个字符串和指针/大小/枚举引用数组。

在Arduino平台上有一个FLASH_STRING_ARRAY宏,我无法弄清楚,但似乎也没有编译。你可以在这里看到它:http://pastebin.com/pMiV5CMr也许它只是C ++或其他东西。它似乎只能在函数内部使用,而不是全局使用。

AVR上的字符串表长期以来一直是一种痛苦而且不优雅。如果没有编写一个小程序来生成必要的代码,那么有一种方法可以用宏来定义它。

奖励积分:使用相同的宏生成CLI_COM_ *常量,无论是枚举还是#defines。

编辑:我想这个名字的另一个名字就是通过宏进行迭代声明。

解决方案:感谢luser,我提出了这个解决方案:

typedef struct 
{
    PGM_P   str;        // pointer to command string
    uint8_t str_len;    // length of command string
    uint8_t id;         // CLI_COM_* ID number
} CLI_COMMAND_LUT_t;



#define COMMAND_TABLE \
        ENTRY(testA) \
        ENTRY(testB) \
        ENTRY(testC)

enum {
#define ENTRY(a) CLI_COM_ ## a,
    COMMAND_TABLE
#undef ENTRY    
};


#define ENTRY(a)    const char CLI_STR_ ## a PROGMEM = #a;
COMMAND_TABLE
#undef ENTRY


CLI_COMMAND_LUT_t command_lut[] PROGMEM = {
#define ENTRY(a) {CLI_STR_ ## a, sizeof(CLI_STR_ ## a), CLI_COM_ ## a},
    COMMAND_TABLE
#undef ENTRY
};

从预处理器生成以下输出:

typedef struct
{
 PGM_P str;
 uint8_t str_len;
 uint8_t id;
} CLI_COMMAND_LUT_t;

enum {
 CLI_COM_testA, CLI_COM_testB, CLI_COM_testC,
};

const char CLI_STR_testA PROGMEM = "testA"; const char CLI_STR_testB PROGMEM = "testB"; const char CLI_STR_testC PROGMEM = "testC";

CLI_COMMAND_LUT_t command_lut[] PROGMEM = {

 {CLI_STR_testA, sizeof(CLI_STR_testA), CLI_COM_testA}, {CLI_STR_testB, sizeof(CLI_STR_testB), CLI_COM_testB}, {CLI_STR_testC, sizeof(CLI_STR_testC), CLI_COM_testC},

};

所以这一切都可以包含在一个区域中,我最终只得到每个命令的一个简单且最重要的单一定义,它既可以作为字符串名称,也可以作为代码的引用。

非常感谢你们,非常感谢!

1 个答案:

答案 0 :(得分:1)

X-Macros可能有帮助。

strings.x:

X(TEMP, "Temp")
X(POWER, "Power")

用法:

// String concatenation macros
#define CONCAT(a, b)   CONCAT2(a, b)
#define CONCAT2(a, b)  a ## b

// Generate string variables
#define X(a, b)      const char CONCAT(CLI_STR_, a) []   PROGMEM = b;
#include "strings.x"
#undef X

// Generate enum constants
#define X(a, b)      CONCAT(CLI_COM_, a),
enum {
#include "strings.x"
};
#undef X

// Generate table
#define X(a, b)      { CONCAT(CLI_STR_, a),     sizeof(CONCAT(CLI_STR_, a)),   CONCAT(CLI_COM_, a) },
const CLI_COMMAND_t cli_cmd_table[] = { 
#include "strings.x"
};
#undef X

这是未经测试的。