Hallo allerseits,
ich programmiere mit dem Atmel Studio 6.1 einen ATtiny84 der hat 512
Byte RAM. Bisher lief mein kleines Programm ganz gut, dann bin ich auf
die Idee gekommen als erstes nach der Portinitialisierung ein Blümchen
auf den LCD-Bildschirm zu malen.
1
voidBlume(void)
2
{
3
uint32_tScreen[102];// 408 von 512 Byte!!!
4
int16_tA,B,C,D;
5
// der Code hier interressiert jetzt nicht
6
}
Wie man sieht braucht die Blume fast den ganzen RAM des ATtiny84.
Danach sind die Globalen Konstanten verändert:
1
constint32_tmax_wert[4]={1123,9999,99999,3600};
statt dessen steht in den Konstanten:
0xAF590763, 0x0000370D, 0x00000000, 0x00000000
Wie kann das sein?
Es gab keine Compiler Warnung!
Was tun außer Blümchen weg lassen?
Peter Zz schrieb:> Wie kann das sein?
Das Programm braucht auch RAM (Stack).
> Es gab keine Compiler Warnung!
Kann er auch nicht.
> Was tun außer Blümchen weg lassen?
Konstanten in den Flash. Größerer Controller.
> So?
Ja.
Aber wenn du es so machst, dann musst du auch die Zugriffe im Programm
verändern. Du kannst dann die Werte nicht mehr einfach so benutzen,
sondern musst über die Funktion pgm_read_xxxx gehen
AVR-GCC-Tutorial: Flash mit PROGMEM und pgm read
Allerdings muss das dein Problem noch nicht lösen. Wenn es im SRAM eng
wird, dann zerschiesst man sich gerne eine Menge Dinge. Das einzelne
Variablen ihren Wert ändern, ist oft nur die Spitze des Eisbergs.
Im übrigen wäre es vernünftig, nicht die Maximalwerte ins SRAM zu
verschieben, sondern das Blümchen.
Denn
1
voidBlume(void)
2
{
3
uint32_tScreen[102];// 408 von 512 Byte!!!
4
int16_tA,B,C,D;
5
// der Code hier interressiert jetzt nicht
6
}
Ganz im Gegenteil. Genau dieser Code interessiert. Denn wenn du das
Screen-Array gar nicht brauchst, löst sich dein Problem ganz von alleine
in Luft auf.
Karl Heinz schrieb:> Du kannst dann die Werte nicht mehr einfach so benutzen,> sondern musst über die Funktion pgm_read_xxxx gehen
Mache ich auch schon z.B. für die LCD-Initialisierung:
oder die Zeichensätze.
Habe andere konstante Werte mit enum gemacht:
1
enum{keine_Taste,up,down,rechts,links,Enter};
Bin unzufrieden, warum werden gerade Konstanten verändert,
die sollten doch, wie der Name schon sagt, konstant sein.
Es gab keine Compiler Warnung, selbst als ich das Array noch größer als
512 Byte gemacht habe.
Peter Zz schrieb:> Bin unzufrieden, warum werden gerade Konstanten verändert,
weil es sich, auch wenn der Name etwas anderes suggeriert, erst mal um
Variablen handelt. Variablen von denen der Programmierer die Zusage
macht, dass er sie im Programm nicht verändern wird und die daher als
konstante Werte aufzufassen sind. Aber es sind nach wie vor Variablen
und wenn der Compiler es nicht schafft, die Werte überall an den
verwendenden Stellen einzusetzen, dann werden die auch im Speicher
angelegt, so wie jede andere Variable auch.
> die sollten doch, wie der Name schon sagt, konstant sein.
Dagegen, dass du mit der Brechstange durch den Speicher pflügst, ist
kein Kraut gewachsen. Wenn die Brechstange zuschlägt, ändern sich
Speicherinhalte im SRAM. Punkt. Was auch immer an dieser Stelle im
Speicher liegt, wird verändert. Die Brechstange kümmert sich nicht
darum, was du eigentlich an dieser Stelle im Speicher liegen hättest.
In einem Tiny gibt es keine Hardware, mit der man SRAM Speicher gegen
Schreibzugriffe schützen könnte.
> Es gab keine Compiler Warnung, selbst als ich das Array noch größer als> 512 Byte gemacht habe.
Weil das dem Compiler wurscht ist. Der Compiler weiß nicht, wieviel SRAM
Speicher zur Verfügung steht.
Karl Heinz schrieb:> Genau dieser Code interessiert. Denn wenn du das> Screen-Array gar nicht brauchst, löst sich dein Problem ganz von alleine> in Luft auf.
Das LCD-Display ist EA DOGS102W-6 mit 102x64 Pixel
Ich zeichne die erste Hälfte des Blümchen ins uint32_t Screen[102]
Array.
Danach wird diese erste Hälfte zum LCD-Display geschickt.
Dann zeichne ich die zweite Hälfte des Blümchen ins Array.
Danach wird diese zweite Hälfte zum LCD-Display geschickt.
Dann wird das Blümchen 2 Sekunden lang angezeigt. Das Array wird danach
nicht mehr gebraucht, die Funktion Blume() wird nie mehr aufgerufen.
Karl Heinz schrieb:>> Es gab keine Compiler Warnung, selbst als ich das Array noch größer als>> 512 Byte gemacht habe.>> Weil das dem Compiler wurscht ist. Der Compiler weiß nicht, wieviel SRAM> Speicher zur Verfügung steht.
Kann es sein, das es nur bei Globalen Variablen eine Compilerwarnung
gibt?!
Peter Zz schrieb:> Ich zeichne die erste Hälfte des Blümchen ins uint32_t Screen[102]> Array.
Wie zeichnest du?
Ist das eine Formel. Vielleicht irgendwas in der Art eines Spirographen.
Oder ist das eine vorgefertigte Bitmap? Oder?
...
> Danach wird diese erste Hälfte zum LCD-Display geschickt.> Dann zeichne ich die zweite Hälfte des Blümchen ins Array.> Danach wird diese zweite Hälfte zum LCD-Display geschickt.>> Dann wird das Blümchen 2 Sekunden lang angezeigt. Das Array wird danach> nicht mehr gebraucht, die Funktion Blume() wird nie mehr aufgerufen.
Das mag schon sein. Aber wenn sie aufgerufen wird, verbraucht sie zuviel
Speicher. Das ist nicht wegzudiskutieren. Das Symptom ist, dass andere
Variablen ihre Werte ändern. Die Ursache ist, dass die Funktion Blume()
zu viel Speicher verbraucht.
Was willst du bekämpfen? Das Symptom oder die Ursache?
Ich würde das Problem an der Wurzel packen und die Ursache bekämpfen.
Die Funktion muss weniger Speicher verbrauchen. Egal wie.
Karl Heinz schrieb:> Dagegen, dass du mit der Brechstange durch den Speicher pflügst, ist> kein Kraut gewachsen.
immerhin habe ich eine Abfrage ob ich die Grenzen des Array Screen[102]
verlassen habe:
Peter Zz schrieb:> Karl Heinz schrieb:>> Dagegen, dass du mit der Brechstange durch den Speicher pflügst, ist>> kein Kraut gewachsen.>> immerhin habe ich eine Abfrage ob ich die Grenzen des Array Screen[102]> verlassen habe:
Du hast das Problem nicht verstanden.
Du hast 10 Eimer Wasser nebeneinander stehen, von denen keiner übergehen
darf. Du achtest peinlich genau darauf, dass genau das nicht passiert,
wenn du Wasser nachfüllst.
Und dann kommt die Feuerwehr und spritzt mit dem Schlauch in deinen
Garten.
Es spielt keine Rolle, ob die peinlich genau auf die Füllmenge achtest.
Denn die Feuerwehr tut das nicht und spritze einfach alles nieder.
Dein Array ist zu groß für deinen Speicher. Das kannst du drehen und
wenden wie du willst. Es gibt einzelne Speicherzellen, die sowohl für
den Array benutzt werden als auch für andere Dinge.
Wenn du Variablen global anlegst, dann wachsen die im Speicher von der
linken Seite her
1
int Variable1, Variable2;
2
3
+---+---+---+---+---+---+---+---+---+---+
4
| | | | | | | | | | |
5
+---+---+---+---+---+---+---+---+---+---+
6
^ ^
7
| |
8
| Variable 1
9
Variable 2
wenn du eine Funktion aufrufst, die lokale Variablen hat, dann müssen
die ja dynamisch erzeugt werden, weil es sie ja nur während der
Ausführung der Funktion gibt. Die werden sukzessive vom rechten Rand des
Speichers aus allokiert
1
void foo()
2
{
3
int lokalVariable1;
4
int lokalVariable2;
5
}
6
7
lokalVariable2
8
| lokalVariable1
9
| |
10
v v
11
+---+---+---+---+---+---+---+---+---+---+
12
| | | | | | | | | | |
13
+---+---+---+---+---+---+---+---+---+---+
14
^ ^
15
| |
16
| Variable 1
17
Variable 2
wird die Funktion verlassen, dann wird am rechten Rand des SPeichers
wieder freigegeben.
Aber: Wenn deine lokalen Variablen zu groß sind, dann wachsen die immer
weiter nach links. Bis es dann eben irgendwann mal kracht, weil die
allokierten lokalen Variablen denselben Speicher erreicht haben, der
auch für die globalen Variablen benutzt wird.
1
void foo()
2
{
3
int lokalVariable1;
4
int lokalVariable2;
5
int lV3;
6
int lV4;
7
int lV5;
8
int lV6;
9
int lV7;
10
int lV8;
11
int lV9;
12
}
13
14
lokalVariable2
15
lV9 lV8 lV7 lV6 lV5 lV4 lV3 | lokalVariable1
16
| | | | | | | | |
17
v v v v v v v v v
18
+---+---+---+---+---+---+---+---+---+---+
19
| | | | | | | | | | |
20
+---+---+---+---+---+---+---+---+---+---+
21
^ ^
22
| |
23
| Variable 1
24
Variable 2
schreibst du etwas an lV9, dann veränderst du auch 'Variable1', weil die
in denselben Speicherzellen hausen.
Und genau das ist dein Problem. Bei dir kracht es, weil das Array zu
groß ist.
Also Formel.
Ich würde an deiner Stelle folgendes machen.
Ich würde mir den Code zum malen auf den PC holen. Würde mir dort ein
Programm machen, welches das Blümchen auf dem PC berechnet und in Form
eines bereits fertigen Bildes als C-Code abspeichert.
1
uint32_tImagePart1[]={0x00000000,0x00001000,....
2
3
uint32_tImagePart2[]={0x00000000,0x00001000,....
und das dann ausgeben. Der Clou an der Sache: Diese Arrays kann ich dann
ins PROGMEM schieben und habe sie so aus dem SRAM draussen.
Und so ganz nebenbei ist dann auch noch eine Funktion auf dem Tiny
abgefallen, mit der man Bitmaps aus dem Flash Speicher ausgeben kann :-)
Das soll ja nicht so uninteressant sein, wenn man mal anstatt eines
Blümchens ein etwas anderes aufwendigeres Logo ausgeben will, dass sich
mit Formeln nicht mehr beschreiben lässt.
bal schrieb:> Und lass die PROGMEM Krücke und benutze lieber _flash,
warum ist PROGMEM eine Krücke und _flash nicht?
> einen halbwegs aktuellen gcc vorrausgesetzt.
Ich habe Atmel Studio 6.1 installiert und fertig!
Woher weiß ich ob mein gcc halbwegs aktuell ist?
Vielleich noch zur Ergänzung der ausführlichen Erklärung von Karl Heinz
Beitrag "Re: const verändert"Peter Zz schrieb:> Bin unzufrieden, warum werden gerade Konstanten verändert,> die sollten doch, wie der Name schon sagt, konstant sein.> Es gab keine Compiler Warnung, selbst als ich das Array noch größer als> 512 Byte gemacht habe.
Deine 'Konstante' ist keine Konstante, sondern eine mit dem
Schlüsselwort 'const' versehene Variable. Wenn du jetzt in deinem
Programm an dieser Variablen rumfummelst, dann warnt der Compiler!
'const' ist also nur deine eigene Absichtserklärung, dass du die
Variable nie ändern willst.
Wenn du das Array jetzt größer machst, dann werden die Daten dort 'noch
viel eher' überschrieben. (Nur dass eben zufällig nichts Wichtiges
drinsteht.) Im Programmablauf benötigen Funktionsaufrufe und Variablen
RAM, der von den hohen Adressen absteigend bei Bedarf einkassiert und am
Ende der Funktion wieder freigegeben wird.
Peter Zz schrieb:> bal schrieb:>> Und lass die PROGMEM Krücke und benutze lieber _flash,>> warum ist PROGMEM eine Krücke und _flash nicht?
__flash folgt einem Standardvorschlag für named address spaces,
während PROGMEM auf _attribute_ hinausläuft, was eine reine
GCC-Erweiterung ist.
Bei __flash handelt es sich um einen type qualifier, der vom
Compiler überprüft werden kann. Die Benutzung von _attribute_
hingegen ist eine Art Schweizer Taschenmesser, bei dem man dem GCC
zwar einiges mit auf den Weg geben kann, aber kontrollieren kann er
das nicht.
Bei __flash kann der Compiler die normale Dereferenzierung (also
das Einsetzen der LPM-Anweisungen) selbst vornehmen, bei PROGMEM
muss man selbst dran denken, das pmg_read*-Geraffel dafür anzuwerfen.
>> einen halbwegs aktuellen gcc vorrausgesetzt.>> Ich habe Atmel Studio 6.1 installiert und fertig!
Auch da kann man letztlich verschiedene Toolchains drunter setzen,
wenn man will.
> Woher weiß ich ob mein gcc halbwegs aktuell ist?
Du schaust dir die Ausgabe von "avr-gcc -v" mal an. Es sollte
mindestens ein GCC 4.7 sein (ist aber meiner Meinung nach der Fall).
Ohne jetzt Ahnung von gcc o. AVR zu haben.
Tut das wirklich Not, das ganze in einem Array zwischen zu speichern?
Kann man die einzelnen Pixel nicht gleich ins LCD schreiben!
Karl Heinz schrieb:> Und so ganz nebenbei ist dann auch noch eine Funktion auf dem Tiny> abgefallen, mit der man Bitmaps aus dem Flash Speicher ausgeben kann :-)> Das soll ja nicht so uninteressant sein, wenn man mal anstatt eines> Blümchens ein etwas anderes aufwendigeres Logo ausgeben will, dass sich> mit Formeln nicht mehr beschreiben lässt.
Teo Derix schrieb:> Ohne jetzt Ahnung von gcc o. AVR zu haben.>> Tut das wirklich Not, das ganze in einem Array zwischen zu speichern?> Kann man die einzelnen Pixel nicht gleich ins LCD schreiben!
wird wohl ein Laufzeitproblem sein.
Einzelne Pixel setzen ist meistens recht aufwändig. Da ist es schon
einfacher erst mal in Streifen die Pixel vorab zu sammeln und dann die
Streifen (also zb jeweils 8 übereinander liegende Pixel) in einem Rutsch
auszugeben. Das macht dann schon einen Unterschied, ob du mit einer
Datenübertragung zum LCD lediglich 1 Pixel oder gleich 8 in einem
Aufwasch korrekt setzt.
Wobei man natürlich auch argumentieren könnte, dass beim Einschalten
eines Gerätes anstatt eines Standbildes eine 'Animation' abläuft, die am
Ende in besagter Blume resultiert und bei der die errechneten Pixel
einfach direkt ausgegeben werden, so wie sie aus der Rechnung anfallen.
Wer sie sehen will, der wartet. Wer sie nicht sehen will, drückt einfach
einen Knopf und die 'Animation' wird abgebrochen.
Persönlich bin ich sowieso kein Freund derartiger
Zwangs-Einschalt-Beglückungen. Die sehen die ersten paar mal nett aus,
aber man sieht sich dann doch recht schnell daran satt. Mit ist lieber
die Gerätefunktion ist durchdacht. Da kann ich dann gerne auch auf das
eine oder andere optische Gimmick verzichten.
Peter Zz schrieb:> Wo soll ich "avr-gcc -v" eingeben?
Auf einer Kommandozeile natürlich. Entgegen anderslautenden Gerüchten
gibt es sowas auch bei Windows. Allerdings wird das Atmel Studio
vermutlich den PATH nicht passend gesetzt haben; du musst dir also
vorher noch die Mühe machen, das Verzeichnis ausfindig zu machen, in
dem sich avr-gcc.exe befindet.
Ich würds einfach ausprobieren.
kleines Testprogramm, erstellt mit Teilen aus dem AVR-GCC-Tutorial und
wenn der Compiler __flash nicht anmeckert, dann wird er es wohl bereits
kennen.
Jörg Wunsch schrieb:> Auf einer Kommandozeile natürlich.
Bin eben selber drauf gekommen:
Obere Leiste "Tools" dann Command Prompt wählen.
Pfade waren anscheinend richtig mein gcc ist 4.7.2
Karl Heinz schrieb:> kleines Testprogramm, erstellt mit Teilen aus dem AVR-GCC-Tutorial und> wenn der Compiler __flash nicht anmeckert, dann wird er es wohl bereits> kennen.
Peter Zz schrieb:> klappt, aber diese ganzen Werte müssen anscheinend zuerst in der> Funktion drin stehen?!
Was meinst du mit 'in der Funktion drinn stehen'?
Du kannst die Arrays auch global machen, oder im C-File als static
Moduleglobale Arrays anlegen, wenn dir das besser gefällt.
Du könntest die tatsächlichen Werte auch in eine eigene Datei auslagern
und per #include einbinden, so dasss du beim Editieren des Source Codes
nicht jedesmal den Zahlenwust der Array Initialisierung siehst.
Aber eines ist klar: Irgendwo müssen die Werte im Code vorkommen, damit
sie auch mitcompiliert werden.
Karl Heinz schrieb:> Du kannst die Arrays auch global machen, oder im C-File als static> Moduleglobale Arrays anlegen, wenn dir das besser gefällt.
Wie? Habt Ihr mal ein Beispiel? Das würde mich jetzt auch mal
interessieren!
Schwimmbadpinkler schrieb:> Karl Heinz schrieb:>> Du kannst die Arrays auch global machen, oder im C-File als static>> Moduleglobale Arrays anlegen, wenn dir das besser gefällt.>> Wie? Habt Ihr mal ein Beispiel? Das würde mich jetzt auch mal> interessieren!
Bitte!
Das steht aber nun wirklich selbst in den allerschlimmsten und
unvollstaendigsten C Buechern drinn!Aber echt, ey.
Stichwort: was macht das Schluesselwort 'static'?