Forum: Mikrocontroller und Digitale Elektronik C: Werte aus Array mit 3.3 multiplizieren?


von olaf (Gast)


Lesenswert?

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_t buffer[2];
2
uint16_t sp;
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???

von Carl D. (jcw2)


Lesenswert?

Weil 255 255 bleibt und -1 -1 wird.

von olaf (Gast)


Lesenswert?

olaf schrieb:
> buffer[0]= 0xFF

hier habe ich mich natürlich verschrieben. Sollte lauten:
buffer[1]=0xFF

von Walter S. (avatar)


Lesenswert?

olaf schrieb:
> sp = *buffer;

heißt
sp = buffer[0];

wäre schlimm wenn da nicht 0xff in sp stünde

von Stefan W. (bier16v)


Lesenswert?

olaf schrieb:
1
> buffer[0]= 0xFF
2
> buffer[0]= 0xFF
3
> sp= 0x00FF
> 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.

von Zupfler (Gast)


Lesenswert?

Probier mal:

sp = (buffer[0] * 255) + buffer[1]);

von Zupfler (Gast)


Lesenswert?

Oder:


sp = (  ( (buffer[0]) + buffer[1]  * 255) * 33) / 10 );

von olaf (Gast)


Lesenswert?

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.

von olaf (Gast)


Lesenswert?

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.

von Dergute W. (derguteweka)


Lesenswert?

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

von Stefan W. (bier16v)


Lesenswert?

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_t sp = ((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.

von Axel S. (a-za-z0-9)


Lesenswert?

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_t buffer[2];
2
uint16_t sp;
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.

von Ralf G. (ralg)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

Zupfler schrieb:
> Oder:
>
> sp = (  ( (buffer[0]) + buffer[1]  * 255) * 33) / 10 );

* 256

aber auf 16-Bit Systemen kommt es zum Überlauf.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

@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.

von c-hater (Gast)


Lesenswert?

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...

von Dirk B. (dirkb2)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.