字符串的 3D 数组

3D array of strings

我正在尝试制作一个循环显示消息组的 LCD 项目 (16x2)。这个想法是它将循环浏览当前所选组中的所有消息,并且在手动更改之前不会离开该组。最初我有一个 3D 数组:第 1 层是消息组,第 2 层是要显示的消息,第 3 层是行。

#define WIDTH 16
#define HEIGHT 2
#define NGRP 3 
#define MAXGRP 6
String mainMsgs[NGRP][MAXGRP][HEIGHT] = { 
                      {
                       {"Value 1","Value 2"},
                       {"Value 3","Value 4"},
                       {"Value 5","Value 6"},
                       {"Value 7","Value 8"},
                       {"Value 9","Value 10"},
                      },
                      {
                       {"Value 1","Value 2"},
                       {"Value 3","Value 4"},
                       {"Value 5","Value 6"},
                       {"Value 7","Value 8"},
                       {"Value 9","Value 10"},
                       {"Value 9","Value 10"},
                       {"Value 11","Value 12"},
                      },
                      {
                       {"Value 1","Value 2"},
                       {"Value 3","Value 4"},
                       {"Value 5","Value 6"},
                       {"Value 7","Value 8"},
                      }

};

我或多或少有这个工作,但我认为数组对于内存来说太大了,因为在第 3 组中途它停止显示消息。当我测试它时,数组索引总是正确的。我假设数组已被截断。在尝试解决此问题时,我遇到了 PROGMEM。我尝试在以下位置转换 arduino 字符串教程:http://www.arduino.cc/en/Reference/PROGMEM 从 1D 数组转换为 3D 数组但无法使其工作,它要么编译失败,要么返回垃圾。以下是我的一些尝试。

const char message1[][2] PROGMEM = { "Value 1", "Value 2" }; // Through to message15
const char* const group1[][5] PROGMEM = { message1, message2, message3, message4, message5 };
const char* const group2[][6] PROGMEM = { message6, message7, message8, message9, message10, message11 };
const char* const group3[][4] PROGMEM = { message12, message13, message14, message15 };
const char* const groups[] PROGMEM = { group1, group2, group3 }; // Attempt 1
const char* const groups[NUMGRP][6] PROGMEM = { 
                                        {message1, message2, message3, message4, message5}, 
                                        {message6, message7, message8, message9, message10, message11}, 
                                        {message12, message13, message14, message15}, 
                                      }; // Attempt 2

所以我尝试使用

将原始数组直接转换为progmem
const char* const mainMsgs[NUMGRP][6][HEIGHT] = { /* same content as before*/ };
strcpy_P(buff, (char*)pgm_read_word(&(mainMsgs[groupID][msgID][i])));

但它仍然返回垃圾。 所以我想我会尝试将数据转换为一维数组,然后使用偏移量访问消息和行。

编辑:编辑了以下代码以反映我在原始草图中使用的代码。

const char message1[] = "Value 1";
const char message2[] = "Value 2"; // Down to message30
const char* const messages[] PROGMEM = { message1, message2,
                    message3, message4,
                    // ... ... ...
                    message29, message30
                  };
int groupStarts[] = { 0, 10, 22 }; // The first index of each group
int numMsgs[] { 5, 6, 4 };
char buff[WIDTH];

这是我使用的测试循环:

int id = 0;
for( groupID = 0; groupID < NGRP; groupID++ ) {
   for( msgID = 0; msgID < numMsgs[groupID]*HEIGHT; msgID+=HEIGHT ) {
     for( lineID = 0; lineID < HEIGHT; lineID++ ) {
       id = groupStarts[groupID] + msgID + lineID;
       strcpy_P(buff, (char*)pgm_read_word(&(messages[id])));
       Serial.print(id);
       Serial.print(" ");
       Serial.print(buff);
     }
     Serial.println("");
     delay(500);
   }
}

这导致了一个几乎可以工作的例子,但它并不完美:

0 Value1 1 Value 2
2 Value3 3 Value 4
4 Value 5 5 Value 6
6 Value 7 7 Value 8
8 Value 9
10 Value 11 11 Value 12
10 Value 11 11 Value 12
12 Value 13 13 Value 14
14 Value 15 15 Value 16
16 Value 17 17 Value 18
18 Value 19 19 Value 20 19 Value 20 19 Value 20...

