Guten Abend zusammen, ich habe vor Jahren ein Projekt entwickelt um den BMP180-Druck- und Temperatursensor in eine Telemetrieeinheit für einen Stratosphärenballon zu integrieren. Jetzt habe ich den Code auf einen STM32F4 portiert und stehe vor einem kleinen Problem: Ich lese die Kalibrierdaten aus dem EEPROM mit I2C aus, unter denen sich auch einige negative Werte befinden. Z. B. hat die Variable "ac2" aus dem Parametersatz den Wert -1056. Diesen habe ich ermittelt, indem ich den Sensor an den AVR angeschlossen habe, um mir die Werte aus dem EEPROM anzeigen zu lassen. Auf dem AVR ist die Ausgabe korrekt ("-1056"), auf dem STM32 nicht. Beide Compiler (jeweils aus der GCC) laufen auf dem gleichen Rechner unter Linux Mint 18. Das Auslesen der Daten aus dem Sensor beim STM32 funktioniert fehlerfrei. Wenn ich die Software auf diesem Kontroller laufen lasse, erhalte ich aus den beiden Registern die zur Variablen "ac2" gehörenden Werte 0xAC=251 und 0xAD=224. Nach der entsprechenden Addition von MSB und LSB ergibt sich ein Wert von 64480 also genau das, was dem Wert -1056 als "signed" Variable entspricht. Hier der relevante Codeauszug: int ac2; ... int msb, lsb; msb = i2c_read(0xAC); lsb = i2c_read(0xAD); ac2 = (msb << 8) + lsb; Die Funktion zum Lesen der Daten aus dem Sensor ist ebenfalls als "signed" definiert: int i2c_read(uint8_t regaddr) {....} Wie bekomme ich den Compiler dazu, das 2er-Komplement korrekt zu behandeln? Eine explizite Typumwandlung hat keinen Erfolg gebracht. Vielen Dank für's Lesen! Peter
int ist auf dem avr 16 bit groß und auf dem arm 32 bit.
Verwende statt int int16_t und am Anfang des c-Files "#include <stdint.h>". Dann sollte der Quelltext auf AVR und ARM gleichermaßen laufen. Zumindest das kurze Code-Beispiel.
2⁵ schrieb: > int ist auf dem avr 16 bit groß und auf dem arm 32 bit. Das kann man mit int16_t in den Griff bekommen. Das Handling der Endianess sieht ja schon gut aus.
Peter R. schrieb: > int ac2; > ... > int msb, lsb; > > msb = i2c_read(0xAC); > lsb = i2c_read(0xAD); > ac2 = (msb << 8) + lsb; Das ist ein potentieller Overflow.
Al Fine schrieb: >> int msb, lsb; >> >> msb = i2c_read(0xAC); >> lsb = i2c_read(0xAD); >> ac2 = (msb << 8) + lsb; > > Das ist ein potentieller Overflow. Insbesondere ist es undefined behaviour, wenn "msb" eine negative Zahl ist. "If E1 has a signed type and nonnegative value, and E1 × 2 E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined."
Wenn lsb negativ ist, geht es auch schief (wird dann bei der Addition mit Einsen auf volle Länge aufgezogen). Besser wäre es m.E etwa so:
1 | uint16_t msb, lsb; |
2 | msb = 0xFF & i2c_read(0xAC); |
3 | lsb = 0xFF & i2c_read(0xAD); |
4 | ac2 = (int16_t)( (msb << 8) | lsb ); |
:
Bearbeitet durch User
In der Annahme, dass i2c_read() so deklariert ist, dass es einen uint8_t zurück gibt, braucht es das "0xFF &" nicht. Aber ja, die Zwischenwerte vorzeichenlos zu speichern, ist auf jeden Fall sinnvoll.
Deshalb: Peter R. schrieb: > Die Funktion zum Lesen der Daten aus dem Sensor ist ebenfalls als > "signed" definiert: > > int i2c_read(uint8_t regaddr) > {....}
Klaus W. schrieb: > Deshalb: OK, aber da würde ich lieber das ändern, als das irgendwie dann durch verUNDen mit 0xFF zu kaschieren. Die Funktion liest ja erst einmal Bytes, und das bezeichnet man sinnvollerweise mit uint8_t. Zusammenbau größerer Objekte oder Uminterpretieren in vorzeichenbehaftet würde ich erst danach machen.
Du hast da vollkommen recht. Aber ich hatte es so verstanden, daß die Lesefunktion unabänderlich vorgegeben ist und unsinnigerweise ein Byte als int liefert. Wenn man einen Daumen auf der Funktion hat, sollte man sie in der Tat zu uint8_t abändern. Dann würden meine beiden 0xFF&... entfallen.
Klaus W. schrieb: > Aber ich hatte es so verstanden, daß die Lesefunktion unabänderlich > vorgegeben ist und unsinnigerweise ein Byte als int liefert. Das hat in C ja Tradition.
Jörg W. schrieb: > Insbesondere ist es undefined behaviour, wenn "msb" eine negative Zahl > ist. Für GCC und Clang kann man das mit -fno-strict-overflow beheben.
Nop schrieb: > Jörg W. schrieb: > >> Insbesondere ist es undefined behaviour, wenn "msb" eine negative Zahl >> ist. > > Für GCC und Clang kann man das mit -fno-strict-overflow beheben. Kann man machen. Man kann es aber auch einfach richtig machen.
mh schrieb: > Kann man machen. Man kann es aber auch einfach richtig machen. Das ist "richtig". Deswegen wird das im Linuxkernel auch so gemacht. Schließlich sind CPUs, auf denen das Zweierkomplement nicht genutzt wird, ausgesprochen unüblich geworden - und nur das war überhaupt der Grund für das UB. Mal abgesehen davon, daß das von Anfang an nicht UB, sondern IB hätte sein sollen.
Nop schrieb: > Das ist "richtig". Für den hier genannten Fall nicht, denn es geht auch anders (und damit durchaus schöner und standardkonform portabel). Auch würde ich sowas wie einen Kernel (egal von wem) nicht als Referenz für "das ist richtig" benutzen, wenn es um Features einer Programmiersprache geht. Nicht-2er-Komplement-Maschinen könnten perspektivisch im C-Standard keine Berücksichtigung mehr finden, wenn ich die Diskussionen in WG14 richtig aufgefasst habe, über das, was UB darf und was nicht, wird gerade heiß diskutiert.
Jörg W. schrieb: > Für den hier genannten Fall nicht, denn es geht auch anders Naja, man kann natürlich einfach entsprechend aufaddieren, um im Wertebereich eines uint16_t zu sein. Oder, sofern man die Endianess im Griff hat, kann man auch mit memcpy arbeiten (wird ja wegoptimiert), oder zumindest für C mit einer union. > Auch würde ich sowas wie einen Kernel (egal von wem) nicht als Referenz > für "das ist richtig" benutzen, wenn es um Features einer > Programmiersprache geht. Der Hinweis ist dahingehend zu verstehen, daß die leitenden Entwickler des Linuxkernels kompetenter sind als jeder Poster hier und insofern diese Lösung nicht pauschal als Anfängerquark zu verwerfen ist. Schlußendlich geht es nicht darum, den C-Standard zu befriedigen, sondern richtig arbeitende Software zu schreiben. Wenn man dabei um den teilweise ziemlich kaputten Standard herumarbeitet und sich dessen bewußt ist, kann das durchaus vertretbar sein. > Nicht-2er-Komplement-Maschinen könnten perspektivisch im C-Standard > keine Berücksichtigung mehr finden, wenn ich die Diskussionen in WG14 > richtig aufgefasst habe Wäre sinnig. > über das, was UB darf und was nicht, wird gerade heiß diskutiert. Da ist man damals einfach zu sehr mit der Gießkanne durchmarschiert - bis dahin, daß simple Compiletime-Fehler als UB definiert wurden (fehlendes abschließendes ' oder " etwa).
Nop schrieb: > Der Hinweis ist dahingehend zu verstehen, daß die leitenden Entwickler > des Linuxkernels kompetenter sind als jeder Poster hier Das schließe ich daraus nicht. Sie sind pragmatisch. Das ist nicht per se verkehrt.
Jörg W. schrieb: > Das schließe ich daraus nicht. Das ist auch nicht "daraus" zu schließen. Das ist eine Voraussetzung, keine Schlußfolgerung.
Nop schrieb: > Das ist eine Voraussetzung Nein, eine Behauptung. Es deklariert eine Art Gottheit, die über alle technischen Argumentationen erhaben ist, da sie ja per se alles richtig macht. Das mag ich daran nicht.
Jörg W. schrieb: > Nein, eine Behauptung. Es deklariert eine Art Gottheit Nein, tut es nicht. In einer Meritokratie stehen Leute nämlich nicht aus Dogma an der Spitze, sondern weil sie mehr zuwege gebracht haben als jeder hier im Forum. Sofern Du keine vergleichbare Meriten vorzuweisen hast, zeugt es lediglich von Dunning-Kruger Deinerseits, wenn Du mit religiösen Begriffen ankommst.
Nop schrieb: > Sofern Du keine vergleichbare Meriten vorzuweisen hast Wenn das für dich dein einziges Kriterium ist: tschüss. Mit solchen Leuten muss ich nicht diskutieren, erst recht nicht anonym.
Peter R. schrieb: > Auf dem AVR > ist die Ausgabe korrekt ("-1056"), auf dem STM32 nicht. Schön, und nun? Wie wärs damit, auch mal den fehlerhaften Wert zu zeigen. Einfach nur "Fehler" zu rufen, bringt nicht viel. Glück für Dich, daß die Profis den Fehler auch ohne Deine Hilfe entdeckt haben.
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.