Максимальный размер массива символов

ilyatar
Offline
Зарегистрирован: 08.08.2013

Привет всем!

Сегодня столкнулся с такой проблемой. Написал скетч для MEGA-2560, в нем используются два массива такого типа:

static char *ruWords[] = {"Один", "Два", ....., "Тридцать один", ""};
static char *enWords[] = {"One", "Two", ....., "Thirty one", ""};

Массивы предназначены для локализации меню и вместо цифр там названия пунктов. В меге все прекрасно работает. Я решил залить этот скетч в Nano v3.0 и возникли проблемы, а именно при адресации к элементам массива ruWords, происходит обращение к фиг знает каким участкам памяти. Такое впечатление, что массив с русскими строками не помещается в памяти. Опытным путем это было подтверждено, я начал добавлять по одному русскому пункту в массив ruWords. Сначала все работало хорошо, но в какой-то момент опять начало лагать, дуинка перезагружалась, либо выводила на экран мусор. В связи с этим возник вопрос, есть ли ограничение на максимальный размер массива, и поможет ли разбиение массива ruWords на несколько массивов меньшего размера?

step962
Offline
Зарегистрирован: 23.05.2011

ilyatar пишет:

[...]

В связи с этим возник вопрос, есть ли ограничение на максимальный размер массива, и поможет ли разбиение массива ruWords на несколько массивов меньшего размера?

Есть: смотрим строчки "ОЗУ" у Mega-2560 и у Nano

Не поможет - объем ОЗУ это никоим образом не увеличивает.

Однако, может помочь размещение подобных массивов в программной памяти (предпочтительно) или EEPROM.

Кроме того, может помочь оптимизация массовов - хранение не фраз, как у вас, а только слов, обозначающих единицы и десятки:

static char *ruWords[] = {"Один", "Два", ....., "Тридцать", "Сорок",...};
static char *enWords[] = {"One", "Two", ....., "Thirty", "Forty",...};

и сборка фраз в процессе вывода

leshak
Offline
Зарегистрирован: 29.09.2011

step962 пишет:

Однако, может помочь размещение подобных массивов в программной памяти (предпочтительно) или EEPROM.

Другими словами вам нужно загуглить слово "PROGMEM"

Впрочем, можно и не гуглить, а сразу в доку лезть

Arduino - PROGMEM и мотать вниз на раздел "Arrays of strings". Там есть готовый пример.

Впрочем, всегда можно столько строк набить, что никакой памяти не хватит  (особенно с русскими буквами).

Тогда самый крайний вариант - цеплять SD карту и с нее, по мере надобности, подгружать.... правда это может весьма печально сказатся на быстродействии.

 

ilyatar
Offline
Зарегистрирован: 08.08.2013

step962 пишет:

Не поможет - объем ОЗУ это никоим образом не увеличивает.

Мой скетч занимает 18кБ, что значительно больше ОЗУ, следовательно скетч грузится в ОЗУ МК по мере необходимости частями? Я так думаю, если разбить один массив на части, то они будут подгружаться и выгружаться в ОЗУ по мере необходимости?

step962 пишет:

Однако, может помочь размещение подобных массивов в программной памяти (предпочтительно) 

Можно подробнее - это что за память?

step962 пишет:

Кроме того, может помочь оптимизация массовов - хранение не фраз, как у вас, а только слов, обозначающих единицы и десятки:

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

ilyatar
Offline
Зарегистрирован: 08.08.2013

leshak пишет:

Arduino - PROGMEM и мотать вниз на раздел "Arrays of strings".

О! Видимо то что надо, спасибо буду смотреть, когда попробую отпишусь. Еще вопрос, а у PROGMEM какое ограничение размера?

maksim
Offline
Зарегистрирован: 12.02.2012

ilyatar пишет:

Мой скетч занимает 18кБ, что значительно больше ОЗУ, следовательно скетч грузится в ОЗУ МК по мере необходимости частями? Я так думаю, если разбить один массив на части, то они будут подгружаться и выгружаться в ОЗУ по мере необходимости?

Размер скейтча никак не связан с размером ОЗУ, в ОЗУ загружаются переменные, а не сам скейтч.

ilyatar пишет:

а у PROGMEM какое ограничение размера?

А вот у PROGMEM ограничение это размер Flash - как раз определяется размером вашего скейтча.

leshak
Offline
Зарегистрирован: 29.09.2011

