Ich wollte einige Werte im EEPROM speichern und wollte auch SRAM freigeben, indem ich einige Variablendeklarationen vermeide, aber der EEPROM-Speicher ist byteweise.
Wenn ich einen int-Wert speichern möchte, muss ich einige Ausdrücke wiederholt verwenden. Ich dachte, ich würde einige Funktionen für diese machen. Ich befürchte jedoch, dass beim Erstellen einer Funktion der SRAM-Speicher weiterhin belegt ist. Besser, ich deklariere eine int-Variable, anstatt das EEPROM zu verwenden.
Wie werden die Funktionen und die lokalen Variablen im SRAM gespeichert? Speichert es nur die Adresse des Funktionszeigers aus dem Flash-Speicher oder werden alle Variablen und Befehle auf dem Stapel gespeichert?
sram
eeprom
memory-usage
Nafis
quelle
quelle
Antworten:
Nur die Daten der Funktion werden auf dem Stapel gespeichert. Der Code bleibt im Flash. Sie können die SRAM-Nutzung nicht wirklich reduzieren, indem Sie stattdessen das EEPROM verwenden, da das EEPROM, wie Sie gesehen haben, nicht auf die gleiche Weise adressierbar ist. Der Code zum Lesen und Speichern des EEPROM muss auch einen SRAM verwenden - wahrscheinlich so viel SRAM, wie Sie speichern wollten! Das EEPROM ist auch langsam zu schreiben und hat eine begrenzte Lebensdauer (in Anzahl der Schreibvorgänge pro Byte). Beides macht es unpraktisch, die Art der temporären Daten zu speichern, die wir normalerweise auf den Stapel legen. Es eignet sich besser zum Speichern selten geänderter Daten, z. B. der einzigartigen Gerätekonfiguration für Geräte in Massenproduktion, oder zum Erfassen seltener Fehler für spätere Analysen.
Bearbeitet: Es gibt keinen Stapel für diese Funktion, bis die Funktion aufgerufen wurde. Ja, dann werden die Daten der Funktion dort abgelegt. Nach der Rückkehr der Funktion wird der Stapelrahmen (der reservierte Bereich des SRAM) nicht mehr reserviert. Es wird eventuell von einem anderen Funktionsaufruf wiederverwendet. Hier ist ein Diagramm eines C-Stapels im Speicher. Wenn ein Stapelrahmen nicht mehr nützlich ist, wird er einfach freigegeben und sein Speicher kann wieder verwendet werden.
quelle
Lokale Variablen und Funktionsparameter werden auf dem Stapel gespeichert. Dies ist jedoch kein Grund, sie nicht zu verwenden. Computer sind so konzipiert, dass sie so funktionieren.
Der Stapelspeicher wird nur verwendet, wenn eine Funktion aktiv ist. Sobald die Funktion zurückkehrt, wird der Speicher freigegeben. Stapelspeicher ist eine gute Sache.
Sie möchten keine rekursiven Funktionen mit vielen Rekursionsebenen verwenden oder viele große Strukturen auf dem Stapel zuweisen. Der normale Gebrauch ist jedoch in Ordnung.
Der 6502-Stack ist nur 256 Byte groß, aber der Apple II funktioniert einwandfrei.
quelle
Der AVR (die Mikrocontroller-Familie, die traditionell auf Arduino-Boards verwendet wird) ist eine Harvard-Architektur , dh ausführbarer Code und ausführbare Variablen befinden sich in zwei separaten Speichern - in diesem Fall Flash und SRAM. Der ausführbare Code verlässt niemals den Flash-Speicher.
Wenn Sie eine Funktion aufrufen, wird die Rücksprungadresse normalerweise an den Stapel verschoben. Die Ausnahme ist, wenn der Funktionsaufruf am Ende der aufrufenden Funktion erfolgt. In diesem Fall wird stattdessen die Rücksprungadresse der Funktion verwendet, die die aufrufende Funktion aufgerufen hat - sie befindet sich bereits auf dem Stapel.
Ob andere Daten auf den Stapel gelegt werden, hängt vom Registerdruck in der aufrufenden Funktion und in der aufgerufenen Funktion ab. Register sind der Arbeitsbereich der CPU, der AVR verfügt über 32 1-Byte-Register. Auf die Register kann direkt über CPU-Anweisungen zugegriffen werden, während Daten im SRAM zuerst in Registern gespeichert werden müssen. Nur wenn Argumente oder lokale Variablen zu groß oder zu viele sind, um in Register zu passen, werden sie auf den Stapel gelegt. Strukturen werden jedoch immer auf dem Stapel gespeichert.
Einzelheiten zur Verwendung des Stacks durch den GCC-Compiler auf der AVR-Plattform finden Sie hier: https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout
Lesen Sie die Abschnitte "Frame Layout" und "Calling Convention". .
quelle
Unmittelbar nach dem Aufrufen eines Funktionsaufrufs in die Funktion wird als erster Code der Stapelzeiger um einen Betrag dekrementiert, der dem Platz entspricht, der für temporäre Variablen innerhalb der Funktion erforderlich ist. Das Geniale daran ist, dass alle Funktionen daher wiedereintrittsfähig und rekursiv werden, da ihre Variablen auf dem Stapel des aufrufenden Programms basieren. Das heißt, wenn ein Interrupt die Ausführung eines Programms anhält und die Ausführung an ein anderes überträgt, kann auch er dieselbe Funktion aufrufen, ohne dass sie sich gegenseitig stören.
quelle
Ich habe mich sehr bemüht, ein Beispiel für Code zu erstellen, um zu demonstrieren, was die hervorragenden Antworten hier aussagen, bisher ohne Erfolg. Der Grund ist, dass der Compiler die Dinge aggressiv optimiert. Bisher haben meine Tests den Stack überhaupt nicht verwendet, selbst mit lokalen Variablen in einer Funktion. Die Gründe sind:
Der Compiler kann in-line den Funktionsaufruf, so dass die Absenderadresse nicht geschoben überhaupt auf die Stapel werden könnte. Beispiel:
void foo (byte a) { digitalWrite (13, a); } void loop () { foo (5); }
Der Compiler macht daraus:
void loop () { digitalWrite (13, 5); }
Kein Funktionsaufruf, kein Stack verwendet.
Der Compiler kann Argumente in Registern übergeben , so dass er sie nicht auf den Stapel schieben muss. Beispiel:
digitalWrite (13, 1);
Kompiliert in:
158: 8d e0 ldi r24, 0x0D ; 13 15a: 61 e0 ldi r22, 0x01 ; 1 15c: 0e 94 05 01 call 0x20a ; 0x20a <digitalWrite>
Die Argumente werden in Register eingetragen und somit wird kein Stapel verwendet (abgesehen von der Rücksprungadresse für den Aufruf von digitalWrite).
Der Compiler optimiert nicht verwendete Variablen. Beispiel:
void foo (byte a) { unsigned long bar [100]; bar [1] = a; digitalWrite (9, bar [1]); } void loop () { foo (3); } // end of loop
Nun , da die bekommen 400 Bytes zuzuteilen für „bar“ oder nicht? Nee:
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
Der Compiler hat das gesamte Array optimiert ! Es kann sagen, dass wir wirklich nur ein machen
digitalWrite (9, 3)
und das ist es, was es erzeugt.Moral der Geschichte: Versuchen Sie nicht, den Compiler zu überdenken.
quelle