1

我正在尝试制作一个循环显示消息组的 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。我尝试将位于http://www.arduino.cc/en/Reference/PROGMEM的 arduino 字符串教程 从 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

所以我尝试使用将原始数组直接转换为程序

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 的建议,但它仍然返回上面显示的错误。

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

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

对于那个很抱歉。这似乎已经解决了它。非常感谢你的帮忙 :)

谢谢。

4

1 回答 1

0

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

所以当你有

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

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

要解决此问题,您将所有内容显式存储在 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 教程将数据放入 progmem

于 2015-06-08T15:20:55.307 回答