Wie kann das sein?
Zuerst sei gesagt, daß es nicht an MPLAB liegt, sondern am C18. Für lokale Variablen und die Parameter-Übergabe an Funktionen reserviert der C18 einen Software-Stack von 256 Bytes (eine Bank), sofern der Debugger nicht aktiviert ist. Aus dem Linkerscript:
// File: 18f4320_g.lkr
// Generic linker script for the PIC18F4320 processor
#IFDEF _CRUNTIME
SECTION NAME=CONFIG ROM=config
#IFDEF _DEBUGDATASTART
STACK SIZE=0x80 RAM=gpr0
#ELSE
STACK SIZE=0x100 RAM=gpr1 // Bank1 mit 0x100 = 256 Bytes wird
// für den Stack reserviert
#FI
#FI
Die restlichen 12 Bytes sind vom Compiler für interne Zwecke reserviert, die in Deinem Beispielprogramm eventuell noch nicht benötigt werden, aber in jeder „real world“ Anwendung verwendet werden.
Wenn ich mir das ASM-Listing ansehe, so macht der Chip irgendwas am Anfang, was ich ihm eigentlich nicht sage(zumindest nicht wissentlich ;-)
Das ist der Startup-Code, den Du im Installationsverzeichnis des C18 unter
\src\traditional\startup\ findest (bzw.
\src\extendend\startup\, wenn Du den Extended-Mode des PICs verwendest).
Welche Datei konkret verwendet wird, hängt von den Einstellungen des Compilers ab. Wenn er Variablen beim Start initialisieren soll (müßte die Standardeinstellung des C18 sein), wird „c018i*“ verwendet. Ohne Intitialisierung jeglicher globalen Variablen (nicht wirklich sinnvoll in C) wird „c018*“ verwendet. Bei standardkonformer Einstellung des C18 findet „c018iz*“ Anwendung.
Zur Erklärung von „standardkonform“: Der ANSI-C Standard fordert, daß alle globalen Variablen (definiert außerhalb jeder Funktion) und alle statischen Variablen ohne explizite Wertzuweisung mit „0“ initialisiert werden.
unsigned char foo; // globale Variable
static unsigned char bar; // statische Variable
Gemäß ANSI-C hätten die beiden obigen Variablen den Wert „0“. Beim C18 in dessen Standardeinstellung ist deren Wert hingegen undefiniert. Um einen definierten Wert in der Standardeinstellung des C18 zu erhalten, muß die „0“ explizit angegeben werden:
unsigned char foo = 0;
static unsigned char bar = 0;
Und was kann ich dagegen tun?
Die einfachste Maßnahme wäre, im Linkerscript den für den Stack reservierten RAM-Bereich zu verkleinern (dazu das Linkerscript in das Projektverzeichnis kopieren, dem Projekt hinzufügen und
STACK SIZE entsprechend anpassen).
Dabei mußt Du sicherstellen, daß selbst für den ungünstigsten „call path“ der Stack nicht überläuft (d.h. das mehr Bytes für die Übergabe von Parametern und lokale Variablen benötigt werden, als im reservierten Bereich aufgenommen werden können). Eine große Anzahl von lokalen Variablen oder eine Funktion, welche sehr viele andere Funktionen mit mehreren Funktionsargumenten aufruft, sind dabei natürlich zu vermeiden.
Eine weitere Möglichkeit wäre die Verwendung der Compiler-Option „-sco“. Dann sind die Funktionsparameter automatisch vom Typ „overlay“ und die lokalen Variablen „static“ und der Stack wird nicht mehr benötigt. Daraus resultierende Nachteile/Probleme siehe
hier.
Generell kann man den Stack-Bedarf dadurch minimieren, daß Funktionsparameter als overlay und lokale Variablen als „static“ definiert werden. Durch „overlay“ entstehen überhaupt keine Nachteile, da der für die Funktionsparameter reservierte Speicher mehrfach verwendet werden kann (der Speicherbedarf wird nur vom Stack in das „normale“ RAM verschoben). „static“ hingegen reserviert die lokalen Variablen einer Funktion für die Laufzeit des gesamten Programms, d.h. dieser Bereich des RAMs kann von keiner anderen Funktion genutzt werden.
Der Ansatz mit „overlay“ allein (bei vielen Funktionsargumenten oder großer Aufruftiefe) ist am vielversprechendsten in Bezug auf den RAM-Bedarf und der Compiler-Einstellung „-sco“ i.A. vorzuziehen.
Viele Grüße
Bernd