Forum: Mikrocontroller und Digitale Elektronik Überlauf?


von Alexander L. (alexl)


Lesenswert?

Hallo!

Ich habe ein sehr seltsames Phänomen:

Ich habe einen Mikrocontroller ATMega8535 programmiert. Der Speicher von 
8k ist zu ca. 90% belegt.

Zur Schaltung: Ich habe ein LCD und ein paar Schalter und Messpunkte 
(ADC) verbunden. Die Schaltung läuft soweit.

Jetzt habe ich das Problem, das der Code, der ganz unten im Programm 
steht nicht richtig ausgeführt wird. Kopiere ich den Teil nach oben, so 
funktioniert dieser dann - jedoch der nach unten gerutschte Teil nicht 
mehr. Ich habe alle Variablen mit volatile deklariert... von daher 
sollte doch keine Variable überlaufen können, weil der Compiler den 
Platz dann vorsieht, oder?

Ich weiß nicht mehr weiter. Ich bin fertig mit dem Programm und die 
letzten 10 Zeilen werden nicht richtig ausgeführt. Auch wenn es nur ein 
lcd_put("blabla"); ist. Da werden dann komische Zeichen ausgegeben.

Vielen Dank für eure Antworten!

Alex

von Simon K. (simon) Benutzerseite


Lesenswert?

Wie hoch ist denn der RAM Verbrauch durch statische Variablen?

Hast du viele Aufrufe ala "lcd_put("XYZ")", wo Strings in doppelten 
Anführungszeichen stehen? Diese konstanten Strings werden beim Start des 
Mikrocontrollers in den RAM kopiert, was eigentlich sinnlos ist.

Ich glaube hier liegt dein Problem...

von Alexander L. (alexl)


Lesenswert?

Wie groß der RAM-Verbrauch ist kann ich Dir nicht genau sagen, weil ich 
mich zuwenig damit auskenne. Beschäftige mich noch nicht lange mit MCs. 
Ich kann aber sagen, welche Arten von Variablen ich deklariert habe:

volatile float Spannungs=0;
volatile float Spannung=0;
volatile uint16_t Messwert=0;
volatile uint32_t Widerstand=0;
volatile uint8_t gef=0;
volatile uint8_t l=0;
volatile uint8_t m=1;
volatile uint8_t k=0

und ja - ich habe viele Aufrufe wie lcd_puts("xyz");

Gibt es hierfür eine bessere Alternative?

von Simon K. (simon) Benutzerseite


Lesenswert?

Du bekommst doch sicher eine Ausgabe von den Größenstatistiken nach dem 
Kompiliervorgang, oder nicht? Woher beziehst du sonst deine Angabe mit 
dem belegten Flash(Programm)-Speicher?

Ein guter Ersatz für die "lcd_puts("xyz")" sachen ist, die Strings im 
Flash zu belassen. Hierfür hat der Compiler allerdings keine nativ 
eingebauten Funktionen, da die Compiler für Systeme gemacht sind, wo 
Programmspeicher und Datenspeicher beide im RAM liegen.

Um diese Möglichkeit trotzdem zu benutzen, gibts aber ein paar 
Hilfsmakros und "Ersatz"-Funktionen.
Ein Beispiel ist zum Beispiel der Funktionsaufruf ala 
"printf_P(PSTR("%u"), MyVal);"

Zu beachten ist hier das PSTR() Makro, was den String per Sections vom 
Linker in den Flash-Bereich legt. Weiterhin das Suffix _P was für 
"Progmem" (denke ich mal) steht. Dieser Funktion ist es also möglich, 
Strings aus dem Flash auszugeben.

Am besten schaust du dafür mal in die avr-libc Dokumentation. Da ist das 
ganze sehr gut erklärt.

von Alexander L. (alexl)


Lesenswert?

Hallo!

Ich konnte mein Problem lösen. Es gibt eine Funktion lcd_puts_P(). Mit 
dieser werden konstante Strings im Flash abgelegt.

