Ja, denke auch dass es am PROGMEM liegt, sonst wäre ich bereits früher drüber gestolpert.
Das erwartete Rechenergebnis wäre +29 statt -131, also 190-161=29.
Ja, denke auch dass es am PROGMEM liegt, sonst wäre ich bereits früher
drüber gestolpert.
Das erwartete Rechenergebnis wäre +29 statt -131, also 190-161=29.
Wenn Du (Byte-)Daten mittels PROGMEM ablegst, musst Du auf diese mittels
pgm_read_byte() zugreifen.
Warum verwendest Du nicht __flash? Dann geht's auch ohne pgm_read_byte().
z = (int16_t)(anzArray[i+1]&0x00ff) - (int16_t)(anzArray[i]&0x00ff);
Wenn das Array im normalen RAM liegen würde... könnte diese Zeile so vereinfacht werden:
z = (int16_t)anzArray[i+1] - anzArray[i];
Der Teil &0x00ff kann entfallen, da er bei 8 Bit gar nichts bewirkt. Der rechte Teil des Ausdrucks muss nicht zu int16 konvertiert werden, da der linke Teil bereits ein int16 ist.
Wenn das Array im normalen RAM liegen würde... könnte diese Zeile so
vereinfacht werden:
z = (int16_t)anzArray[i+1] - anzArray[i];
Ja, hatte ich zuerst so codiert, ein cast auf int16. Ist so wieder drin, nur halt mit dem __flash in der Definition des Arrays.
Wahrscheinlich wäre pgm_read_byte() portabler, weiß gar nicht genau das der __flash Präfix bewirkt.
War ein älterer Code wo die Anzeige manchmal Unsinn anzeigte und wollte der Sache nun auf den Grund gehen. Seltsam, dass es trotz des fehlerhaften Zugriffs überhaupt einigermaßen funktionierte.
Wegen der gcc-Version: im MAP-File fand ich dies: gcc/avr/5.4.0
Na ja, auf eine andere Toolchain mit anderem Compiler, wenn man den Code
in ein paar Jahren nochmal anfassen würde.
Als da wäre?
Für einen anderen (neueren) gcc passts immer, und für alle anderen existierenden AVR-Compiler musst du die Flash-Zugriffe eh umschreiben, weil sowohl PROGMEN als auch _flash gcc-spezifisch sind. Für andere Prozessoren gilt das dann sowieso.
Ist aber auch völlig egal, denn du wirst das eh niemals irgendwo hin portieren.
Wohin könnte das denn eventuell mal irgendwann portiert werden?
Oliver
Na ja, auf eine andere Toolchain mit anderem Compiler, wenn man den Code
in ein paar Jahren nochmal anfassen würde.
Am einfachsten und portabelsten geht das mit __flash, weil das im Gegensatz zu PROGMEM kein Attribute sondern ein Qualifier ist. Zu Beispiel so:
1
#ifndef __FLASH
2
#define __flash /* empty */
3
#endif
Damit hättest du das Programm dann auf Reduced Tiny wie ATtiny portiert, wo es keine Named Address-Spaces gibt. (Auf den Reduced Tiny braucht's eh kein PROGMEM oder Flash, weil .rodata ins Flash allokiert ist, und nicht ins RAM wie bei den meisten anderen AVRs).
z = (int16_t)(anzArray[i+1]&0x00ff) - (int16_t)(anzArray[i]&0x00ff);
Wenn das Array im normalen RAM liegen würde... könnte diese Zeile so
vereinfacht werden:
z = (int16_t)anzArray[i+1] - anzArray[i];
Der Teil &0x00ff kann entfallen, da er bei 8 Bit gar nichts bewirkt. Der
rechte Teil des Ausdrucks muss nicht zu int16 konvertiert werden, da der
linke Teil bereits ein int16 ist.
Noch nicht einmal dieser Cast ist nötig, da die Operanden sowieso implizit zu "int" konvertiert werden ("integer promotion").
z = (int16_t)(anzArray[i+1]&0x00ff) - (int16_t)(anzArray[i]&0x00ff);
liest du nämlich nur aus, was an den entsprechenden Addressen im RAM steht, du holst dir hier gar keine Daten aus dem Flash ab und deshalb stimmt auch die Rechnung nicht mit dem Erwartungswert überein.
Im Gegensatz zu dir holt sich das Microchip Studio Watch die Daten richtig aus dem Flash ab.
Das richtige Ergebnis müsstest du erhalten indem du
Wie bekommt man ein mathematisch korrektes Ergebnis trotz
Indem du z.B. die PROGMEM-Methoden zum Lesen der Daten aus dem
Flashspeicher verwendest.
...
1
z=(int16_t)pgm_read_byte(&anzArray[i+1])-
2
>(int16_t)pgm_read_byte[&anzArray[i]);
Ja richtig, der Vorschlag kam schon von Volker ganz oben im 4. Post, mit einer Alternative der __flash-Definition des Array. Beides funktioniert in dem Fall korrekt.
Die Diskussion ging jetzt hier noch darum, was der Code eigentlich für Daten im Fehlerfall verarbeitet.
Da das eigentliche Problem gelöst ist, sollte man den Thread beenden.
Ja. Aber kein Grund, diesen konstruktiven Thread zu sperren. Selbst in einem Jahr wird jemand "Lösungen" zum "Überlauf" präsentieren. Doch: Beide Gleichungen für z im Ausgangspost sind korrekt, für jeden C-Compiler (zumindest für 8-Bit-Bytes, evt. auch für 9-Bit-Bytes).
Nö, der stößt einen nur im Falle eines Falles mit der Nase auf
fehlerhaften Code.
Die Optimierung lässt hier falschen/kaputten Code so aussehen, als würde er funktionieren.
Das war ein Teil der Fragen hier, warum der falsche Code auch mal funktioniert.
Das ist eben die Optimierung, die einem eben hier genau diesen Streich spielt.
Wenn der Compiler den Wert von i kennt, dann rechnet der aus den konstanten Werten des Arrays bei eingeschalteter Optimierung das Ergebnis gleich selber aus. Der „falsche“ Speicherzugriff findet dann gar nicht statt. Zufall ist das dann nicht.
Ob der i kennen kann, ist aus dem Code oben nicht zu erkennen.
Die Optimierung lässt hier falschen/kaputten Code so aussehen, als würde
er funktionieren.
Das war ein Teil der Fragen hier, warum der falsche Code auch mal
funktioniert.
Das hat aber nicht unbedingt was mit Optimierung zu tun.
Ganz allgemein hat man die äquivalenten Implikationen
Code funktioniert nicht => Programm[*] ist nicht korrekt
Programm[*] ist korrekt => Code funktioniert
wobei [*] auch Transformatoren wie Compiler, Assembler, Linker etc. beinhaltet sowie Microcode/Silizium. Oft wird aber angenommen es sei auch
Code funktioniert => Programm korrekt
gültig, was idR nicht der Fall ist.
So kann man zum Beispiel aus bestandenen Tests idR /nicht/ schlussfolgern, das Programm sei korrekt, ganz egal ob nun Optimierungen aktiv sind oder nicht.
Die Optimierung lässt hier falschen/kaputten Code so aussehen, als würde
er funktionieren.
Das war ein Teil der Fragen hier, warum der falsche Code auch mal
funktioniert.
Das ist eben die Optimierung, die einem eben hier genau diesen Streich
spielt.
Ob ohne oder mit Optimierung spielt überhaupt keine Rolle.*) Das Ergebnis ist immer gleich. (Ohne Optimierung kann man vielleicht im Simulator ein paar mehr Variablen überprüfen, die sonst in Registern gehalten werden (AVR-Studio) oder 'wegoptimiert' wurden und somit im Watch-Fenster nicht angezeigt werden können.)
Hier: Das Programm macht genau das, was programmiert wurde! Einen Zugriff auf den RAM. Will man auf den Flash zugreifen, muss 'pgm_read_XXXX' verwendet werden. (bzw. '__flash' ... C, gcc)
Ob ohne oder mit Optimierung spielt überhaupt keine Rolle.
Bei dem Eingangsproblem, und ähnlichen, spielt das schon eine Rolle
Durch die Optimierung wird der Logische Fehler verdeckt.
Das ist der Streich, der einem da die Optimierung spielt.
Hier vereinfacht:
1
constintaPROGMEM{4};
2
constintbPROGMEM{5};
3
4
5
voidsetup()
6
{
7
Serial.begin(9600);
8
Serial.println(a+b);
9
}
10
11
voidloop()
12
{
13
14
}
Mit -Os kommt, trotz falschem Zugriff auf die Konstanten, eine 9. Reproduzierbar.
Was mit -O0 (oder wenn man die Konstanten volatile macht) kommt, ist nicht wirklich vorhersehbar.
Mit -Os kommt, trotz falschem Zugriff auf die Konstanten, eine 9.
OK, so betrachtet hast Du Recht: Der Code definiert eine Gleichsetzung, die in HW nicht gegeben ist, die der C-Compiler aber wegoptimieren darf.
Wenn ich (fälschlicherweise und ohne volatile) beschreibe, dass x @ 0x100 konstant 5 ist, liest er ohne Optimierung bei y=x was da wirklich steht. Mit Optimierung setzt er y=5 direkt, ohne zu lesen.
Ich sehe das eher so:
Der Compiler und seine Optimierungsstrategien haben (offensichtlich) keine/wenig Ahnung von den Speicher Sections, melden darum auch keinen Fehler. Haben keine Chance.
Die Section Attribute werden vom Compiler einfach nur an den Linker übergeben, welcher dann die Daten in den Sections verteilt und die Adressen in den generierten Code einträgt.
Ob man sagt "es hat keine andere Chance", oder "der darf das", hilft in der Praxis nicht wirklich weiter.
Gerade auch in Verbindung mit Schleifen kann einen der Effekt/Streich überraschen.
Programm läuft gut und scheinbar richtig (mit -Os).
Nur eine Schleifeniteration hinzugefügt, und es kommt nur noch Müll raus.
Ob man sagt ... "der darf das", hilft in der Praxis nicht wirklich weiter.
Da bin ich anderer Meinung. C ist in vielen Bereichen genau definiert. Und wenn ich explizit sage, dass eine Variable immer (konstant) 5 ist, dann "darf" der Compiler das berücksichtigen, muss es aber nicht.
Sie es anders rum: Wenn der Compiler sich nicht auf meine Angaben verlassen kann, worauf dann?
Der Klassiker ist sicher das fehlende volatile eines flags oder eines Schleifenzählers. Hier darf der Compiler annehmen, dass es nicht volatile ist.
Der Compiler und seine Optimierungsstrategien haben (offensichtlich)
keine/wenig Ahnung von den Speicher Sections, melden darum auch keinen
Fehler. Haben keine Chance.
Das ist ja jetzt nichts neues. Das Konzept der verschiedenen Speichertypen kennt C nicht, und damit kennt das auch der Compiler nicht.
Was nichts daran ändert, daß ein fehlerhafte Programm fehlerhaft ist, auch wenn es anscheinend die erwarteten Ergebnisse liefert.
Wenn der Compiler sich nicht auf meine Angaben
verlassen kann, worauf dann?
Menschen machen Fehler, übersehen was, vergessen was.
Das dürfte der Grund dafür sein, dass den Compilern über die Jahre beigebracht wurde immer mehr von diesen Problemstellen zu erkennen und Meldungen zu werfen.
Diese Meldungen sind durchaus hilfreich um (potentielle) Fehler auszubügeln.
Hier kommt leider keine Meldung!
Das ist schade.
In C weniger Problem, da man die Alternative __flash hat.
Aber in C++ mit seinem PROGMEM kann einem das schon auf die Füße fallen, bzw. als Falle Jahrelang in häufig wiederverwendeten Programmteilen schlummern.
Aber in C++ mit seinem PROGMEM kann einem das schon auf die Füße fallen,
bzw. als Falle Jahrelang in häufig wiederverwendeten Programmteilen
schlummern.
Diese Konstanten alle in einer Klasse privat 'verstecken'?