Пространство, занимаемое глобальными переменными

Используя Nano для небольших программ, я получаю сводные отчеты о количестве места, занимаемого глобальными переменными.

В типичной программе он говорит что-то вроде Sketch использует 16316 байт (53%) пространства для хранения программы. Максимум - 30720 байт. Глобальные переменные используют 1798 байт (87%) динамической памяти, оставляя 250 байт для локальных переменных. Максимум - 2048 байт. Мало доступной памяти, могут возникнуть проблемы со стабильностью.

Если я подсчитаю количество байтов, занятых объявленными глобальными переменными, то это будет 126 байт, в зависимости от того, как bool занимает память ( один бит или один байт ).

Могу ли я заставить компилятор дать мне дамп того, что он на самом деле назначает в качестве глобального пространства переменных, чтобы согласовать разницу между 126 байтами и 1798 байтами? Тогда я мог бы изменить программу, чтобы обойти проблему с памятью, и продолжать использовать Нано.

Заранее спасибо за любую полезную информацию.

Спасибо тем, кто прокомментировал, за помощь.

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

, 👍2

Обсуждение

Начните с пустого скетча и посмотрите его требования к оперативной памяти. Затем добавьте Serial.begin(9600); и обратите внимание на разницу. Ваши 1798 байт, безусловно, используются какой-то библиотекой. И да, есть инструмент **avrdump** , но не такой простой в использовании., @DataFiddler

Если вы погрузитесь в уровни командной строки, вы можете попросить компоновщика сгенерировать файл карты. Однако это не тривиально для новичка., @the busybee

Вот подсказка для будущего использования: эта форма (обратите внимание на макрос 'F') `Serial.print(F("Ваше сообщение здесь.\n")); ' сохранит текст сообщения во флэш - памяти вместо оперативной памяти. Это будет работать для любого объекта, использующего библиотеку печати. Хотя ваша забота еще не заключалась в нехватке оперативной памяти, в более крупной программе добавление текстовых сообщений может вызвать различные виды сбоев вне оперативной памяти., @JRobert

Пожалуйста, *не отвечайте* на вопросы в комментариях. Под ним есть хорошая большая коробка, куда идут **ответы**. @Harry - если вы сами найдете ответ (как вам это кажется), не стесняйтесь сделать ответ самостоятельно., @Nick Gammon


1 ответ


1

Оперативная память драгоценна на микроконтроллере, PROGMEM (программная память) - не так уж и много. На Arduino Uno и Nano (с использованием ATmega328P) у вас есть:

  • 1 кБ EEPROM
  • 2 Кб оперативной памяти
  • 32 Кб PROGMEM

Очевидно, что программной памяти гораздо больше, чем оперативной.

Переменные копируются в оперативную память при запуске программы

Проблема с константами в целом (включая строковые константы, такие как "hello") заключается в том, что компилятор генерирует код для копирования их в ОЗУ при запуске программы, поскольку большинство функций C не ожидают найти данные в PROGMEM. (Он должен генерировать различные инструкции для доступа к PROGMEM).

Использование эталонной памяти

Чтобы проверить использование памяти, я начну с скетча, который просто возвращает, сколько памяти свободно, не делая ничего другого. Тестировался на IDE 1.0.6, Arduino Uno, на Ubuntu.

#include "memdebug.h"

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  Serial.print (F("Free memory = "));
  Serial.println (getFreeMemory ());
  }  // end of setup

void loop () { } 

Выход:

Free memory = 1702

Поскольку мы начали с 2048 байт оперативной памяти, мы уже использовали 2048-1702 = 346 байт. Они используются следующим образом:

34 bytes for the HardwareSerial instance (Serial)
64 bytes for the Serial transmit buffer
64 bytes for the Serial receive buffer
4 bytes for the Serial transmit buffer head and tail pointers
4 bytes for the Serial receive buffer head and tail pointers
9 bytes for keeping track of millis / micros
4 bytes for memory allocation (__malloc_heap_start, __malloc_margin)
128 bytes for the heap safety margin
6 bytes for a few nested function calls (main -> setup -> getFreeMemory)
16 bytes for the compiler vtable for HardwareSerial
4 bytes for variables __brkval and __flp (used in memdebug)
2 bytes pushed onto the stack in main (to save registers)
2 bytes pushed onto the stack in setup (to save registers)
4 bytes pushed onto the stack in getFreeMemory (to save registers)
1 byte because the stack pointer starts at 0x8FF rather than 0x900

(Это 346 байт на счету)

Советы

Стек начинается с самого высокого возможного адреса и растет вниз. Каждый вызов функции добавлял бы 2 байта в стек (обратный адрес) плюс любые локальные (автоматические) переменные были бы выделены там.

Запас прочности кучи-это буфер между верхней частью кучи и нижней частью стека. Без него, если бы вы сделали "malloc" и получили всю доступную память, в функциях не осталось бы памяти для вызовов функций и локальных переменных.

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

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

Константы печати

Давайте выведем константу, добавив эту строку:

  Serial.println ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo");

Вывод теперь таков:

Free memory = 1630
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo

Таким образом, свободная оперативная память сократилась на 72 байта, что соответствует длине этой строки (позволяя один байт для терминатора 0x00 в конце).

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

Первое, что мы можем сделать, это использовать макрос F (), который хитро расширяется для печати непосредственно из PROGMEM, тем самым экономя оперативную память. Поэтому мы меняем строку выше на следующую:

  Serial.println (F("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo"));

Выход:

Free memory = 1702
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo

Вернемся к 1702 байтам свободной памяти. Итак, урок № 1 состоит в том, чтобы использовать макрос F (), как показано выше, при печати констант.

Более подробная информация

У меня есть больше информации на моей странице об использовании оперативной памяти, которая обсуждает размещение массивов констант в PROGMEM, а также то, что я упоминал выше.

,