PROGMEM поврежден массив

Я пытаюсь сохранить большое количество значений в нескольких массивах с помощью PROGMEM, но обнаруживаю, что некоторые значения считываются обратно поврежденными. Я сохраняю только 1 и 0, но когда я читаю эти значения, я иногда получаю значения больше 1. Для этого я использую MEGA 2560.

// Каждый массив содержит 32000 значений
const byte VALUES_1[] PROGMEM = { 1, 0, 1, .... 0 };
const byte VALUES_2[] PROGMEM = { 1, 1, 0, .... 0 };
const byte VALUES_3[] PROGMEM = { 1, 0, 1, .... 1 };
const byte VALUES_4[] PROGMEM = { 1, 0, 1, .... 1 };
const byte VALUES_5[] PROGMEM = { 1, 0, 1, .... 1 };

const long ARR_LEN = 32000;
long index = 0;

void setup() {
  Serial.begin(57600);
  while (!Serial) {}
}

byte readNextValue() {
  if (index >= 160000L) {
    return -1;
  }

  Serial.print(index);
  Serial.print(" ");

  long arr = index / ARR_LEN;
  long i = index % ARR_LEN;
  index++;

  byte j = -1;
  switch(arr) {
    case 0:
      j = pgm_read_byte_far(VALUES_1 + i);
      Serial.println(j);
      return j;
    case 1:
      j = pgm_read_byte_far(VALUES_2 + i);
      Serial.println(j);
      return j; 
    case 2:
      j = pgm_read_byte_far(VALUES_3 + i);
      Serial.println(j);
      return j;
    case 3:
      j = pgm_read_byte_far(VALUES_4 + i);
      Serial.println(j);
      return j;
    case 4:
      j = pgm_read_byte_far(VALUES_5 + i);
      Serial.println(j);
      return j;
  }

  Serial.println("Unable to return next value");
  return -1;
}

void loop() {
  // поместите сюда свой основной код для многократного запуска:
  readNextValue();
}

Я заметил, что когда я использую pgm_read_byte_far для case 0, я сразу получаю мусорные значения, но использование pgm_read_byte_near будет привести к тому, что эти мусорные значения появятся когда-нибудь позже. Любое понимание этой проблемы приветствуется.

, 👍1

Обсуждение

Не используйте ни близко, ни далеко? Позвольте компилятору разобраться, что есть что?, @Majenko

Общий размер вашего массива составляет 5 * 32000 = 160 000 байт. Почему бы не сохранить его в виде битов, которые займут всего 20 000 байт? Тогда бы отпали все вопросы дальней адресации. Вы можете использовать небольшой скрипт для преобразования 0 и 1 в подходящие байты, а затем простой макрос или функцию для извлечения отдельных битов., @Nick Gammon

Также см. [этот вопрос](https://arduinoprosto.ru/q/34586/reading-a-10-byte-buffer-out-of-a-progmem-string-stored-in-a-string-table /) об индексации в большие массивы, где я разместил ответ, позволяющий избежать повреждения., @Nick Gammon

Спасибо за ответы, ребята. Завтра попробую некоторые предложения и посмотрю, как пойдет. В худшем случае, похоже, мне придется просто использовать такие биты, как предложил Ник, чтобы избежать проблемы с адресацией 64 КБ, которая, похоже, есть у Arduino., @KaYBlitZ

Память Mega достаточно ограничена: если вы сохраняете только 0 или 1 в байте (8 бит), вы теряете много памяти (7 неиспользуемых битов на байт). Рассмотрите возможность объединения 8 0 и 1 в 1 байт (с использованием операторов сдвига битов)., @Michel Keijzers


1 ответ


2

Просто дополнительная информация на случай, если у кого-то возникнет такая же проблема. Одним из решений является ответ Ника в его комментарии, который сначала захватывает дальние адреса в настройке, сохраняет их в массиве и использует. я

// Каждый массив содержит 32000 значений
const byte VALUES_1[] PROGMEM = { 1, 0, 1, .... 0 };
const byte VALUES_2[] PROGMEM = { 1, 1, 0, .... 0 };
const byte VALUES_3[] PROGMEM = { 1, 0, 1, .... 1 };
const byte VALUES_4[] PROGMEM = { 1, 0, 1, .... 1 };
const byte VALUES_5[] PROGMEM = { 1, 0, 1, .... 1 };

long VALUES[] = { 0, 0, 0, 0, 0 };

const long ARR_LEN = 32000;
long index = 0;

void setup() {
  Serial.begin(57600);
  while (!Serial) {}

  VALUES[0] = (long) pgm_get_far_address(VALUES_1);
  VALUES[1] = (long) pgm_get_far_address(VALUES_2);
  VALUES[2] = (long) pgm_get_far_address(VALUES_3);
  VALUES[3] = (long) pgm_get_far_address(VALUES_4);
  VALUES[4] = (long) pgm_get_far_address(VALUES_5);
}

byte readNextValue() {
  if (index >= 160000L) {
    return -1;
  }

  Serial.print(index);
  Serial.print(" ");

  long arr = index / ARR_LEN;
  long i = index % ARR_LEN;
  index++;

  byte j = pgm_read_byte_far(VALUES[arr] + i);
  Serial.println(j);

  return j;
}

void loop() {
  // поместите сюда свой основной код для многократного запуска:
  readNextValue();
}

Это работает отлично, все байты верны. Лучшим ответом было бы преобразовать байты в биты, как говорили все остальные, чтобы сэкономить место в памяти.

,