Каким может быть раздел .data + .text = Сколько флэш-памяти будет потреблять конечная программа
Мне не удалось понять один момент, связанный с кодом Arduino.
Предположим, у меня есть следующий код
void setup()
{
Serial.begin(9600);
Serial.println("foo");
}
void loop()
{
}
Когда я компилирую приведенный выше код для Arduino Uno. Arduino IDE дает.
Sketch использует 1478 байт (4%) дискового пространства программы. Максимум 32256 байты. Глобальные переменные используют 192 байта (9%) динамической памяти, оставляя 1856 байт для локальных переменных. Максимум — 2048 байт.
Я проверяю сгенерированный файл с помощью avr-size
.
.data 26 8388864
.text 1452 0
.bss 166 8388890
.comment 17 0
.note.gnu.avr.deviceinfo 64 0
.debug_aranges 208 0
.debug_info 6150 0
.debug_abbrev 2721 0
.debug_line 2239 0
.debug_frame 504 0
.debug_str 1770 0
.debug_loc 1908 0
.debug_ranges 80 0
Как видите, .data + .text = занимает место для хранения программы. Затем я проверил этот документ. https://courses.cs.washington.edu/courses/ csep567/04sp/pdfs/avr-libc-user-manual.pdf. Там говорится о avr-size
Note:
The avr-size program (part of binutils), coming from a Unix background,
doesn’t account for the .data initialization space added to the .text section, so in
order to know how much flash the final program will consume, one needs to add
the values for both, .text and .data (but not .bss), while the amount of pre-allocated
SRAM is the sum of .data and .bss.
Затем я проверяю содержимое раздела .data с помощью
avr-objdump.exe -s -j .data <my.elf>
Тогда я получил
Contents of section .data:
800100 00000000 ff005f00 8c004c01 bd009b00 ......_...L.....
800110 af00666f 6f000d0a 0000 ..foo.....
Я не понимаю, что на самом деле означает приведенная выше цитата? "фу"; хранится в разделе .data, но зачем мне добавлять этот раздел в раздел .text, чтобы подсчитать, сколько потребляет флэш-память?
@Volkan Ünal, 👍2
Обсуждение2 ответа
Значения ваших инициализированных данных (.data) хранятся во флэш-памяти вместе с вашим кодом. Инициализированные данные копируются в ОЗУ после каждой загрузки, сброса или включения питания, прежде чем ваш код начнет выполняться.
Вот почему F («макро») и перегруженные методы в ядре Arduino существуют, чтобы избежать этого «спама» в оперативной памяти. Однако Atmega4809 в этом не нуждается, поскольку вся память отображена в единое адресное пространство. Или платам на базе ARM это не нужно., @KIIV
@JRobert, интересное поведение. Значит все в сегменте .data ест из области флэша, да?, @Volkan Ünal
@embeddedstack, флэш-память в MCU — это «жесткий диск»., @Juraj
Да — сегмент .data содержит переменные, для которых вы указали начальные значения. Эти значения должны быть сохранены в энергонезависимой памяти для инициализации переменных в ОЗУ перед началом выполнения., @JRobert
Просто чтобы расширить то, что сказал Дж.Роберт: инициализируемые переменные и другие литералы (например, слово «foo» в вашем примере) должны откуда-то взяться и, таким образом, храниться в программной (флэш-памяти) и копироваться. в ОЗУ как часть инициализации C++.
Однако неинициализированные переменные не занимают флэш-память. Сравните:
char foo [1000];
void setup()
{
Serial.begin(9600);
Serial.println("foo");
Serial.print (foo);
}
void loop()
{
}
Это отчет для Uno:
Sketch uses 1498 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 1192 bytes (58%) of dynamic memory, leaving 856 bytes for local variables. Maximum is 2048 bytes.
Однако если вы инициализируете глобальную переменную foo следующим образом:
char foo [1000] = { 1, 2, 3, 4, 5 };
void setup()
{
Serial.begin(9600);
Serial.println("foo");
Serial.print (foo);
}
void loop()
{
}
Теперь флеш занимает на 1000 байт больше:
Sketch uses 2498 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 1192 bytes (58%) of dynamic memory, leaving 856 bytes for local variables. Maximum is 2048 bytes.
Это ясно показывает, что, поскольку неинициализированные переменные в глобальной области видимости установлены в ноль, компилятор сгенерировал код именно для этого. Однако инициализированные переменные необходимо скопировать из флэш-памяти в оперативную память.
Очень полезный метод при использовании этих чипов с небольшим объемом оперативной памяти — использовать букву «F». макрос, который фактически указывает компилятору копировать строки непосредственно из флэш-памяти, а не в ОЗУ, тем самым экономя пространство ОЗУ. Например:
Serial.println (F("Hello, world"));
В этом случае строка «Привет, мир» все равно должен храниться во флэш-памяти, но не копироваться в оперативную память, что позволяет экономить драгоценную память для других целей.
Запись инициализированных массивов во флеш
Я долго обсуждал использование PROGMEM для хранения данных, а не просто строковых букв для печати на мой форум.
В этом примере кода показано, как можно хранить сообщения в PROGMEM (флэш-памяти) и извлекать их во время выполнения. Он использует pgm_read_word
для чтения указателей, а затем strcpy_P
для копирования строки из PROGMEM в ОЗУ, чтобы затем ее можно было распечатать.
const int NUMBER_OF_ELEMENTS = 10;
const char Message0000 [] PROGMEM = "Twas bryllyg, and ye slythy toves";
const char Message0001 [] PROGMEM = "Did gyre and gymble";
const char Message0002 [] PROGMEM = "in ye wabe:";
const char Message0003 [] PROGMEM = "All mimsy were ye borogoves; And ye mome raths outgrabe.";
const char Message0004 [] PROGMEM = "\"Beware the Jabberwock, my son! \n The jaws that bite, the claws that catch!";
const char Message0005 [] PROGMEM = "Beware the Jubjub bird, and shun\n The frumious Bandersnatch!\"";
const char Message0006 [] PROGMEM = "He took his ";
const char Message0007 [] PROGMEM = "vorpal sword in hand:";
const char Message0008 [] PROGMEM = "Long time the manxome foe he sought - ";
const char Message0009 [] PROGMEM = "So rested he by the Tumtum tree, \n And stood awhile in thought.";
const char * const messages[NUMBER_OF_ELEMENTS] PROGMEM =
{
Message0000,
Message0001,
Message0002,
Message0003,
Message0004,
Message0005,
Message0006,
Message0007,
Message0008,
Message0009,
};
void setup ()
{
Serial.begin (115200);
Serial.println ();
unsigned int count = 0;
for (int i = 0; i < NUMBER_OF_ELEMENTS; i++)
{
char * ptr = (char *) pgm_read_word (&messages [i]);
char buffer [80]; // должно быть достаточно большим!
strcpy_P (buffer, ptr);
Serial.println (buffer);
count += strlen (buffer) + 1;
} // конец цикла for
Serial.print (F("String memory used = "));
Serial.println (count);
} // конец настройки
void loop () { }
Этот конкретный пример не использует много оперативной памяти:
Sketch uses 2220 bytes (6%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
Спасибо за ваш вклад., @Volkan Ünal
- Arduino EEPROM сохраняет старые данные после прошивки новой программой
- Как функция/метод может определить, является ли передаваемый массив const PROGMEM (flash) или нет (RAM)?
- AVR-GCC не может вызвать функцию, расположенную по фиксированному адресу
- В чем разница/связь между Arduino и AVR?
- Как преобразовать скетч примера Arduino в полный проект C++?
- Не удается записать загрузчик через Arduino Nano clone ISP в atmega328p
- Больше не могу прошить ESP8266 (NodeMCU)
- Поскольку double и float представляют один и тот же тип данных (обычно), что предпочтительнее?
Как вы думаете, где хранятся эти строковые литералы?, @KIIV
@KIIV в SRAM ?, @Volkan Ünal
SRAM энергозависим, то есть не может ничего запомнить без питания., @KIIV