Hi Leute, ich bin da auf etwas merkwürdiges gestoßen. Und zwar habe ich mir eine kleine MovingAverage-Klasse geschrieben um eben recht einfach einen gleitenden Durchschnitt berechnen zu können. Aber irgendwie ist der Durchschnittswert, den mir meine Klasse zurück gibt, immer um 128 größer. Ich kompiliere das ganze mit avr-gcc V4.7.0 auf meiner Linux-Maschine und flashe dann mit avrdude einen Atmega8. Nutze ich die selbe Klasse in einem normalen gcc-Projekt, das auf meinem Linux läuft und simuliere die Eingangsdaten, dann stimmt alles. Es kommt also der richtige Durchschnittswert raus. Leider bin ich noch nicht so bewandert mit der ganzen avr-gcc-Kompiliererei, dass ich mir da den ASM-Output geben lassen kann und den auch noch verstehe, um zu sehen, was möglicherweise falsch laufen könnte. Jedenfalls ist es sehr ärgerlich. Im Anhang befindet sich der C++-Code, der genau das simuliert, was auf dem Atmega8 passiert. Dieser bekommt Daten aus dem ADC von einem Joystick, der in Mittelstellung ist, aber ein bisschen rauscht. Die direkten Daten aus dem ADC sind korrekt und liegen immer so im Bereich 124-130. Nach dem Nutzen der MovingAverage-Klasse sollten sie immer noch in diesem Bereich liegen. Allerdings tun sie das nicht, sondern schwanken dann um 0 herum, also konkret um 253-255 und 0-3, wegen des Überlaufs. Der avr-gcc scheint da also irgendwas falsch zu machen. Der gcc macht es richtig. Grüße, Nicolas
hallo, zwei dinge.. template<typename T> class MovingAverage { ... } templates und überhaupt klassen für avr? heikel.. da dann den asm-code durchzusteigen verlangt übung. dann: printf("%d: add(%d) -> %d\n", i, value, result); %d ist für signed int, %u wäre unsigned decimal integer ist das beides so gewollt?
1 | #if ((T) - 1 < 0)
|
2 | int32_t sum; |
3 | #else
|
4 | uint32_t sum; |
5 | #endif
|
Template-Parameter mit dem Präprozessor zu testen ist nicht drin. Nicht einmal wenn korrekt formuliert.
n.n. schrieb: > templates und überhaupt klassen für avr? heikel.. Völlig ok. Solange das nicht 100x expandiert wird kommt hier auch nix anderes bei raus als in C. In dem Fall hier hätte allerdings auch die Länge noch als Template-Parameter gepasst, damit den malloc() erspart und evtl. die eine oder andere Division erleichtert.
Besser wär freilich, wenn keine nicht initialisierten values subtrahiert würden. Eine Funktion clear() zu definieren allein reicht nicht.
Der Algorithmus selbst ist auch nicht so ganz der Bringer. Du dividierst bereits den ersten Wert durch maxvalues, so dann sich average langsam von unten kommend an den Mittelwert herantastet.
Apropos "Bug im Compiler": Wenn das Ergebnis in Linux stimmt, auf dem AVR aber nicht, dann hat trotzdem selten der Compiler Schuld. Es gibt beispielsweise gewisse Unterschiede bei der Initialisierung des Speichers. In Linux kriegst du beim ersten malloc() eines Speichers Nullen geliefert, weil der Systemaufruf dazu den schon selbst initialisiert. Der AVR tut dir den Gefallen nicht, der liefert aus was zufällig da drinsteht.
Fassen wir das alles mal zusammen: Der TO hat printf nicht richtig verwendet und ist darum mit dem Vorzeichenbit (bit 7 = 128) durcheinandergekommen.
Das printf ist völlig ok. Alle 3 Werte kommen als "int" an.
Danke für die Antworten. Ich nehme dann mal Stellung: n.n. schrieb: > printf("%d: add(%d) -> %d\n", i, value, result); > %d ist für signed int, > %u wäre unsigned decimal integer > > ist das beides so gewollt? Im Atmega8-Code habe ich ja eine ganz andere main-Methode. Das hier mag nicht vollkommen korrekt sein, habe ich aber auch nur mal schnell zu Testzwecken mit dem gcc dahin gehackt. A. K. schrieb: > Template-Parameter mit dem Präprozessor zu testen ist nicht drin. Nicht > einmal wenn korrekt formuliert. Guter. Das habe ich gerade auch nochmal nachgelesen. Aber auch wenn das in dem Fall jetzt falsch ist, hatte ich vorher fest uint32_t benutzt ohne Direktive, und da trat das besagte Problem auch schon auf. A. K. schrieb: > In dem Fall hier hätte allerdings auch die Länge noch als > Template-Parameter gepasst, damit den malloc() erspart und evtl. die > eine oder andere Division erleichtert. Die Klasse ist noch nicht ganz fertig. Später soll auch 'maxValues' variabel werden, deshalb scheidet es als Template-Parameter hier aus. Der gesamte Code ist ja noch in der Entwicklungsphase. Möglicherweise wird es auch später doch irgendwann zum Template-Parameter. Mal abwarten... A. K. schrieb: > Besser wär freilich, wenn keine nicht initialisierten values subtrahiert > würden. Eine Funktion clear() zu definieren allein reicht nicht. UND A. K. schrieb: > In Linux kriegst du beim ersten malloc() eines Speichers > Nullen geliefert, weil der Systemaufruf dazu den schon selbst > initialisiert. Der AVR tut dir den Gefallen nicht, der liefert aus was > zufällig da drinsteht. schäm Daran hätte ich selbst denken können. Das muss ich mal testen. Womöglich ist das schon der Fehler. Ändere ich nämlich den Konstruktor in das hier ab:
1 | MovingAverage(size_t maxValues, T average = 0) { |
2 | values = (T*) malloc(maxValues * sizeof(T)); |
3 | for (size_t i = 0; i < maxValues; i++) { |
4 | values[i] = rand() % 256; |
5 | }
|
6 | |
7 | this->maxValues = maxValues; |
8 | index = 0; |
9 | sum = 0; |
10 | this->average = average; |
11 | }
|
Dann sieht es mit dem gcc ganz ähnlich aus. Aber bis ich das wieder mit dem Atmega8 testen kann, muss ich noch bis Mittwoch warten. A. K. schrieb: > Der Algorithmus selbst ist auch nicht so ganz der Bringer. Du dividierst > bereits den ersten Wert durch maxvalues, so dann sich average langsam > von unten kommend an den Mittelwert herantastet. Das ist mir bewusst. In dem ursprünglichen Atmega8-Code läuft das ganze aber sowieso unendlich oft in einer while-Schleife, womit sich bereits nach 1-2 ms der Average-Wert angepasst hat, weil dann alle Felder einmal beschrieben wurden. Deshalb stört mich das nicht sonderlich, zumindest bis jetzt. Wie gesagt ist es noch in der Entwicklung. Na gut, dann pack ich mal einen clear()-Aufruf noch in den Konstruktor, dann sollte das Problem gelöst sein... Vielen Dank an alle!
Kann man hier irgendwie als Threadstarter eine Markierung anbringen, die besagt, dass das Problem gelöst ist? Den Titel kann ich ja auch nicht mehr editieren.
A. K. schrieb: > In Linux kriegst du beim ersten malloc() eines Speichers > Nullen geliefert, weil der Systemaufruf dazu den schon selbst > initialisiert. Dass er das tut, liegt vor allem an der Mehrnutzerumgebung: es muss sichergestellt sein, dass kein Prozess Daten fremder Nutzer zu sehen bekommt. Das geht nun mal am besten, indem man allen anonymen Speicher, den ein Prozess bekommt (Stack, Heap) ausnullt. Man kann natürlich genauso gut was anderes reinschreiben; wenn man beispielsweise bei FreeBSD in den MALLOC_OPTIONS den Buchstaben "J" einträgt, dann wird aller Speicher auf 0xa5 initialisiert. Da kann man sich dann schön ansehen, welche Programme alle abstürzen, weil sie vergessen haben, sich um die Initialisierung ihres Speichers zu kümmern. ;-) Bastel K. schrieb: > Kann man hier irgendwie als Threadstarter eine Markierung anbringen, die > besagt, dass das Problem gelöst ist? Leider nicht (hätte ich auch gern als Feature), aber ich kann das für dich tun.
Jörg Wunsch schrieb: > Dass er das tut, liegt vor allem an der Mehrnutzerumgebung: es muss > sichergestellt sein, dass kein Prozess Daten fremder Nutzer zu sehen > bekommt. Das ist in Einnutzerumgebungen genauso kritisch. > Da kann man sich dann schön > ansehen, welche Programme alle abstürzen, weil sie vergessen haben, > sich um die Initialisierung ihres Speichers zu kümmern. ;-) Nette Idee. ;-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.