Hallo,
ich erhalte von einem ADC zwei Bytes mit jeweils dem Wert 0xFF, die in
einem Array "buffer" gespeichert werden. Den Wert aus dem Array, also
0xFFFF, möchte ich nun mit 3.3 mutltiplizieren. Aber wie mache ich das?
Dafür habe ich den Wert aus dem Array zunächst in einer extra Variable
"sp" gespeichert.
1
uint8_tbuffer[2];
2
uint16_tsp;
3
4
HAL_I2C_Master_Receive(&hi2c1,0x6B,buffer,2,10);
5
6
sp=*buffer;
Wenn ich mir nun die Variablen anschaue:
buffer[0]= 0xFF
buffer[0]= 0xFF
sp= 0x00FF
Wieso hat die Variable sp nun nicht den Wert 0xFFFF???
> Wieso hat die Variable sp nun nicht den Wert 0xFFFF???
Der zweite Index müsste ja 1 sein, du hast zwei mal den Index 0
verwendet.
Wenn du das Ergebnis deiner Multiplikation in der Variable sp speichern
willst, muss du den Datentyp anpassen. Ja nachdem wie "genau" dein
Ergebnis sein muss und in welchem Wertebereich sich dein Buffer bewegt,
gibt es unterschiedliche Möglichkeiten.
Du könntest ja deinen Wert mit 33 multiplizieren und dann durch 10
dividieren, wenn du bei den Zwischenergebnissen keinen Überlauf hast
wäre das eine Einfache Möglichkeit ganz ohne Datentypumwandlung.
Zupfler schrieb:> sp = (buffer[0] * 255) + buffer[1]);
Damit erhält man 0xFF00.
Stefan W. schrieb:> Ja nachdem wie "genau" dein> Ergebnis sein muss und in welchem Wertebereich sich dein Buffer bewegt,> gibt es unterschiedliche Möglichkeiten.> Du könntest ja deinen Wert mit 33 multiplizieren und dann durch 10> dividieren, wenn du bei den Zwischenergebnissen keinen Überlauf hast> wäre das eine Einfache Möglichkeit ganz ohne Datentypumwandlung.
Das wäre dann ja meine nächste Aufgabe. Aber als erstes müsste ich mal
in einer Variablen den Wert 0xFFFF erhalten.
Stefan W. schrieb:> Wenn du das Ergebnis deiner Multiplikation in der Variable sp speichern> willst, muss du den Datentyp anpassen.
Welchen Datentyp bräuchte ich? Habe extra uint16_t verwendet, damit ich
zwei Bytes speichern kann, also genau den Wert 0xFFFF.
sp = (buffer[0]<<8) | buffer[1];
So würde ich den richtigen Wert 0xFFFF rausbekommen.
Aber mich würde interessieren, wie ich diesen Wert direkt aus dem Array
in eine neue Variable speicher.
olaf schrieb:> sp = (buffer[0]<<8) | buffer[1];>> So würde ich den richtigen Wert 0xFFFF rausbekommen.
So ist's auch OK, wenn's mit der Endianess vom ADC passt.
> Aber mich würde interessieren, wie ich diesen Wert direkt aus dem Array> in eine neue Variable speicher.
So:
1
sp=*(uint16_t*)buffer;
Aber das macht sicher jede Menge Aerger, wenn buffer nicht 16bit aligned
ist oder eben die Endianess nicht passt.
Gruss
WK
Wenn du den Wert 0xffff mit 3,3 multiplizieren willst, wird dir der
Datentyp uint16 zu klein, da 0xffff schon der maximal darstellbare Wert
ist. D.h. du musst schon den nächst größeren Datentyp uint32 verwenden.
Mit dem geht auch die einfache Multiplikation mit 3,3 indem du mit 33
multiplizierst und dann durch 10 dividiert.
1
uint32_tsp=((buffer[0]<<8)|buffer[1])*33;
2
sp=sp/10;
Dabei verlierst du halt die Nachkommastellen, aber bei den großen Werte
ist das vermutlich vernachlässigbar.
olaf schrieb:> ich erhalte von einem ADC zwei Bytes mit jeweils dem Wert 0xFF, die in> einem Array "buffer" gespeichert werden. Den Wert aus dem Array, also> 0xFFFF
Wenn das eine 16-Bit Zahl ist, die lediglich für den Transport in zwei
Bytes zerlegt wird, warum speicherst du die überhaupt erst in einem
Array? Um auf einzelne Bytes (oder Bits) einer Ganzzahl-Variable
zuzugreifen, hat C leistungsfähige Operatoren zum Verschieben und
ausmaskieren (arithmetisches UND). Genauso zum zusammenkleben mit
Schieben und arithmetischem ODER. Siehe den Artikel Bitmanipulation.
> möchte ich nun mit 3.3 mutltiplizieren. Aber wie mache ich das?
Für eine Multiplikation mit 3.3 wirst du die Zahl im allgemeinen Fall in
eine Fließkommazahl umwandeln müssen. Eventuell reicht aber auch
Multiplikation mit 33 und anschließende Division durch 10. Beachte aber
mögliche Überläufe und Rundungsfehler.
> Dafür habe ich den Wert aus dem Array zunächst in einer extra Variable> "sp" gespeichert.
1
uint8_tbuffer[2];
2
uint16_tsp;
3
4
HAL_I2C_Master_Receive(&hi2c1,0x6B,buffer,2,10);
5
6
sp=*buffer;
Nein, hast du nicht. *buffer ist in diesem Kontext das gleiche wie
buffer[0]. Aber anstatt irgendwelchen Dereferenzierungsorgien, womöglich
noch gespickt mit wilden typecasts solltest du lieber die Grundlagen
lernen. Portabel und sicher geht das mit Bitmanipulation. Und wenn
dein Compiler aus diesem Jahrtausend ist, dann verschiebt und verundet
er für den Zugriff auf einzelne Bytes auch nichts, sondern greift direkt
auf die entsprechenden Speicherstellen zu. Sofern die Zielplattform
byteweisen Zugriff überhaupt hat.
Zupfler schrieb:> Probier mal:>> sp = (buffer[0] * 255) + buffer[1]);
Nein. Nicht 255.
Wenn du (ständig) von einem ADC '0xFF' erhälst, dannkannst du den ADC
gleich weglassen und deine 0xFFFF mit 3.3 multiplizieren ;-(
...
olaf schrieb:> So würde ich den richtigen Wert 0xFFFF rausbekommen.
Völlig falscher Ansatz!
Ich rate jetzt mal: Das sollte nur ein Beispiel sein? da nimmt man aber
was Griffiges, z.B. '0x12' und '0x34', dann sieht man nämlich auch, ob
die Bytes in der für das Zielsystem richtigen Reihenfolge
zusammengebastelt werden.
olaf schrieb:> sp = (buffer[0]<<8) | buffer[1];>> So würde ich den richtigen Wert 0xFFFF rausbekommen.> Aber mich würde interessieren, wie ich diesen Wert direkt aus dem Array> in eine neue Variable speicher.
Genau so.
Vertraue dem Compile. Der erkennt dein Vorhaben und macht daraus (meist)
das Optimale. Gerade bei solch trivialen Dingen.
@TO
Wenn dein ADC und dein Zielsystem mit der gleichen Byteorder 'arbeiten'
und du dir 100% sicher bist, dass sich da niemals was ändern wird,
kannst du beide Variablen auch in eine Union packen.
Dirk B. schrieb:> Vertraue dem Compile. Der erkennt dein Vorhaben und macht daraus (meist)> das Optimale. Gerade bei solch trivialen Dingen.
Nein. Der Compiler kann ja schliesslich nicht wissen, mit welcher
"Endianess" die Bytes im Buffer liegen, da er keinerlei Information über
die Quelle der Daten besitzt. Das kann ihm also nur der Programmierer
sagen.
Und wer der Programmierer kompetent genug ist, ihm das zu sagen, ist er
auch kompetent genug, dem Compiler den optimalen Weg zur Zusammenführung
der Daten zu sagen. Nur dann weiss man nämlich sicher, dass der Compiler
auch tatsächlich den optimalen Weg benutzen wird.
Sprich: eigentlich braucht man keinen Compiler, wenn man diesem doch
alles haarklein erklären muss, um zuverlässig ein richtiges und
obendrein optimales Ergebnis zu erzielen...
c-hater schrieb:> Nein. Der Compiler kann ja schliesslich nicht wissen, mit welcher> "Endianess" die Bytes im Buffer liegen, da er keinerlei Information über> die Quelle der Daten besitzt. Das kann ihm also nur der Programmierer> sagen.
Genau das sagt der Programmierer doch mit der Zeile.
Der Compiler macht dasraus dann optimalen Maschinencode.
Der muss da nicht schieben, sondern greift gleich Byteweise darauf zu.
Oder liest 16-Bit ein und swapt die Bytes, falls es erforderlich ist.
Bei union oder cast von Pointern, bist du abhängig von der Endianess.
Dirk B. schrieb:> Der Compiler macht dasraus dann optimalen Maschinencode.
Nur in deinen Träumen. Tatsächlich aber ist es ein Glücksspiel. Es hängt
nicht nur vom Compiler selber und den gewählten Optimierungsoptionen ab,
sondern auch vom konkreten Target.
Letzte Sicherheit bietet erst die Lektüre und Analyse des erzeugten
Maschinencodes. Wenn ich aber diesen Aufwand treiben muss, um sicher
sein zu können, dass die Sache optimal umgesetzt wird, dann schreibe ich
sie doch lieber gleich selber optimiert hin. Und zwar in einer Sprache,
die sich dafür eignet, deterministische Ergebnisse zu produzieren und
somit kein Glücksspiel ist, bei dem (bestenfalls) die formale
Korrektheit garantiert ist, aber niemals die optimale Effizienz.
Hi
Jetzt haben wir gelernt, daß der TO doch erst Mal anzugeben hat, welchen
Compiler Er wohl benutzt, um daraus erkennen zu können, ob Dieser
überhaupt mit dieser Aufgabe 'optimalen' Code erstellen kann.
Häh?
Der TO hat das Problem, daß bei Seiner Rechnung nicht das Ergebnis raus
kommt, was Er erwartet hat.
OB die $FF korrekt zu einem Word zusammen gebäbbt werden, ist auch noch
nicht klar - dafür erprobt der TO gerade die obere Grenze der Messwerte
- was ja auch Mal passieren muß.
Was sich der TO ggf. von Euch gewünscht hätte, ist sehr wahrscheinlich,
wie Er diese besch***grmpfl Berechnung hinzuschreiben hat, daß durch
'$FFFF x 3.3' hinten '1B331,8' heraus kommt.
Vll. mit dem Hinweis, daß die 16-Bit-Variabel mit $FFFF bereits
vollständig gefüllt ist und hier nicht Mehr möglich ist (eben Diese zu
$0000 überläuft, wenn man nur 1 dazu addiert).
Er also mehr Bits braucht - denke, 32 wird die nächste Grenze sein.
Dann sollen Fließkommazahlen recht viel Rechenzeit verbraten - eine
Integer-Multiplikation mit 33 und eine Integerdivision mit 10 erschlagen
die Berechnung (ohne Komma) binen eines Bruchteil der Rechenzeit und des
benötigten Speicherplatz.
Ausnahme: Float wird eh benutzt, dann wird der Compiler wohl so schlau
sein, diese Routine hierfür erneut zu nutzen und nicht 2x einzubinden.
MfG