简体   繁体   English

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

[英]Clean and tidy string tables in PROGMEM in AVR-GCC

I'm looking for a way to cleanly define an array of strings in PROGMEM for an AVR project. 我正在寻找一种方法在PROGMEM中为AVR项目干净地定义一个字符串数组。 I have a command line processor that needs a list of command strings. 我有一个命令行处理器,需要一个命令字符串列表。

The traditional way to do it on the AVR architecture is to define each string separately, then an array of pointers to those strings. 在AVR架构上执行此操作的传统方法是分别定义每个字符串,然后是指向这些字符串的指针数组。 This is extremely verbose and ugly: 这非常冗长和丑陋:

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_* are enum'ed indicies, but could be replaced by function pointers or something) (CLI_COM_ *是枚举的标记,但可以用函数指针或其他东西替换)

This mess could be reduced using macros to define the strings and build the table, something like: 使用宏来定义字符串并构建表格可以减少这种混乱,例如:

#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) };

(untested, btw, but should be fine) (未经测试,顺便说一句,但应该没问题)

However, I would like to define all my strings only once and have a macro generate both the individual strings and the array of pointers/sizes/enum references. 但是,我想只定义我的所有字符串一次,并有一个宏生成单个字符串和指针/大小/枚举引用数组。

On the Arduino platform there is a FLASH_STRING_ARRAY macro which I can't quite figure out, but which doesn't seem to compile either. 在Arduino平台上有一个FLASH_STRING_ARRAY宏,我无法弄清楚,但似乎也没有编译。 You can see it here: http://pastebin.com/pMiV5CMr Maybe it's C++ only or something. 你可以在这里看到它: http//pastebin.com/pMiV5CMr也许它只是C ++或者什么。 Also it seems like it can only be used inside a function, not globally. 它似乎只能在函数内部使用,而不是全局使用。

String tables on AVR have long been a pain and inelegant. AVR上的字符串表长期以来一直是痛苦和不优雅的。 Short of writing a little program to generate the necessary code it would be nice to have a way to define it with macros. 如果没有编写一个小程序来生成必要的代码,那么有一种方法可以用宏来定义它。

Bonus points: Generate the CLI_COM_* constants with the same macro, either as an enum or with #defines. 加分点:使用相同的宏生成CLI_COM_ *常量,可以是枚举,也可以是#defines。

EDIT: I suppose another name for this would be iterative declaration via a macro. 编辑:我想这个的另一个名字是通过宏的迭代声明。

SOLUTION: Thanks to luser, I came up with this solution: 解决方案:感谢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
};

The produces the following output from the preprocessor: 从预处理器生成以下输出:

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},

};

So all that lot can be wrapped up an a region and I end up with just a simple and most importantly single definition of each command that serves as both its string name and the reference for the code. 因此,所有这些都可以包含在一个区域中,我最终只得到每个命令的一个简单且最重要的单一定义,它既可以作为字符串名称,也可以作为代码的引用。

Thanks a lot guys, much appreciated! 非常感谢,非常感谢!

X-Macros might help. X-Macros可能有所帮助。

strings.x: strings.x:

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

Usage: 用法:

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

This is untested. 这是未经测试的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM