У меня есть некоторые трудности с пониманием управления памятью.
В документации Arduino говорится, что можно сохранять константы, такие как строки или что-либо, что я не хочу менять во время выполнения, в памяти программы. Я думаю, что он встроен где-то в сегмент кода, что должно быть вполне возможно в архитектуре фон Неймана. Я хочу использовать это, чтобы сделать возможным отображение моего пользовательского интерфейса на ЖК-дисплее.
Но я озадачен этими инструкциями, чтобы просто читать и печатать данные из памяти программы:
strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
Serial.println( buffer );
С какой стати я должен скопировать этот чертов контент в оперативную память перед тем, как получить к нему доступ? И если это правда, что происходит со всем кодом тогда? Он также загружается в оперативную память перед выполнением? Как тогда обрабатывается код (32 кБ) только с 2 кБ ОЗУ? Где эти маленькие гоблины с дискетами?
И еще интереснее: что происходит с буквальными константами, как в этом выражении:
a = 5*(10+7)
5, 10 и 7 действительно копируются в ОЗУ перед загрузкой их в регистры? Я просто не могу в это поверить.
источник
string_table
массива. Этот массив может иметь размер 20 КБ и никогда не помещается в память (даже временно). Однако вы можете загрузить только один индекс, используя вышеуказанный метод.Ответы:
AVR - это модифицированное семейство гарвардской архитектуры , поэтому код хранится только во флэш-памяти, тогда как при манипулировании данные существуют в основном в оперативной памяти.
Имея это в виду, давайте ответим на ваши вопросы.
Само по себе это не нужно, но по умолчанию код предполагает, что данные находятся в ОЗУ, если только код не изменен, чтобы специально искать его во флэш-памяти (например, с помощью
strcpy_P()
).Нет. Гарвардская архитектура. Смотрите страницу Википедии для получения полной информации.
Преамбула, сгенерированная компилятором, копирует данные, которые должны быть модифицированы / модифицированы в SRAM перед запуском фактической программы.
Не знаю. Но если ты увидишь их, я ничем не смогу помочь.
Неа. Компилятор оценивает выражение во время компиляции. Что бы ни случилось, зависит от строк кода вокруг него.
источник
const uint8_t test1[5]= { 0x54, 0x65, 0x73, 0x74, 0x31 }; const uint8_t bla[9]= { 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x62 }; const uint8_t Menu[4]= { 0x3d, 0x65, 0x6e, 0x75};
как перенести эти данные во флэш-память, а затем в функцию SPI.transfer (), которая принимает один вызов uint8_t за вызов.Вот как
Print::print
печатается из памяти программы в библиотеке Arduino:__FlashStringHelper*
является пустым классом, который позволяет перегруженным функциям, таким как print, дифференцировать указатель на программную память от одной до обычной памяти, так как обе эти функции видятсяconst char*
компилятором (см. /programming/16597437/arduino-f- что делает на самом деле )Таким образом, вы можете перегрузить
print
функцию для вашего ЖК-дисплея так, чтобы она принимала__FlashStringHelper*
аргумент, давала его вызватьLCD::print
, а затем использовалаlcd.print(F("this is a string in progmem"));' to call it.
F () `- макрос, который гарантирует, что строка находится в памяти программы.Для предопределения строки (чтобы быть совместимым со встроенной печатью Arduino) я использовал:
Я думаю, что альтернативой будет что-то вроде
что позволит избежать
__FlashStringHelper
броска.источник
Все константы изначально находятся в памяти программы. Где еще они будут, когда питание отключено?
Это на самом деле гарвардская архитектура .
Вы не Фактически существует аппаратная инструкция (LPM - Загрузка памяти программ), которая перемещает данные непосредственно из памяти программ в регистр.
У меня есть пример этой техники в выводе Arduino Uno на монитор VGA . В этом коде есть растровый шрифт, хранящийся в памяти программы. Он читается на лету и копируется в вывод следующим образом:
Разборка этих строк показывает (частично):
Вы можете видеть, что байт памяти программ был скопирован в R30, а затем немедленно сохранен в регистр USART UDR0. ОЗУ не задействовано.
Однако есть сложность. Для обычных строк компилятор ожидает найти данные в оперативной памяти, а не в PROGMEM. Это разные адресные пространства, поэтому 0x200 в ОЗУ отличается от 0x200 в PROGMEM. Таким образом, компилятор сталкивается с проблемой копирования констант (например, строк) в ОЗУ при запуске программы, поэтому ему не нужно беспокоиться о том, чтобы узнать разницу позже.
Хороший вопрос. Вам не сойдет с рук более 2 КБ постоянных строк, потому что не будет места, чтобы скопировать их все.
Вот почему люди, которые пишут такие вещи, как меню и другие многословные вещи, делают дополнительные шаги, чтобы присвоить строкам атрибут PROGMEM, который запрещает их копирование в ОЗУ.
Если вы добавите атрибут PROGMEM, вы должны будете предпринять шаги, чтобы сообщить компилятору, что эти строки находятся в другом адресном пространстве. Создание полной (временной) копии - один из способов. Или просто печатайте напрямую из PROGMEM, по байтам за раз. Примером этого является:
Если вы передаете этой функции указатель на строку в PROGMEM, она выполняет «специальное чтение» (pgm_read_byte), чтобы извлечь данные из PROGMEM, а не из ОЗУ, и распечатать их. Обратите внимание, что это занимает один дополнительный тактовый цикл на байт.
Нет, потому что они не должны быть. Это скомпилирует в инструкцию «загрузить литерал в регистр». Эта инструкция уже есть в PROGMEM, поэтому с литералом теперь разбираются. Не нужно копировать его в оперативную память, а затем читать обратно.
У меня есть длинное описание этих вещей на странице. Сохранение постоянных данных в памяти программ (PROGMEM) . Это пример кода для настройки строк и массивов строк, достаточно легко.
Здесь также упоминается макрос F (), который является простым способом простой печати из PROGMEM:
Небольшая сложность препроцессора позволяет скомпилировать его во вспомогательную функцию, которая извлекает байты в строке из PROGMEM по одному байту за раз. Промежуточное использование оперативной памяти не требуется.
Эту технику достаточно просто использовать для других целей, кроме последовательного (например, для вашего ЖК-дисплея), выводя печать из класса «Печать».
Например, в одной из библиотек LCD, которые я написал, я сделал именно это:
Ключевым моментом здесь является получение из Print и переопределение функции «write». Теперь ваша переопределенная функция делает все, что нужно для вывода символа. Поскольку он получен из Print, теперь вы можете использовать макрос F (). например.
источник