Ja, ich bekomme eine Übersicht. Kann jedoch nicht viel damit anfangen. 
Welcher Bereich ist der Flash und welcher der RAM? Ich denke mal 
Program: ist Flash und Data wird RAM sein, oder? Aber genau deswegen 
verstehe ich nicht, wieso es vorher nicht funktionierte... wenn Data der 
RAM ist, dann hatte ich nur 89% davon belegt. Jetzt, wo die ganzen 
Strings im Flash liegen bin ich bei 25,8%.


AVR Memory Usage
----------------
Device: atmega8535

Program:    7350 bytes (89.7% Full)
(.text + .data + .bootloader)

Data:        132 bytes (25.8% Full)
(.data + .bss + .noinit)


Build succeeded with 0 Warnings...

von Werner B. (Gast)


Lesenswert?

.text ist der reine Programmcode
Der initilisierte Teil der Daten liegt für die Initialisierung auch erst 
einmal im Flash (.data) und wird beim Programmstart in das Ram (.data) 
kopiert.
Beispiel
static int a_number = 4711;
oder auch die "strings" ohne PROGMEM.
.bss sind die nicht initialisierten Daten (bzw. mit 0 initialisierte 
daten) und werden beim Start mit 0 initialisiert.
z.B. static int another_integer;

==> Im flash liegen .text + .data
Im Ram liegen (nach programmstart) .data + .bss + stack

von Simon K. (simon) Benutzerseite


Lesenswert?

Hehe, ja du hast schon richtig geraten. Program ist der Flash (steht 
auch drunter). Data sind die Variablen.

Und jetzt zu deiner Aussage:

> nur 89% davon belegt

Wie ich schon gesagt habe: Die Ausgabe nach der Kompilierung gibt die 
Belegung des Arbeitsspeichers durch statische Variablen an. Wenn du 
dich aber schonmal mit dem C-Compilersystem und seiner 
Speicherverwaltung auseinandergesetzt hast, wirst du sicher auch den 
Stack kennen. Lokale Funktionsvariablen werden auf einer Art Stack 
erzeugt und dort gespeichert. Verlässt man die Funktion, schrumpft der 
Stack wieder (Die Variablen werden wieder "freigegeben").

So, das Programm, was dir nach dem Kompiliervorgang die Speicherbelegung 
anzeigt kann aber eben nur die statische Belegung anzeigen, da sich die 
Größe des Stacks zur Laufzeit verändern kann. Und deshalb musst du 
etwas Reserve neben den statischen Variablen lassen.

Ergo: 89% sind schon zuviel Speicher, der durch statische Variablen 
belegt wird.

Edit: Mist, zu spät :D

PS: Deine _P trifft das Problem genau ins Schwarze. Hast du auch das 
PSTR() Makro nicht vergessen? Oder hast du die Variablem lokal erzeugt 
und per Schlüsselwort "PROGMEM" ins Flash verlagert?

von Alexander L. (alexl)


Lesenswert?

hmm... erst mal Danke für Deine Hinweise - wenn ich auch nicht alle 
verstehe. Eigentlich bin ich auf dem Gebiet C neu. Ich kenne Pascal, 
Object Pascal, PHP und MySQL fast aus dem FF... jedoch was C anbelangt 
wage ich mich auf Neuland... aber ohne Probieren kann man nichts lernen.

Soviel dazu;)

>PS: Deine _P trifft das Problem genau ins Schwarze. Hast du auch das
>PSTR() Makro nicht vergessen? Oder hast du die Variablem lokal erzeugt
>und per Schlüsselwort "PROGMEM" ins Flash verlagert?

Weder noch eingebaut. PROGMEM weiß ich inzwischen was es bedeutet, da 
ich in Google was gefunden habe. Damit schreibt man Variablen in den 
Flash-Speicher um den RAM zu schonen (oder ganze Strukturen). Was PSTR() 
ist weiß ich (noch) nicht. Wahrscheinlich eine Möglichkeit einen String 
im RAM abzulegen (zumindest der Name deutet darauf hin).

