Forum: Mikrocontroller und Digitale Elektronik Sehr seltsames Verhalten von Arrays


von Manuel V. (manuelito)


Lesenswert?

Hallo,

Ich weiß nicht was gerade das Problem ist, aber ich kann keine Arrays 
mehr benutzen...

Folgendes habe ich geschrieben:
1
uint8_t data []= {128,128,128,128,139,128,128,196,128,128,244,128,128,248,128,128,210,128};
2
for (uint16_t i = 0; i < 18; i++)
3
  write8(data[i]);
Das gibt allerdings sehr seltsame Ergebnisse aus (genau kann ich es 
nicht sagen, da ich kein LCD o.ä. habe, um mir Daten auszugeben.

Auf jeden Fall macht dieser Code auf keinen Fall das gleiche wie:
1
write8(128);
2
write8(128);
3
write8(128);
4
write8(128);
5
write8(139);
6
...

Die Funktion, die ich aufrufe, ist folgende: void write8(uint8_t d)

Ich hoffe ihr seht, wo mein Denkfehler ist...

Das ganze läuft auf einem ATmega16.

Viele Grüße,
Manuel

PS: Kann ich auf diese Weise eigentlich auch Arrays aus 7000 Elementen 
ansprechen? Da der ATmega16 nur 1K-Ram hat, kann das Array ja auf jeden 
Fall nicht vollständig in den RAM geladen werden...

von ich (Gast)


Lesenswert?

Gebt mir ein: D
Gebt mir ein: E
Gebt mir ein: B
Gebt mir ein: U
Gebt mir ein: G
Gebt mir noch ein: G
Gebt mir wieder ein: E
Gebt mir ein: R

Vielleicht sollte der Gedankengang, alles ist korrekt bis auf diese paar 
Zeilen, noch einmal überdacht werden, da die scheinbar nicht vorhandene 
Ausgabe ja fundiert auf einen Fehler in diesen Zeilen hinweist.

von Karl H. (kbuchegg)


Lesenswert?

Manuel Vossel schrieb:

> Folgendes habe ich geschrieben:
>
1
uint8_t data []=
2
> {128,128,128,128,139,128,128,196,128,128,244,128,128,248,128,128,210,128};
3
> for (uint16_t i = 0; i < 18; i++)
4
>   write8(data[i]);
> Das gibt allerdings sehr seltsame Ergebnisse aus (genau kann ich es
> nicht sagen, da ich kein LCD o.ä. habe, um mir Daten auszugeben.
>
> Auf jeden Fall macht dieser Code auf keinen Fall das gleiche wie:
>
1
write8(128);
2
> write8(128);
3
> write8(128);
4
> write8(128);
5
> write8(139);
6
> ...
7
>

Das Timing ist ein klein wenig anders, aber ansonsten sind sie 
gleichwertig.


> PS: Kann ich auf diese Weise eigentlich auch Arrays aus 7000 Elementen
> ansprechen? Da der ATmega16 nur 1K-Ram hat, kann das Array ja auf jeden
> Fall nicht vollständig in den RAM geladen werden...

Und wie willst du in einen Speicher mit einer definierten Maximalgröße 
Daten reinpferchen, die größer sind. Harry Potter Modus?

von Kast (Gast)


Lesenswert?

Wenn du C++/C99 verwendest sollte die Funktion write8 korrekt aufgerufen 
werden. Was dort geschieht kann hier keiner sehen.

Da du 16Kb an Flash hast, könntest du das Array immer aus den Flash 
lesen (vorausgesetzt du willst es nur lesen) ohne es in den RAM zu 
laden.

von Bastler (Gast)


Lesenswert?

deine Indexvariable [i] ist 16bit deklariert.

schätzungsweise überspringt somit dein Array jede zweite Stelle.

von Noname (Gast)


Lesenswert?

>deine Indexvariable [i] ist 16bit deklariert.
>schätzungsweise überspringt somit dein Array jede zweite Stelle.

Ja. Und an ungeraden Freitagen sogar Schäfchen. :-)

von Helmut L. (helmi1)


Lesenswert?

Bastler schrieb:
> schätzungsweise überspringt somit dein Array jede zweite Stelle.

Noe , die laeuft schon von 0 .. 17.

von Manuel V. (manuelito)


Lesenswert?

Habe das Problem jetzt mehr durch Zufall gefunden.
Ich hatte besagtes 7000k-Array vorher schon definiert, und das hat dem 
wohl im RAM alles durcheinander gebracht...
Debuggen hatte ich schon versucht, allerdings hat der die for-Schleife 
immer übersprungen (auch bei Step Into, nicht nur bei Step Over) - ich 
benutze AVR Studio.

Kast schrieb:
> Da du 16Kb an Flash hast, könntest du das Array immer aus den Flash
> lesen (vorausgesetzt du willst es nur lesen) ohne es in den RAM zu
> laden.

Ja, ich will das nur lesen.
Allerdings müsste ich das erstellen des Arrays dann wahrscheinlich in 
der .hex-Datei machen?! Oder gibts da eine andere Möglichkeit, dass das 
schon im Flash liegt, wenn der µC startet?
Ich müsste im 40ms-Takt daraus jeweils 96 Byte lesen und per write8 
schreiben.

Aber falls das jetzt zu kompliziert ist, mach ich mir die Mühe nicht. Im 
Endeffekt liegen die Daten sowieso auf einer SD-Karte, weil das ung. 8MB 
werden, von denen ich pro 40ms 576byte lesen muss und rausschicke (das 
ganze wird eine Animation auf einem digitalen RGB-Strip).

von Karl H. (kbuchegg)


Lesenswert?

Manuel Vossel schrieb:
> Habe das Problem jetzt mehr durch Zufall gefunden.
> Ich hatte besagtes 7000k-Array vorher schon definiert, und das hat dem
> wohl im RAM alles durcheinander gebracht...

Genau deswegen gibt doch die Toolchain im AVR-Studio eine 
Speicherverbrauchsstatistik aus (zumindest in AVR-Studio 4, wie das bei 
5 ist weiß ich nicht). Ist der SRAM Verbauch bei den kleineren 
Prozessoren bei 80 bis 85%, wirds meistens schönb langsam kritisch.


> Allerdings müsste ich das erstellen des Arrays dann wahrscheinlich in
> der .hex-Datei machen?!

Nö.
Du musst das nur richtig definieren und anders aufs Array zugreifen.

> Oder gibts da eine andere Möglichkeit, dass das
> schon im Flash liegt, wenn der µC startet?

Das liegt sowieso im Flash. Wo soll es denn sonst herkommen?


> Ich müsste im 40ms-Takt daraus jeweils 96 Byte lesen und per write8
> schreiben.
>
> Aber falls das jetzt zu kompliziert ist, mach ich mir die Mühe nicht. Im
> Endeffekt liegen die Daten sowieso auf einer SD-Karte,

und du machst dir Sorgen, ob das Lesen aus dem Flash schnell genug ist?

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Genau deswegen gibt doch die Toolchain im AVR-Studio eine
> Speicherverbrauchsstatistik aus (zumindest in AVR-Studio 4, wie das bei
> 5 ist weiß ich nicht).

Naja, die Idee 7000 Bytes in in 1k SRam reinklopfen zu wollen, ist schon 
absurd, wobei das wieder beweist, dass es Nerds gelingt selbst den 
unwahrscheinlichsten Fehler zu machen.

Jedoch:

ATM32
AVR-Studio 4.18
WinAVR-20090313
Optimierung: -O0
1
int main (void)
2
{
3
uint8_t dta[10000];
4
5
dta[0] = 64;
6
dta[9999] = 128;
7
}
compiliert problemlos, Speicherverbrauch:
1
AVR Memory Usage
2
----------------
3
Device: atmega32
4
5
Program:     172 bytes (0.5% Full)
6
(.text + .data + .bootloader)
7
8
Data:          0 bytes (0.0% Full)
9
(.data + .bss + .noinit)
Da sollte wenigstens eine Warnung kommen.

Das Disassemblat zeigt, dass ein rechter Unsinn daraus erzeugt wird:
1
9:        dta[0] = 64;
2
+00000041:   E480        LDI       R24,0x40
3
+00000042:   8389        STD       Y+1,R24 
4
10:       dta[9999] = 128;
5
+00000043:   01FE        MOVW      R30,R28 
6
+00000044:   5FE0        SUBI      R30,0xF0
7
+00000045:   4DF8        SBCI      R31,0xD8
8
+00000046:   E880        LDI       R24,0x80
9
+00000047:   8380        STD       Z+0,R24
Es wird versucht die Zellen im SRam per Y auf 0xE14C (57676) und per Z 
auf 0x085B (2139) zu beschreiben.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

MWS schrieb:
> Da sollte wenigstens eine Warnung kommen.

Schwierig, denn dann müsste der Compiler zur Übersetzungszeit in der 
Lage sein, den Gebrauch des Stacks zu überprüfen. Bei einem so einfachen 
Beispiel wie Deinem ist das noch recht einfach (Größenprüfung 
automatischer Variablen), aber was ist, wenn ein µC 1 k Speicher hat, 
aber acht Funktionen verwendet werden, die jeweils eine automatische 
Variable von 256 Byte Größe verwenden? Ist das ein Fehler oder ist das 
in Ordnung? Die Antwort lautet: Je nachdem.

von Karl H. (kbuchegg)


Lesenswert?

Rufus Τ. Firefly schrieb:
> MWS schrieb:
>> Da sollte wenigstens eine Warnung kommen.
>
> Schwierig,

In dem krassen Fall ginge das natürlich. Auch in einer Funktion kann ich 
nicht mehr Daten allokieren, als der µC überhaupt Speicher hat.

Ob man das in den gcc integrieren könnte, weiß ich nicht.

Aber ich gebe was anderes zu bedenken:
Warnungen tenideren dazu als "Sicherheitsnetz" misbraucht zu werden: Der 
Compiler hat nicht gewarnt, also ist alles in Ordnung.
Solange der Compiler nicht tatsächlich alle Fälle eindeutig erkennen 
kann, bin ich normalerweise ein Gegner solcher Warnungen. Und damit bin 
ich wieder bei dir, da du unmissverständlich zeigst, dass das im 
Allgemeinen für jedes Programm und jede Allokierung nicht feststellbar 
ist, feststellbar sein kann.

von Manuel V. (manuelito)


Lesenswert?

MWS schrieb:
> Naja, die Idee 7000 Bytes in in 1k SRam reinklopfen zu wollen, ist schon
> absurd, wobei das wieder beweist, dass es Nerds gelingt selbst den
> unwahrscheinlichsten Fehler zu machen.

Nö, das hatte ich nicht vor, ich wollte nur Arrays aus 7k ansprechen, wo 
die liegen, ist mir schnurz.
Und das funktioniert mit dem Link von kbuchegg auch problemlos, danke!


Karl Heinz Buchegger schrieb:
> Genau deswegen gibt doch die Toolchain im AVR-Studio eine
> Speicherverbrauchsstatistik aus (zumindest in AVR-Studio 4, wie das bei
> 5 ist weiß ich nicht). Ist der SRAM Verbauch bei den kleineren
> Prozessoren bei 80 bis 85%, wirds meistens schönb langsam kritisch.

Das habe ich jetzt gerade auch entdeckt, danke!

von MWS (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Schwierig, denn dann müsste der Compiler zur Übersetzungszeit in der
> Lage sein, den Gebrauch des Stacks zu überprüfen.

Da geb' ich Dir in begrenztem Umfang recht, denn es ist zur 
Übersetzungszeit problemlos möglich zu prüfen ob 10000 x 1Byte im SRam 
Platz haben. Zumindest grobe Schnitzer wären zu entdecken.

Eine vor main() deklarierte Variable wird richtig mit einem 
Speicherverbrauch von 488.3% angegeben, allerdings keine Warnung. 
Innerhalb von main() ist, sobald der Stack verwendet wird, die 
Prozentangabe falsch, wird nur durch ein vorgesetztes static korrekt 
angezeigt, welches die Variable in den Standardadressbereich mappt und 
nicht mehr in den Stack. Auch hier keine Warnung, was aber nun gar kein 
Problem wäre. Da geht mir die Logik ein wenig ab, was aber der 
Standardisierung von C geschuldet sein mag.

Wohin soll den main() auf einem µC zurückkehren ? Geht nicht, also 
sollte eine innerhalb main() stehende Allokation prinzipiell keinen 
Unterschied zur globalen Allokation machen, ist aber trotzdem so.

Karl Heinz Buchegger schrieb:
> bin ich normalerweise ein Gegner solcher Warnungen.

Das ist jetzt keine schlüssige Argumentation, da jedem mit 
Programmproblemen empfohlen wird, sich die Warnungen des Compilers 
anzusehen. Wer das tut und eine Warnung findet "Bereichsüberschreitung 
bei Variablendeklaration", hat die Chance zu korrigieren und sei's nur 
ein Vertipper.

> Und damit bin
> ich wieder bei dir, da du unmissverständlich zeigst, dass das im
> Allgemeinen für jedes Programm und jede Allokierung nicht feststellbar
> ist, feststellbar sein kann.

Selbstverständlich ist das nicht allgemeingültig möglich, eine 
Bereichsüberschreitung geht immer, nur bei solch einem offensichtlichen 
Fehler wäre es sinnvoll wenn der Compiler meckert. Wie Du siehst, hätte 
es in diesem Fall geholfen, es würde sogar dann helfen, wenn jemand 
absichtlich ein Array im Flash anlegen möchte, das Schlüsselwort aber 
vergisst. Es wäre m.E. sogar gerechtfertigt statt einer Warnung einen 
Fehler zu melden.

Manuel Vossel schrieb:
> Nö, das hatte ich nicht vor, ich wollte nur Arrays aus 7k ansprechen, wo
> die liegen, ist mir schnurz.

Ehm, ja, aber das Programmieren von µC's setzt voraus, die Hardware des 
µC zu kennen und spätestens dann sollte klar sein, daß keine 7k in 1k 
SRam reingehen. Und bezüglich "ansprechen": lesen aus dem Flash ist ok, 
aber schreiben vergleichbar mit dem SRam kannst Du da nicht.

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:

> Karl Heinz Buchegger schrieb:
>> bin ich normalerweise ein Gegner solcher Warnungen.
>
> Das ist jetzt keine schlüssige Argumentation, da jedem mit
> Programmproblemen empfohlen wird, sich die Warnungen des Compilers
> anzusehen.

Natürlich tun wir das.
Wir sehen aber auch immer wieder die Umkehrung: Der Compiler hat nicht 
gewarnt, also ist alles in Ordnung.
Darauf zielen meine Bedenken ab: Auf das Misverständnis, dass Warnungen 
ein 100% Sicherheitsnetz wären.

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wir sehen aber auch immer wieder die Umkehrung: Der Compiler hat nicht
> gewarnt, also ist alles in Ordnung.

Also beim Lesen der "Hilfe, mein Programm geht nicht"-Posts gewann ich 
stets den Eindruck, dass warnungsfreie Compilierung in Verbindung mit 
nicht funktionierendem Programm eher zur Ausnahme zählt.

Meist läuft's so ab:
TO: Mein Programm geht nicht.
A: Wo ist der Code ?
TO: Hab's jetzt mit angehängt.
A: Was geht nicht ?
TO: Es macht nicht was es soll.
A: Hast Du Warnungen vom Compiler ?
TO: Ja, aber es waren zu viele.
Oder alternativ:
TO: Ja, aber dabei hab' ich mir nichts gedacht.

> Darauf zielen meine Bedenken ab: Auf das Misverständnis, dass Warnungen
> ein 100% Sicherheitsnetz wären.

Würd's eher als Komfortfunktion verstehen, wenn ein Compiler bei 
offensichtlichen Fehlern sich bemerkbar macht.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sicher, wenn in einem Rutsch mehr automatische Variablen deklariert 
werden, als überhaupt auf dem Stack verfügbar sein kann, dann könnte der 
Compiler meckern, sobald aber die Speichernutzung etwas verteilter (und 
damit realistischer) ist, geht's eben nicht.

Und da genügt das einfache von mir erwähnte Beispiel

> was ist, wenn ein µC 1 k Speicher hat,
> aber acht Funktionen verwendet werden,
> die jeweils eine automatische Variable
> von 256 Byte Größe verwenden?
> Ist das ein Fehler oder ist das in Ordnung?

Hinzu kommt, daß der Compiler nur einzelne Module bei der Übersetzung zu 
Gesicht bekommt, sobald ein Programm aus mehreren Modulen besteht, ist 
eine Stackverbrauchsanalyse nicht mehr möglich.

Die kann erst in der Linkphase erfolgen, der Linker aber weiß nichts von 
automatischen Variablen.

Und --um auf mein Beispiel zurückzukommen--, selbst wenn die Summe aller 
in allen Funktionen und Modulen deklarierten automatischen Variablen den 
Stack um ein mehrfaches sprengt, muss das kein Fehler sein. Obige 
Funktionen können ja nacheinander aufgerufen werden, was vollkommen 
legitim ist. Erst, wenn diese Funktionen sich gegenseitig aufrufen, 
knallt es.
Da genügt sogar eine Funktion, die sich rekursiv aufruft, was, sofern 
es nicht zu oft geschieht, auch vollkommen legitim ist. Geschieht es 
aber zu oft, knallt es -- selbst wenn die Funktion gar keine 
automatischen Variablen verwendet.

Hier helfen nur sehr aufwendige Codeanalyseprogramme, oder Erfahrung und 
der Gebrauch eines Debuggers.

von MWS (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Und da genügt das einfache von mir erwähnte Beispiel
>
>> was ist, wenn ein µC 1 k Speicher hat,
>> aber acht Funktionen verwendet werden,
>> die jeweils eine automatische Variable
>> von 256 Byte Größe verwenden?
>> Ist das ein Fehler oder ist das in Ordnung?

Könnte sein, dass wir da unterschiedliche Vorstellungen um den 
Funktionsumfang solch einer Prüfung haben.

Meine wäre bei offensichtlichen Fehlern zu warnen und nicht zu versuchen 
alles zu erschlagen. Denn das geht, wie mir auch klar ist, nur höchst 
schwierig, oder ist quasi unmöglich.

Sobald also der reservierte Bereich innerhalb einer Funktion die Menge 
des tatsächlich verfügbaren SRams übersteigt, wäre ein Fehler 
auszugeben.

Auch da kann man sich natürlich wieder um den Nutzwert streiten, aber 
wie hier am Beispiel des TOs gesehen hätt's etwas geholfen.

von DeBug Er (Gast)


Lesenswert?

Bastler schrieb:
> deine Indexvariable [i] ist 16bit deklariert.
>
> schätzungsweise überspringt somit dein Array jede zweite Stelle.

Also mit Verlaub, das ist aber vollkommener Blödsinn. Der Index wird zur 
Basisadresse addiert. Dabei ist es vollkommen belanglos, ob die Zahl 1 
nun 4, 8 oder 16 bit Darstellung hat. Die Adresse als Ergebnis ist 
dieselbe.

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.