Я хотел сохранить некоторые значения в EEPROM, а также хотел освободить SRAM, избегая некоторых объявлений переменных, но память EEPROM является байтовой.
Если я хочу сохранить значение типа int, мне придется использовать некоторые выражения несколько раз. Я думал, что сделаю некоторые функции для них. Но я обеспокоен тем, что, если я создам функцию, она все равно будет занимать память SRAM, лучше я объявляю переменную int вместо использования EEPROM.
Как функции и локальные переменные хранятся в SRAM? Хранит ли он только адрес указателя функции из флэш-памяти или все переменные и команды хранятся в стеке?
sram
eeprom
memory-usage
Нафис
источник
источник
Ответы:
В стеке хранятся только данные функции; его код остается во флэш-памяти. Вы не можете реально уменьшить использование SRAM, используя вместо этого EEPROM, потому что, как вы видели, EEPROM не адресуется таким же образом. Код для чтения и хранения EEPROM также должен использовать SRAM - возможно, столько же SRAM, сколько вы пытались сохранить! ЭСППЗУ также медленно записывается и имеет ограниченное время жизни (по количеству записей в каждый байт), что делает нецелесообразным использование для хранения вида временных данных, которые мы обычно помещаем в стек. Он лучше подходит для сохранения редко изменяемых данных, таких как уникальная конфигурация устройства для серийных устройств, или для регистрации нечастых ошибок для последующего анализа.
Отредактировано: нет стека для этой функции, пока функция не была вызвана, так что да, то есть, когда туда попадают какие-либо данные функции. Что происходит после возврата функции, так это то, что ее стековый кадр (зарезервированная область SRAM) больше не зарезервирован. В конечном итоге он будет повторно использован другим вызовом функции. Вот диаграмма стека C в памяти. Когда кадр стека больше не используется, он просто освобождается, и его память становится доступной для повторного использования.
источник
Локальные переменные и параметры функции хранятся в стеке. Однако это не причина, чтобы не использовать их. Компьютеры предназначены для такой работы.
Память стека используется только при активной функции. Как только функция возвращается, память освобождается. Память стека - ХОРОШАЯ вещь.
Вы не хотите использовать рекурсивные функции с большим количеством уровней рекурсии или размещать много больших структур в стеке. Нормальное использование в порядке однако.
Стек 6502 занимает всего 256 байтов, но Apple II работает просто отлично.
источник
AVR (семейство микроконтроллеров, традиционно используемое на платах Arduino) - это Гарвардская архитектура , означающая, что исполняемый код и переменные находятся в двух отдельных запоминающих устройствах - в данном случае флэш-память и SRAM. Исполняемый код никогда не покидает флэш-память.
Когда вы вызываете функцию, адрес возврата обычно помещается в стек. Исключение составляют случаи, когда вызов функции происходит в конце вызывающей функции. В этом случае будет использоваться адрес возврата функции, вызвавшей вызывающую функцию - он уже находится в стеке.
Помещаются ли какие-либо другие данные в стек, зависит от давления регистра в вызывающей функции и вызываемой функции. Регистры являются рабочей областью ЦП, AVR имеет 32 однобайтовых регистра. Доступ к регистрам может осуществляться напрямую с помощью инструкций ЦП, тогда как данные в SRAM сначала должны быть сохранены в регистрах. Только если аргументы или локальная переменная слишком велики или слишком велики, чтобы поместиться в регистры, они будут помещены в стек. Однако структуры всегда хранятся в стеке.
Подробнее о том, как стек используется компилятором GCC на платформе AVR, можно прочитать здесь: https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout.
Прочитать разделы «Расположение кадра» и «Соглашение о вызовах». ,
источник
Сразу после вызова функции, входящей в функцию, первый код, который выполняется, должен уменьшить указатель стека на величину, равную пространству, требуемому для временных переменных, внутренних для функции. Самое замечательное в этом то, что все функции становятся реентерабельными и рекурсивными, потому что их переменные построены на стеке вызывающей программы. Это означает, что если прерывание останавливает выполнение одной программы и передает выполнение другой, оно также может вызывать ту же функцию, не мешая друг другу.
источник
Я изо всех сил пытался создать пример кода, чтобы продемонстрировать то, что здесь говорят отличные ответы, но пока безуспешно. Причина в том, что компилятор агрессивно оптимизирует вещи. До сих пор мои тесты вообще не использовали стек, даже с локальными переменными в функции. Причины:
Компилятор может рядный вызов функции, таким образом, обратный адрес не может быть в стек на всех. Пример:
void foo (byte a) { digitalWrite (13, a); } void loop () { foo (5); }
Компилятор превращает это в:
void loop () { digitalWrite (13, 5); }
Нет вызова функции, не используется стек.
Компилятор может передавать аргументы в регистрах , тем самым сохраняя его, помещая их в стек. Пример:
digitalWrite (13, 1);
Компилируется в:
158: 8d e0 ldi r24, 0x0D ; 13 15a: 61 e0 ldi r22, 0x01 ; 1 15c: 0e 94 05 01 call 0x20a ; 0x20a <digitalWrite>
Аргументы помещаются в регистры и, таким образом, стек не используется (кроме адреса возврата для вызова digitalWrite).
Компилятор оптимизирует переменные, которые вы не используете. Пример:
void foo (byte a) { unsigned long bar [100]; bar [1] = a; digitalWrite (9, bar [1]); } void loop () { foo (3); } // end of loop
Теперь, когда есть выделить 400 байт для «бар» , не так ли? Нет:
00000100 <_Z3fooh>: 100: 68 2f mov r22, r24 102: 89 e0 ldi r24, 0x09 ; 9 104: 0e 94 cd 00 call 0x19a ; 0x19a <digitalWrite> 108: 08 95 ret 0000010a <loop>: 10a: 83 e0 ldi r24, 0x03 ; 3 10c: 0e 94 80 00 call 0x100 ; 0x100 <_Z3fooh> 110: 08 95 ret
Компилятор оптимизировал весь массив ! Это может сказать, что мы действительно просто делаем
digitalWrite (9, 3)
и это то, что он генерирует.Мораль истории: не пытайтесь перехитрить компилятор.
источник