Обработка большого количества массивов и исчерпание памяти - PullRequest
0 голосов
/ 29 апреля 2019

Первое: я унаследовал этот проект от того, кто не смог его завершить из-за нехватки времени.

Код содержит чуть более 100 объявленных массивов, каждый из которых содержит набор INT. Все массивы уникальны.

byte arr_foo[] = {2, 5, 6, 8, 3};
byte arr_bar[] = {1, 7};
byte arr_baz[] = {6, 10, 9, 11, 7, 8, 3};

Эти INT относятся к определенному светодиоду на плате - всего их 11. И массивы представляют собой определенную последовательность, в которой эти светодиоды должны загораться.

То, что они пытались сделать, это написать подпрограмму, которая, если ей дать имя массива, затем пошла бы для извлечения содержимого этого массива и обработки INT. Что ж, передача имени массива в виде строки для сопоставления с переменной не работает. И вот, где они передали это, говоря, что у них нет времени, чтобы понять это.

Итак, я смотрю на это и думаю, почему не двумерный массив? Я быстро столкнулся с проблемой там.

byte seqs[][7] = {
  {2, 5, 6, 8, 3},
  {1, 7},
  {6, 10, 9, 11, 7, 8, 3}
}

Хотя в принципе это работает, проблема заключается в том, что он дополняет каждый массив завершающими нулями, потому что я сказал, что у каждого есть [7] элементов. Это приводит к потере большого количества памяти и исчерпанию памяти.

Так что я застрял. Я не уверен, как обращаться со 100+ отдельными массивами, кроме как написать более 100 отдельных подпрограмм для последующего вызова. Также я не могу понять, как сделать его более эффективным.

Тогда возникает проблема: мне может все еще не хватить памяти, когда будет добавлено больше последовательностей. И что тогда? Добавить внешнюю флеш-память i2c, и засунуть туда вещи? Никогда не сталкиваясь с этим, я не уверен, как это реализовать, в каком формате хранить значения и как это сделать. Верно ли, что нужно сначала написать программу, которая загружает все данные в память, загрузить ее и запустить, а затем поместить на микроконтроллер реальную программу, которая будет обрабатывать эти данные?

Так что, я полагаю, я спрашиваю о двух вещах: как лучше обрабатывать множество-много (маленьких) массивов и иметь возможность использовать их в рутине, которая их вызывает, и если мне лучше отложить это данные во внешнюю флэш-память, в каком формате они должны храниться?

Ответы [ 3 ]

1 голос
/ 29 апреля 2019

Если эти массивы образуют схему светодиодов, которые должны гореть, а другие выключены, вы можете сохранить состояние всех светодиодов в uint16_t и иметь массив из них в PROGMEM. (Как в ответе Кингсли)

Если вы не знакомы с HEX-нотацией, вы можете использовать двоичный формат.

const PROGMEM uint_16_t patterns[] = {
// BA9876543210  Led Pins
 0b000101101100, //foo: 2, 5, 6, 8, 3
 0b000010000010, //bar: 1, 7
 0b111111001000, //baz: 6, 10, 9, 11, 7, 8, 3
// ... 
};

Меня интересует порядок ваших чисел, поэтому я не уверен, верна ли вообще эта догадка. Так что более подробно о том, как работать с этим подходом сейчас нет

1 голос
/ 29 апреля 2019

Размещение данных в двумерных массивах вообще не экономит места.

Прямо сейчас вы сохраняете эти значения в 2 КБ SRAM.Измените эти объявления, чтобы использовать ключевое слово PROGMEM , чтобы они сохранялись там, где много больше места.

Использование PROGMEM дает указание компилятору загружать эти данные во флэш-часть памяти:

const PROGMEM uint8_t arr_foo[] = { 2, 5, 6, 8, 3 };

Однако к данным нужно обращаться с помощью вызова функции, вы не можете просто использоватьэто напрямую.

for (byte k = 0; k < 5; k++) 
{
    uint8_t next_led = pgm_read_byte_near( arr_foo + k );
    // Do something with next_led
}
0 голосов
/ 29 апреля 2019

Обновление

Мне ваши комментарии полностью изменили цель вашего вопроса.

Поскольку я читаю это сейчас, нет необходимости в специальном типе данных «name» для идентификации массива. Похоже, что вы хотите просто передать различные массивы в качестве аргументов функции.

Обычно это делается с помощью указателей, и следует отметить две вещи:

  1. В большинстве случаев массивы "распадаются" на указатели автоматически. Это означает, что в большинстве мест вместо указателя может использоваться переменная массива. И указатель может быть использован как массив.
  2. Массив в C не содержит никакой информации о длине во время выполнения. Длина массива должна храниться / передаваться отдельно. Кроме того, вы можете определить структуру (или класс в C ++), который содержит как массив, так и его длину в качестве членов.

Пример:

Если вы хотите передать массив элементов типа T в функцию, вы можете объявить функцию для приема указателя на T:

void somefunc(uint8_t* arr, uint8_t arrLength) {

  for ( uint8_t i = 0; i < arrLength; i++ ) {
    uint8_t value = arr[i];
    value = *(arr+i); // equivalent to arr[i]
  }

}

или эквивалентно

void somefunc(uint8_t arr[], uint8_t arrLength) {
...
}

затем вызовите эту функцию, просто передав переменную массива и длину соответствующего массива, например

uint8_t arr_foo[] = { 1,2,3,4,5 };
uint8_t arr_bar[] = { 1,2 };

somefunc(arr_foo,5);
somefunc(arr_bar,2);

Постоянные данные массивов могут быть помещены в PROGMEM для экономии оперативной памяти, но, как отмечали другие, доступ к чтению немного сложнее, требуя pgm_read_...() вызовов в C ++. (AVR gcc поддерживает __flash -качественные данные только в C, но не в C ++.)

Тогда есть проблема, у меня может все еще не хватить памяти позже время по мере добавления новых последовательностей.

Обратите внимание, что AVR "Arduino" имеет 32 КБ флэш-памяти. Если каждая последовательность занимает 15 байтов, она, вероятно, может содержать 1000 или 2000 таких элементов вместе с вашей программой.

тогда что? Добавьте внешнюю флэш-память i2c и вставьте вещи в там? Никогда не сталкиваясь с этим, я не уверен, как реализовать что, в каком формате хранить значения и как это сделать.

Если в какой-то момент у вас действительно заканчивается флэш-память, вы все равно можете прибегнуть к любой форме внешнего хранилища.

Распространенным решением является флэш-память SPI, которая легко доступна в мегабитном диапазоне. Winbond является известным поставщиком. Просто найдите модули и библиотеки "Arduino SPI flash".

Более сложный подход заключается в поддержке SD-карт в качестве внешней памяти. Но, вероятно, оно того не стоит, если вы на самом деле не хотите хранить гигабайты данных.

Правильно ли, что сначала нужно написать программу, которая загружает все данные в памяти, загрузите это и запустите, затем поместите фактическую программу что собирается обрабатывать эти данные на микроконтроллере?

Это определенно один из способов сделать это. Если позволяет ваше кодовое пространство, вы можете альтернативно включить подпрограммы для записи во внешнюю флэш-память в своем приложении, например, в своего рода загрузчик, чтобы вы могли переключаться в режим «загрузки внешних флэш-данных» без повторной перепрограммирования микроконтроллера.

...