Ich nutze die fertige Funktion von Peter Fleurys LCD Ansteuerung um die 
Daten im Flash abzulegen. Diese Funktion heißt lcd_puts_P

Link zum LCD-Modul: http://jump.to/fleury

Dort steht in der Manual:

void lcd_puts_p    (     const char *      progmem_s     )

Display string from program memory without auto linefeed.
Parameters:
      s   string from program memory be be displayed
Returns:
    none

Da ich diese lcd_puts_P Befehle nicht für Variablen sondern nur für 
konstante Strings nutze sollte es funktionieren... zumindest konnte ich 
noch keine Programmfehler beim Testen entdecken... natürlich wäre es 
auch einen Versuch Wert sämtliche Variablen im Flash zu speichern, um 
das Programm noch ein wenig ausbauen zu können.

von Hannes L. (hannes)


Lesenswert?

> natürlich wäre es
> auch einen Versuch Wert sämtliche Variablen im Flash zu speichern,

Nein, ist es nicht (den Versuch wert).

Denn Flash ist nicht für Variablen geeignet, nur für Konstanten.

In Harvard-Architektur gibt es physikalisch getrennte Speicher für 
Programm und Daten. Dabei gehören Daten ins RAM (rechnen wir die 
Register des AVRS der Einfachheit mal mit zum RAM) und Programm ins ROM. 
ROM ist im Laufe der Entwicklung (über PROM, EPROM, OTP) zu Flash 
mutiert, was ein mehrmaliges Beschreiben des Programmspeichers 
ermöglicht. Der Programmspeicher bleibt aber trotzdem ein Speicher für 
Werte, die sich während der Laufzeit NICHT verändern, also für 
Programmcode und Konstanten. Ihn für Variablen (Veränderliche!) nutzen 
zu wollen, wäre sinnfrei.

Du solltest also (zumindest beim AVR) immer korrekt zwischen Konstante 
(deren Inhalt schon zur Entwurfszeit bekannt ist und die sich während 
der Laufzeit nie ändert) und Variable (Speicherzelle mit veränderbarem 
Inhalt) unterscheiden und alle Konstanten möglichst in den 
Programmspeicher legen. Das Anlegen einer Variablen mit Zuweisung eines 
Startwertes entspricht übrigens einer Variable (RAM) und einer Konstante 
(Bereitstellen des Startwertes im Flash). Wie man's nun dem Compiler 
sagt, kann ich Dir nicht sahgen, ich programmiere AVRs in Assembler 
mithilfe des Datenbletts des AVRs.

...

von Simon K. (simon) Benutzerseite


Lesenswert?

Alexander L. wrote:
> Weder noch eingebaut. PROGMEM weiß ich inzwischen was es bedeutet, da
> ich in Google was gefunden habe. Damit schreibt man Variablen in den
> Flash-Speicher um den RAM zu schonen (oder ganze Strukturen). Was PSTR()
> ist weiß ich (noch) nicht. Wahrscheinlich eine Möglichkeit einen String
> im RAM abzulegen (zumindest der Name deutet darauf hin).
>
> Ich nutze die fertige Funktion von Peter Fleurys LCD Ansteuerung um die
> Daten im Flash abzulegen. Diese Funktion heißt lcd_puts_P
>
> Display string from program memory without auto linefeed.
> Parameters:
>       s   string from program memory be be displayed
> Returns:
>     none

Hmpf, wie sieht denn dein Funktionsaufruf auf? etwa
1
lcd_puts_P("Test");

Das dürfte rein theoretisch eigentlich nicht funktionieren. Denn "Test" 
liegt nach dem Prozessorstart im RAM, und lcd_puts_P versucht dann an 
der Stelle, wo der String im RAM liegt, im Flash zu lesen.
Deswegen benutzt man in der Regel das PSTR() Makro. Der String wandert 
dann automatisch ins ROM und kann der Funktion lcd_puts_P() ohne 
Probleme übergeben werden.

