Suche nach einem praktikablen c-Syntax, der Rechenfehler beim Erweitern
von uint8_t auf int16_t bei unsigned Werten >127 vermeidet.
Weiß auch nicht, warum das gerade jetzt nicht klappt, das Beispiel:
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.
Wulf D. schrieb:> 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().
Grüßle,
Volker
Volker B. schrieb:> Warum verwendest Du nicht __flash? Dann geht's auch ohne> pgm_read_byte().
Kommt meiner dunklen Erinnerung nach darauf an welche GCC
Version man verwendet.
> 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.
Wastl schrieb:> Volker B. schrieb:>> Warum verwendest Du nicht __flash? Dann geht's auch ohne>> pgm_read_byte().>> Kommt meiner dunklen Erinnerung nach darauf an welche GCC> Version man verwendet.
Wird unterstützt ab GCC v4.7 (Release 2012).
https://gcc.gnu.org/gcc-4.7/changes.html#avr
Allerdings wird es nicht in C unterstützt sondern nur mit GNU-C; mit
-std=c99 funktioniert es also nicht.
Steve van de Grens schrieb:> 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
Gruß Wulf
Wulf D. schrieb:> Seltsam, dass es trotz des> fehlerhaften Zugriffs überhaupt einigermaßen funktionierte.
Den Effekt kenne ich.
Da ist es dann die Optimierung, welche das richtige Verhalten
vortäuscht.
Oliver S. schrieb:> Wulf D. schrieb:>> Wahrscheinlich wäre pgm_read_byte() portabler>> 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.
Volker B. schrieb:> Wenn Du (Byte-)Daten mittels PROGMEM ablegst, musst Du auf diese mittels> pgm_read_byte() zugreifen.Wulf D. schrieb:> Seltsam, dass es trotz des> fehlerhaften Zugriffs überhaupt einigermaßen funktionierte.
@TO:
Denkfehler.
Die Variablenüberwachung sieht anhand des Codes, dass Daten aus dem
Flash angezeigt werden sollen.
Das übersetzte Programm zieht sich die Werte aus dem RAM: Sie mal nach,
was 'Wert aus Adresse 0x28' minus 'Wert aus Adresse 0x27' ergibt...
Wulf D. schrieb:> Seltsam, dass es trotz des> fehlerhaften Zugriffs überhaupt einigermaßen funktionierte.
die -131 sind m.E. nicht mit signed/unsigned erklärlich.
Wulf D. schrieb:> 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.
Oliver
Wulf D. schrieb:> Oliver S. schrieb:>> Wulf D. schrieb:>>> Wahrscheinlich wäre pgm_read_byte() portabler>>>> 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).
Steve van de Grens schrieb:>> 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").
P. S. schrieb:> Noch nicht einmal dieser Cast ist nötig, da die Operanden sowieso> implizit zu "int" konvertiert werden ("integer promotion").
Jepp. Wobei nur bei avr ein int einem int16_t entspricht.
Bei 32Bit CPUs wäre es ein int32_t. Aber da eh nicht portiert wird,
wurst.
Ralf G. schrieb:> Wulf D. schrieb:>> Seltsam, dass es trotz des>> fehlerhaften Zugriffs überhaupt einigermaßen funktionierte.>> @TO:> Denkfehler.> Die Variablenüberwachung sieht anhand des Codes, dass Daten aus dem> Flash angezeigt werden sollen.>> Das übersetzte Programm zieht sich die Werte aus dem RAM: Sie mal nach,> was 'Wert aus Adresse 0x28' minus 'Wert aus Adresse 0x27' ergibt...
Das liefert auch nicht diese Differenz.
Im unterem RAM ab 0x0060,data ist alles auf default 0x00. Es wird nur
wenig SRAM benutzt.
Wulf D. schrieb:> Im unterem RAM ab 0x0060,data ist alles auf default 0x00
Also konkret: RAM an Adresse 0x28 und 0x29 ist 0?
Alternativ lade anzArray[i] in eine uint8 und in eine uint16 variable
(am Besten volatile) und lass die dann ausgeben.
Am Ende gibt es irgendwo einen nachvollziehbaren Fehler. Und der hat
nichts mit Überlauf zu tun.
Wulf D. schrieb:> Wie bekommt man ein mathematisch korrektes Ergebnis trotz
Indem du z.B. die PROGMEM-Methoden zum Lesen der Daten aus dem
Flashspeicher verwendest. Mit
Wulf D. schrieb:> 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
M. K. schrieb:> Wulf D. schrieb:>> 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.
Bruno V. schrieb:> Wulf D. schrieb:>> Im unterem RAM ab 0x0060,data ist alles auf default 0x00>> Also konkret: RAM an Adresse 0x28 und 0x29 ist 0?>> Alternativ lade anzArray[i] in eine uint8 und in eine uint16 variable> (am Besten volatile) und lass die dann ausgeben.
Ja, im SRAM steht fast nur 0x0. Habe deinen Vorschlag getestet:
Aus dem SRAM kommen die Daten nicht, jedenfalls nicht aus der vermuteten
Stelle.
Da das eigentliche Problem gelöst ist, sollte man den Thread beenden.
P.S.: Vielleicht aus den Registern, wenn da ab Adr.0x27 b1 30 stehen
würde, aber da steht b3 30. Vielleicht wird beim Lesen etwas verbogen.
Wulf D. schrieb:> 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).
Wulf D. schrieb:> P.S.: Vielleicht aus den Registern, wenn da ab Adr.0x27 b1 30 stehen> würde, aber da steht b3 30. Vielleicht wird beim Lesen etwas verbogen.
Arduino F. schrieb:> Der Optimizer spielt dir (manchmal) Streiche!
Nö, der stößt einen nur im Falle eines Falles mit der Nase auf
fehlerhaften Code.
Oliver
Oliver S. schrieb:> 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.
Arduino F. schrieb:> Das ist eben die Optimierung, die einem eben hier genau diesen Streich> spielt.
Der C-Code zeigt auf falschen Speicher. Der Code ist ansonsten richtig,
egal ob optimiert oder nicht.
Bruno V. schrieb:> Der C-Code zeigt auf falschen Speicher.
Das weiß ich doch!
Nichts anderes sage ich.
Dank Optimierung kommt trotzdem das richtige Ergebnis, obwohl der
Zugriff falsch ist.
Arduino F. schrieb:> Dank Optimierung kommt trotzdem das richtige Ergebnis, obwohl der> Zugriff falsch ist.
Eher durch Zufall. Der Optimizer hat nichts verschleiert oder repariert.
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.
Oliver
Arduino F. schrieb:> 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
1) Code funktioniert nicht => Programm[*] ist nicht korrekt
2) Programm[*] ist korrekt => Code funktioniert
wobei [*] auch Transformatoren wie Compiler, Assembler, Linker etc.
beinhaltet sowie Microcode/Silizium. Oft wird aber angenommen es sei
auch
3) 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.
Johann L. schrieb:> Das hat aber nicht unbedingt was mit Optimierung zu tun.
Das ein- und ausschalten der Optimierung bringt es ans Licht, dass der
Code kaputt ist.
Genau das hat die Optimierung damit zu tun.
Nicht mehr und nicht weniger.
Wulf D. schrieb:> Wie bekommt man ein mathematisch korrektes Ergebnis trotz> Datentypen-Mix?
Mach es in Assembler. Hilft übrigens auch, einfachen Code sinnvoller zu
strukturieren ;)
Arduino F. schrieb:> 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)
*) ja, es gibt ein seltene Ausnahmen...
Ralf G. schrieb:> 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.
Das gilt genauso für die .eeprom Section
Ralf G. schrieb:> Hier: Das Programm macht genau das, was programmiert wurde! Einen> Zugriff auf den RAM.
Eben nicht!
Mit -Os tut es das eben nicht, oder nicht unbedingt.
Mit -O0 greift es ins RAM und liefert sicherlich unerwünschtes.
> Will man auf den Flash zugreifen, muss> 'pgm_read_XXXX' verwendet werden. (bzw. '__flash' ... C, gcc)
Das musst du mir nicht erklären!
Arduino F. schrieb:> 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.
Bruno V. schrieb:> OK, so betrachtet hast Du Recht:
Ich danke dir für die Zustimmung.
Bruno V. schrieb:> aber wegoptimieren darf.
Darf, nicht darf...
Naja.
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.
Arduino F. schrieb:> 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.
Arduino F. schrieb:> 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.
Oliver
Bruno V. schrieb:> 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.
Arduino F. schrieb:> 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'?