Для простоты понимания можете такие аналогии провести с PC:

RAM - это "оперативка", а FLASH - это "винчестер" (HDD). Программа на "винте" может занимать сколько угодно (в пределах объема винчестера) и подгружать необходимые данные в память - по мере надобности.

Возвращаясь к ардуине.

Скетч - у вас записывается на FLASH. И там же выполняется. Но переменные, по умолчанию, что-бы иметь возможность их легко менять и легко читать - при старте копируются в RAM (оперативку).

Когда вы помечаете переменную ключевым словом PROGMEM вы говорите компилятору: "а не нужно ее при старте копировать в RAM, я сам скопирую когда она мне понадобится" (ну и, естественно нужно это не забыть сделать). За счет того что вы можете свои строки "подгружать" по одной (или даже "по одному символу"), типа "загрузил-отправил на экран, на это же место загрузил вторую строку - отправил", у вас не будет в RAM одновременно всех строк. Следовательно можно работать с гораздо большим количеством. То которое влезет во флешь (сколько влезет - либо читать доку на плату, либо смотреть при компиляции размер скетча).

ilyatar
Offline
Зарегистрирован: 08.08.2013

leshak пишет:

Для простоты понимания можете такие аналогии провести с PC:

Ну уж я не совсем тупой, просто с программированием МК недавно завязался и некоторые тонкости не знаю :)

leshak пишет:

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

За разъяснение работы PROGMEM спасибо! Переписал как в примере расписано и все получилось. Правда вылез еще один непонятный косяк, но я его обошел, к сожалению не разобравшись в сути проблемы. Может быть у МК есть глубина передачи массива символов, внутрь функции? Т.е. у меня произошло следущее, была такая реализация:

void printToLCD {
  printToScreen("Welcome", "Begin");
}

void printToScreen(char *st1, char *st2) {
  char buf1[33];
  char buf2[33];
  strcpy(buf1, localizedString(st1));
  strcpy(buf2, localizedString(st2));
  //Дальше работа с переменными buf1 и buf2
}

char localizedBuffer[33];  //Я знаю что для такой реализации одного буфера мало, это просто пример

char *localizedString(char *st) {
  //Здесь механизм локализации строки
  return localizedBuffer;
}

И видимо пробрасывание переменных st1 и st2 из функции в функцию где-то терялось, в общем нифига не работало. Заработало только когда я в функцию printToScreen стал передавать локализованные строки и дальше этой функции они не уходили. Вот такая у меня осталась непонятка. А еще отлаживать код без real-time debuger очень трудно.

leshak
Offline
Зарегистрирован: 29.09.2011

ilyatar пишет:

И видимо пробрасывание переменных st1 и st2 из функции в функцию где-то терялось, в общем нифига не работало. Заработало только когда я в функцию printToScreen стал передавать локализованные строки и дальше этой функции они не уходили. Вот такая у меня осталась непонятка. А еще отлаживать код без real-time debuger очень трудно.

Нифига не понятно что у вас нифига не работало.

ilyatar
Offline
Зарегистрирован: 08.08.2013

Проблема по которой создавалась тема решена, всем спасибо! В этой теме не буду больше флудить и мучать ваши мозги.

leshak
Offline
Зарегистрирован: 29.09.2011

OK :)

Кстати, есть еще родственный PROGMEM-му макрос. PSTR

Он делает то же самое что и PROGMEM для переменных, только для строк захадкожены в код. 

void setup(){
   Serial.begin(9600); 

  Serial.println("Hello from RAM");
  println(PSTR("Hello from FLASH"));
  
}

void loop(){
}

// аналоги Serial.print и Serial.println, но строки берут из флеша.
void print (PGM_P s) {
        char c;
        while ((c = pgm_read_byte(s++)) != 0)
            Serial.print(c);
    }

void println(PGM_P s){
	print(s);
	Serial.println();
}

 

 

 

Freerider
Offline
Зарегистрирован: 02.09.2013

У меня схожая проблема с переполнением ОЗУ. В определенный момент программы нужно передавать крупные массивы байт.

Если я вместо использования PROGMEM. Данные массивы буду инициалицировать не глобально, а локально в функциях.

Как я понимаю время существования локальной переменной только во время выполнения функции, что вполне меня устривает.

Или же при старте ардуины все возможные переменные записываются в ОЗУ и глобальные и локальные.....мне кажется это не так. Разъясните пожалуйста.