您可能会注意到没有显示值 10,值 11 和 12 重复两次,当它达到值 19 时,它只是陷入无限循环。

我不太想最后的循环应该是什么。

理想情况下,我更愿意保留 3D 数组结构,因为我发现它更易于阅读和理解,但我很乐意为任一版本的代码提供解决方案。

编辑以反映 shuttle87 的建议:

#include <avr/pgmspace.h>

#define WIDTH 16
#define HEIGHT 2

const char string1[] PROGMEM = "Message 1";
const char string2[] PROGMEM = "Message 2";
const char string3[] PROGMEM = "Message 3";
const char string4[] PROGMEM = "Message 4";
const char string5[] PROGMEM = "Message 5";
const char string6[] PROGMEM = "Message 6";

const int groupLen[] = { 2, 3, 1 };

const char* msgs[][3][2] = {
  {
    {string1, string2 },
    {string3, string4 }
  },
  {
    {string5, string6 },
    {string3, string4 },
    {string1, string2 }
  },
  {
    {string2, string3 }
  }
};

char buffer[WIDTH];

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  for( int g = 0; g < 3; g++ ) {
    Serial.print("Switching to group: ");
    Serial.println(g);
    for( int m = 0; m < groupLen[g]; m++ ) {
      Serial.print("Switching to message: ");
      Serial.println(m);
      for( int l = 0; l < HEIGHT; l++ ) {
        Serial.print("Switching to line: ");
        Serial.println(l);
         strcpy_P(buffer, (char*)pgm_read_word(&(msgs[g][m][l])));
         Serial.println(buffer);
      }
      delay(500);
    }
  }
}

我得到的当前输出是:"Switchi" 仅此而已,这是否意味着我的 Arduino 由于我的代码而挂起,或者我以某种方式杀死了它?我还更新了单个数组版本以反映我实际编码它的方式。复制进去的时候我复制错了,有点乱。它的工作方式更像是 shuttle87 建议的方式,但它仍然 returns 上面显示的错误。

编辑:刚刚意识到我错过了:

const char* msgs[][3][2] = {
      {
        {string1, string2 },
        {string3, string4 }
      },
      {
        {string5, string6 },
        {string3, string4 },
        {string1, string2 }
      },
      {
        {string2, string3 }
      }
    };

应该已经开始:

const char* const messages[][3][2] PROGMEM= {
      {
        {string1, string2 },
        {string3, string4 }
      },
      {
        {string5, string6 },
        {string3, string4 },
        {string1, string2 }
      },
      {
        {string2, string3 }
      }
    };

很抱歉。这似乎确实解决了它。非常感谢您的帮助:)

谢谢。

您的大多数尝试都有相同的问题,您已将指向 table 的指针存储在 progmem 中,但实际的 table 数据本身(在本例中为字符串)并未存储在 progmem 中.不幸的是,GCC 属性(自 gcc 4.7 起)仅适用于当前声明,因此您必须在每个变量上指定 progmem。

所以当你有

 const char message1[][2] PROGMEM = { "Value 1", "Value 2" };

message1 存储在 progmem 中,但字符串 "Value 1" 不是。此外,如果我没记错的话,avr-gcc 编译器总是将字符串文字存储在 SRAM 中。即使你指定一个地方把它们放在 progmem 中,它仍然被复制到 SRAM 中(有一次我试图编写一个库来使用 c++11 用户定义的字符串文字将东西放在 progmem 中,但这阻碍了我)。一维数组的解也属于这个问题

要解决此问题,您需要将所有内容显式存储在程序内存中,例如,您的 1D 解决方案如下所示:

const char string_msg0_0[] PROGMEM = "Value 1";
const char string_msg0_1[] PROGMEM = "Value 2";

PGM_P strings_pgm_table[] PROGMEM = {string_msg0_0, string_msg0_1};

char buffer[MAX_STRING_SIZE];
strcpy_P(buffer, (PGM_P)pgm_read_word(&(strings_pgm_table[i])));

我建议您看看 AVR-GCC tutorial putting data in progmem