Hallo Leute.
Ich habe gestern in meinem inzwischen nicht mehr ganz so kleines
Programm (für einen 90CAN128) eine existierende case - Schleife um 4
Anweisungen ergänzt.
Die Anweisungen in der Schleife sehen etwa alle gleich aus.
case 23: strcpy((char*)&bufferstring[0],(char*)&serialstring[4]);
var23 = atoi(bufferstring);
write_var23();
send_string_uart0("OK\n\r");
break;
Alle Variablen in dieser Funktion kommen aus dem main.c und sind dort
global definiert. Am Anfang dieser Funktion befindet sich entsprechend
noch die Deklaration dieser Variablen
extern int var23;
Danach ging in dem Programm nix mehr. Das Display flackert nur noch
wild, gelegentlich gibt es einen Reset.
Die Case-Anweisung wird zu diesem Zeitpunkt nicht durchlaufen.
Ich tippe mal auf Stack-Überlauf, denn wenn ich die Zeilen wieder
entferne geht alles wie gewohnt.
Das führt uns zu folgenden Fragen:
Extern deklarierte Variable verbrauchen Programmspeicher?
Permanent?
Wie sieht es dann mit lokalen Variablen aus?
Wenn externe Variable sich am Programmspeicher bedienen, gibt es dann
auch einen Weg, dies zu verhindern?
>Extern deklarierte Variable verbrauchen Programmspeicher?
Ja
>Permanent?
Ja
>Wie sieht es dann mit lokalen Variablen aus?
Radio Eriwan. Die benötigen entweder Programmspeicher, solange sie
existieren, oder sie werden in einem Prozessorregister gehalten (wenn
sie klein genug sind, und ausreichen Register im Prozessor frei sind).
>Wenn externe Variable sich am Programmspeicher bedienen, gibt es dann>auch einen Weg, dies zu verhindern?
Nicht wirklich. Das Wesen einer Variablen ist nunmal, einen
Speicherbereich darzustellen.
Oliver
<<Nicht wirklich.>>
Und wenn ich alles ins Main.c stopfe mit dem Pferdefuß einen
unübersichtlichen Programmcode zu erzeugen aber dafür den Speicher zu
sparen?
@ Wolfram
Der Programmteil wird nicht ausgeführt. Es kommt schon vorher zum
Fehler, ohne das
strcpy((char*)&bufferstring[0],(char*)&serialstring[4]); überhaupt zur
Ausführung kommt.
>Und wenn ich alles ins Main.c stopfe mit dem Pferdefuß einen>unübersichtlichen Programmcode zu erzeugen aber dafür den Speicher zu>sparen?
Es gibt nur ein Programm, und nur ein Ram. Da ist alles drin. Es ist
völlig egal, ob du deinen Surce-Code in eine oder in 100 Dateien
schreibst, und es ist auch völlig egal, wie oft du darin eine Variable
als extern derklarierst.
int var23; benötigt exakt 2 byte Ram.
extern int var23; benötigt gar nichts, das gibt dem Compiler nur die
Möglichkeit, diese Source-Datei zu Kompilieren, mit dem Hinweis, das
var23 irgendwo anders deklaiert wird. Der Linker macht dann den Rest.
Wenn du allerdings gar keine Variablen hinzugefügt hast (var23 gabs
schon vorher), dann darf eigentlich nichts passieren. Daher ist die von
Wolfram geäusserte Vermutung, daß da igend was anderes queschiesst, gar
nicht so abwegig.
Jetzt ist Hardcore-Debugging angesagt.
Oliver
<<int var23; benötigt exakt 2 byte Ram.
extern int var23; benötigt gar nichts, >>
Ich merke, ich habe mich bei der Fragestellung missverständlich
ausgedrückt, entsprechnd Deine erste Antwort missverstanden.
Das die Case Schleife schuld ist, kann ich mir nicht vorstellen.
Immerhin stehen schon 22 identische Anweisungen davor, nur eben für
var01 - var22.
Ok, da hatte ich die Frage anders verstanden.
Aber trotzdem: Ein xx128 hat 4kB SRAM. Das bekommst du mit
Integer-Variablen so schnell nicht voll, und auch
Funktionsverschachtelung führt da so schnell nicht zu Problemen.
Entweder hast du da riesige Arrays, sowas wie
1
intLCD_BUFFER[240][128];// :-))
oder Stringkonstanten im Ram, anstatt im Flash, oder so etwas.
Und die Vermutung mit amoklaufenden Stringfunktionen ist auch noch nicht
vom Tisch.
Oliver
Nun, es ist einiges von diesem Zeug darin,
send_string_uart0("OK\n\r");
aber das sollte doch im Flash stehen, oder?
Und es gibt noch ein großen String für die variablen Ausgaben.
char sendstring[40];
Das ist aber schon die größte und die wird ja auch nicht beeinflusstr
von dem Code in der Case-Schleife
Ein paar von den varXX sind floats, aber das kann es ja auch nicht sein.
...
flyingwolf wrote:
> Nun, es ist einiges von diesem Zeug darin,>> send_string_uart0("OK\n\r");> aber das sollte doch im Flash stehen, oder?
Nein. Warum sollte es?
Die send_string_uart0 muss ganz anders aussehen, wenn der String
im Flash liegt.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Vereinfachung_f.C3.BCr_Zeichenketten_.28Strings.29_im_Flash>> Und es gibt noch ein großen String für die variablen Ausgaben.> char sendstring[40];> Das ist aber schon die größte und die wird ja auch nicht beeinflusstr> von dem Code in der Case-Schleife>> Ein paar von den varXX sind floats, aber das kann es ja auch nicht sein.> ...
Dann könnten es auch noch sonstige Fehler im Programm sein, die
sich jetzt bemerkbar machen.
> send_string_uart0("OK\n\r");> aber das sollte doch im Flash stehen, oder?
Nein. Die Strings werden im RAM gespeichert. Das geht nicht anders, weil
der Prozessor auf Assembler-Ebene unterschiedliche Instruktionen für den
Zugriff auf RAM und Flash braucht. Du kannst sie mit PROGMEM als globale
Variablen definieren und eine zweite Sendefunktion schreiben, um das zu
umgehen.
Was sagt denn avr-size, wieviel RAM statisch belegt ist?
Verstehe ich das richtig?
bei z.B.
if (irgendwas == 1) send_string_uart0("Press any key to continue");
landet der ganze Text automatisch im SRAM, auch wenn die If-Anweisung
gar nicht ausgeführt wird?
Umpf!
Nun, dann weiss ich zumindest wo mein Speicher hin ist.
Wenn ich das Tutorial jetzt richtig deute sollte ich gar nicht ganz
unbedarft
send_string_uart0("Press any key to continue");
schreiben, sondern die Texte in hunderten von Variablen a la
char StringImFlash1[] PROGMEM = "Press any key to continue";
deklarieren und deren Ausgabe dann durch
send_string_uart0(StringImFlash1);
erzeugen?
Gibt es noch eine bestimmte Regel an die ich mich dabei halten muss,
oder genügt es in der entsprechenden Funktion die Texte als Lokale
Variable zu definieren und dann in der oben angezeigten Weise
auszugeben?
Mit bisschen Geschick kannst du das einfacher schreiben als:
1
send_string_uart0_P(PSTR("Press any key to continue"));
Der _P-Suffix ist dabei die übliche Kennzeichnung dafür, dass die
Funktion einen progmem-String bekommt. Die Funktion ist dabei
selbst dafür verantwortlich, die Daten aus dem Progmem zu ziehen
(letztlich mittels LPM-Anweisungen).
Der PSTR-Makro ist ein wenig Magie, die den String in den Progmem
legt und dessen Adresse gleich zurückgibt. Der String selbst bildet
dabei sowas wie eine anonyme Array-Variable.
Aber bevor wir dich noch mehr verwirren arbeite zuerst mal das Oben von
Karl heinz Buchegger angeführte Tutorial durch. Dann wird dir
hoffentlich vieles klarer.
Erst einaml vielen Dank. Da habe ich doch wieder viel dazugelernt. Das
werden wir jetzt mal vertiefen. Zuerst in der einfachen Methode zu Fuss
und dann schauen wir mal ob wir das mit der Magie hinbekommen, aber das
wird wohl ein paar Tage brauchen
flyingwolf wrote:
> Erst einaml vielen Dank. Da habe ich doch wieder viel dazugelernt. Das> werden wir jetzt mal vertiefen. Zuerst in der einfachen Methode zu Fuss> und dann schauen wir mal ob wir das mit der Magie hinbekommen, aber das> wird wohl ein paar Tage brauchen
Das schöne ist, dass du die Magie nicht erfinden
musst.
Du musst nur eine klitzekleine Änderung in deiner send.....
Funktion machen, vor den String ein PSTR und schon bist
du fertig.
Noch ein Hinweis:
Lass um Gottes Willen deine jetzige send.... Funktion in Ruhe
und mache eine zweite Funktion, die du laut üblicher Konvention
send_string_uart0_p
nennst.
<< Du musst nur eine klitzekleine Änderung in deiner send.....
Funktion machen, vor den String ein PSTR und schon bist
du fertig.>>
Ja schon, aber woher hätte ich vorhin ahnen können, dass es so einfach
ist ;-)
Ich habe mich ja auch gleich noch an meiner LCD-Routine vergangen um
selbigem Problem gleich mit auf den Leib zu rücken. Dabei habe ich mich
dieser wunderbaren Vorlage aus dem Tutorial bedient, auch wenn es einen
Moment gedauert hat, bis der Groschen fiel. Nun die Frage
Natürlich hat der Compiler (und nicht ganz zu unrecht) was zu meckern an
der Zeile
while (charcounter = pgm_read_byte(data))
und bemerkt in seiner nüchternen Art
uart.c:87: warning: suggest parentheses around assignment used as truth
value
Kann ich dem Compiler irgendwie sagen, das er an dieser Stelle mal
ausnahmsweise nicht meckern soll?
> while (charcounter = pgm_read_byte(data))> suggest parentheses around assignment used as truth value
erfüll doch dem Compiler seinen Wunsch :-)
Worum gehts denn da.
Ein, besonders bei Einstegern, beliebter Fehler ist es,
wenn man für einen Vergleich anstelle von == ein einfaches
= schreibt.
Da wird dann ein simples
sscanf( "%d", &Count );
if( Count = 1 )
printf( "Sie haben 1 eingegeben" );
schnell zum Drama.
Daher warnen manche Compiler vor solchen Konstrukten.
Wohlwissend, dass es in C durchaus Konstruktionen gibt,
bei denen man tatsächliche eine Zuweisung haben möchte.
Man kann natürlich auch die Langform schreiben:
while( ( charcounter = pgm_read_byte(data) ) != 0 ) {
...
}
und hat damit auch für den neuesten Einsteiger klar gemacht was
Sache ist (oder auch nicht). Aber gcc gibt sich schon zufrieden
wenn du einfach nur die Klammern machst, der explizite Vergleich
muss nicht sein:
while( ( charcounter = pgm_read_byte(data) ) ) {
dann glaubt dir der gcc, dass du wirklich einen Zuweisung
meinst.