PS: Variablen, die im ROM landen sind automatisch mit "const" 
modifiziert. Die könntest du zur Laufzeit nicht mehr ändern. Bloß nicht 
alle Variablen in den Flash. Lies hierzu nochmal Hannes Post...

PPS:
>von daher
>sollte doch keine Variable überlaufen können, weil der Compiler den
>Platz dann vorsieht, oder?

Dafür ist Volatile übrigens nicht da. Du brauchst Volatile nur dann zu 
verwenden, wenn du jegliche Optimierung verhindern willst auf dieser 
Variable. Nötig ist das zB, wenn du eine statische Variable einmal in 
einem Interrupt und einmal in einer anderen Funktion beschreiben/lesen 
willst. Ansonsten macht es den Code wahrscheinlich nur langsamer ;)

von Alexander L. (alexl)


Lesenswert?

Hallo!

Vielen Dank für die Hinweise.

Bedeutet der Aufruf für konstante Strings müsste dann lauten:

lcd_puts_P(PSTR("Mein String"));

Aber wenn mein lcd_puts_P("xyz"); nicht funktioniert wie du sagst, wieso 
ist dann

Data:        132 bytes (25.8% Full)
(.data + .bss + .noinit)

von ca. 89% auf 25,8% geschrumpft? Hat der Compiler evtl. den Fehler 
entdeckt und selbstständig optimiert? Ich nutze -Os.

Gruß,

Alex

von Simon K. (simon) Benutzerseite


Lesenswert?

Nunja, bin jetzt auch kein C-Compiler-Freak, da müsste Karl Heinz, Jörg 
Wunsch oder wie sie nicht alle heißen ran :-))

> lcd_puts_P(PSTR("Mein String"));

Ja, so mache ich es immer, und so steht es in den Doc's. Das sollte so 
richtig sein.


> Aber wenn mein lcd_puts_P("xyz"); nicht funktioniert wie du sagst, wieso
> ist dann

> Data:        132 bytes (25.8% Full)
> (.data + .bss + .noinit)

Hm, die Frage ist berechtigt. Ehrlichgesagt: Keine Ahnung.

von Werner B. (Gast)


Lesenswert?

Ich verwende zwar die fleury rouinen nicht, erinnere mich aber "dunkel" 
dass die _P's dort makros sind explizit ein PSTR() um die strings bauen. 
Am beseten in den zugehörigen headerfiles nachsehen bevor weiter 
spekuliert wird.
Werner

von jemand (Gast)


Lesenswert?

Du hast vollkommen recht:
1
/**
2
 @brief macros for automatically storing string constant in program memory
3
*/
4
#define lcd_puts_P(__s)         lcd_puts_p(PSTR(__s))

von Simon K. (simon) Benutzerseite


Lesenswert?

Joup, alles klar. Dann wäre in diesem Fall sogar die Verwendung von 
PSTR() falsch. Das wusste ich nicht. Dachte der Fleury hält sich da an 
die allgemeine (inoffizielle) Konvention.

von Ulrich (Gast)


Lesenswert?

@simon:
wie lautet die inoffizielle Regel in diesem Fall?

von Simon K. (simon) Benutzerseite


Lesenswert?

Hm? Er hält sich doch nicht dran, also gibts in diesem Falle auch 
keine!

Aber alle anderen PROGMEM Funktionen, die zum Beispiel in der avr-libc 
enthalten sind, erwarten, dass man das PSTR() Makro benutzt.

Dass man dem Herrn Fleury das nicht vorwerfen kann, sollte klar sein. 
Schließlich gibts dafür keine Norm o.ä.. Jeder kann das mit einem 
weiteren Makro definieren (wie in diesem Falle), kann es aber auch sein 
lassen. Es stiftet halt nur etwas Verwirrung.
Deshalb sprach ich von "inoffiziell".

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.