Forum: Mikrocontroller und Digitale Elektronik float aus UART berechnen


von Draco (Gast)


Lesenswert?

Ich habe da mal ne Frage :D

Mir stehen zwei Ansatzpunkte zur Verfügung. Wobei ich eher den zweiten 
Ansatzpunkt, aus Sichtpunkt der Geschwindigkeit, bevorzuge.

Der Hintergrund:

Ich möchte mehrere Werte (ersteinmal drei um genau zu sein) von einem PC 
über RS232 zu einem µC (tiny2313) schicken. Die Werte liegen mir als 
32Bit Float im PC vor, sind aber alles 16Bit und 8Bit Integer Werte 
(einmal bis max 300, 10 und 10.000). PC Seitig programmiere ich mit C# 
und den µC mit Atmel Studio.

Meine Überlegungen:

1. Die Werte direkt als Float rausschicken, da komme ich halt auf 
insgesamt 12Byte. Der µC muss dann den Float in Int umrechnen um 
weiterzumachen. Da aber so alle 50-100ms ein Paket kommt, sind selbst 
12Byte schon viel. Desweiteren muss ich den Float ja auch umrechnen, die 
würde ich dann in etwa so bewerkstelligen wollen:
1
unsigned int *temp;
2
float val;
3
4
temp = (unsigned int *)&val;


2. Überlegung: Die Werte schon direkt im PC mit C# umrechnen und dann 
als Integer (Char 16Bit) schon rüber schicken. Da komme ich dann auf 
bloß 6 Byte. Ist ja immerhin bloß die hälfte. Nur, weiß ich nicht genau 
wie ich in C# die den Float richtig in Char bringe?! Momentan mach ich 
dies für alle drei Werte so (Schon mit UART, die 132 ist die Position im 
ByteField - davon werden 4Byte (Float) genutzt):

1
ComPort.Write(Convert.ToInt16(BitConverter.ToSingle(OutBytes, 132)).ToString());

Da überträgt er es ja dann direkt als String aber, das heißt ich müsste 
im Tiny den String erst wieder in eine Integer wandeln - und genau da 
ist mein Problem! Das kostet mit atoi wahnsinnig viel Speicher und vor 
allem auch Zeit.



Wie kann ich dies besser lösen?! Jemand nen Ansatz?!
Wäre euch sehr dankbar!

von Loddar (Gast)


Lesenswert?

Draco schrieb:
> Die Werte liegen mir als
> 32Bit Float im PC vor, sind aber alles 16Bit und 8Bit Integer Werte

dann schick sie als Integer, die Float Darstellung in PC und mc sind 
nicht notwendigerweise gleich, 8/16 Bit schon (endianess beachten)

von -.-.- (Gast)


Lesenswert?

Draco schrieb:
> Der Hintergrund:
>
> Ich möchte mehrere Werte (ersteinmal drei um genau zu sein) von einem PC
> über RS232 zu einem µC (tiny2313) schicken. Die Werte liegen mir als
> 32Bit Float im PC vor, sind aber alles 16Bit und 8Bit Integer Werte
> (einmal bis max 300, 10 und 10.000). PC Seitig programmiere ich mit

Entweder dein Datentyp ist ein float (32-bit) oder ein int. Was meinst 
du mit "sind alles 16Bit und 8 Bit Integer Werte"?.

Draco schrieb:
> dies für alle drei Werte so (Schon mit UART, die 132 ist die Position im
> ByteField - davon werden 4Byte (Float) genutzt):
>
> ComPort.Write(Convert.ToInt16(BitConverter.ToSingle(OutBytes,
> 132)).ToString());
>
> Da überträgt er es ja dann direkt als String aber, das heißt ich müsste
> im Tiny den String erst wieder in eine Integer wandeln - und genau da


Ja, der überträgt das als String, weil die Funktion Write vermutlich 
einen String überträgt. Du kannst aber auch deine Zahlen einzeln 
raushauen, also z.B. zuerst die ersten 8 Bit deiner 16 Bit Zahl, dann 
die nächsten 8 Bit. usw. Dann hast du auch keinen String, sondern nur 
ein Bytefeld.
Ich versteh aber nicht was dein Problem ist, weil ein String ja nur ein 
Zeichen mehr hat, als deine einzelnen Zeichen, nämlich die 0.
Das du nach jedem mal, wenn du ein Zeichen empfangen hast, dieses 
verarbeiten musst, ist halt so. Das ist eine Eigenschaft deines Systems.

von Peter II (Gast)


Lesenswert?

Draco schrieb:
> Wie kann ich dies besser lösen?!

float sind doch nur 4byte. Die sollte auch zwischen PC und µC gleich 
sein.

in c kannst du die 4byte einfach per memcopy in ein float kopieren.
1
uint8_t[4] temp;
2
float val;
3
memcpy( &float, tmp, 4 );

in C# kannst du die mit BitConverter.GetBytes(floats); arbeiten.

von Loddar (Gast)


Lesenswert?

Peter II schrieb:
> Die sollte auch zwischen PC und µC gleich
> sein.

das ist nur was für Mutige

von Peter II (Gast)


Lesenswert?

Loddar schrieb:
> das ist nur was für Mutige

warum?

https://de.wikipedia.org/wiki/IEEE_754

welcher PC oder µC macht das anders?

von Peter D. (peda)


Lesenswert?

Peter II schrieb:
> float sind doch nur 4byte. Die sollte auch zwischen PC und µC gleich
> sein.

Hüstel.
Es gibt mindestens 4 gebräuchliche Permutationen der 4 Bytes eines float 
oder long bzw. 2 bei short.

Eine praktischere Lösung wäre, wenn PC und MC in Network Byte Order 
konvertieren könnten.

Wenn es aber eh nur ints sind, dann einfach auf dem PC die Kommastellen 
abschneiden. Dann muß der MC nicht die float Lib dazulinken.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Peter D. schrieb:
> Hüstel.
> Es gibt mindestens 4 gebräuchliche Permutationen der 4 Bytes eines float
> oder long bzw. 2 bei short.

warum sollte 4 Byte durcheinander kommen? Es sind einfach 4 Bytes 0-3 
ferig.

Bei einen "Text" kommt doch auch nicht mal "xteT" raus.

von Peter II (Gast)


Lesenswert?

Peter D. schrieb:
> Eine praktischere Lösung wäre, wenn PC und MC in Network Byte Order
> konvertieren könnten.

wozu? float ist gleich auf beiden Seiten gleich - die Byteorder ist für 
int werte.

von Loddar (Gast)


Lesenswert?

Peter II schrieb:
> Loddar schrieb:
>> das ist nur was für Mutige
>
> warum?
>
> https://de.wikipedia.org/wiki/IEEE_754
>
> welcher PC oder µC macht das anders?

siehe dazu Zitat aus deinem Link:
Die bei einer Rechenanlage konkrete Anordnung der Bits im Speicher kann 
von diesem Bild abweichen und hängt von der jeweiligen Bytereihenfolge 
(little/big endian) und weiteren Rechnereigenheiten ab.

von Peter II (Gast)


Lesenswert?

Loddar schrieb:
> Die bei einer Rechenanlage konkrete Anordnung der Bits im Speicher kann
> von diesem Bild abweichen und hängt von der jeweiligen Bytereihenfolge
> (little/big endian) und weiteren Rechnereigenheiten ab.

und wie ist sie auf einen tiny? Und wie ist sie auf einem PC?

Man darf dort bei seinen eigenem Protokoll übertragen wie man will. Man 
muss auf niemand Rücksicht nehmen. Wenn sie wirklich verschieden ist, 
kann man dem PC es zumuten es zu drehen, damit es für den µC passt.

von Rolf M. (rmagnus)


Lesenswert?

Loddar schrieb:
> Peter II schrieb:
>> Die sollte auch zwischen PC und µC gleich
>> sein.
>
> das ist nur was für Mutige

Oder für welche, die einfach wissen, dass es gleich ist, weil sie es 
nachgelesen haben.

Peter D. schrieb:
> Peter II schrieb:
>> float sind doch nur 4byte. Die sollte auch zwischen PC und µC gleich
>> sein.
>
> Hüstel.
> Es gibt mindestens 4 gebräuchliche Permutationen der 4 Bytes eines float
> oder long bzw. 2 bei short.

Und die hast du alle auf dem hier diskutierten tiny2313 schon erlebt?

> Eine praktischere Lösung wäre, wenn PC und MC in Network Byte Order
> konvertieren könnten.

Ich würde eher die Byteorder des Systems nehmen, bei dem der Aufwand zum 
rumdrehen am teuersten ist.
"Network Byteorder" ist der Begriff für die Reihenfolge, die IP-Stacks 
für ihre Protokollheader verwenden. Welchen Vorteil soll die denn auf 
einer RS232 haben?

von Peter D. (peda)


Lesenswert?

Rolf M. schrieb:
> "Network Byteorder" ist der Begriff für die Reihenfolge, die IP-Stacks
> für ihre Protokollheader verwenden. Welchen Vorteil soll die denn auf
> einer RS232 haben?

Das wäre ein Standard, der die Reihenfolge festlegt, d.h. damit 
funktioniert es garantiert.
Ansonsten muß man prüfen, wie es die verwendeten CPUs und Compiler 
halten. Es kann rein zufällig passen, muß aber nicht.

von Rolf M. (rmagnus)


Lesenswert?

Peter D. schrieb:
> Das wäre ein Standard, der die Reihenfolge festlegt, d.h. damit
> funktioniert es garantiert.
> Ansonsten muß man prüfen, wie es die verwendeten CPUs und Compiler
> halten.

Das muss man sowieso. Schließlich brauchst du ja erstmal eine Funktion, 
um in deine network byteoder zu konvertieren. Auf dem PC kann man da 
noch die Funktionen des IP-Networking dafür missbrauchen, solange man 
nicht mehr als 32 Bit benötigt, aber auf dem µC hat man die meist nicht.

von Walter S. (avatar)


Lesenswert?

Rolf M. schrieb:
>> das ist nur was für Mutige
>
> Oder für welche, die einfach wissen, dass es gleich ist, weil sie es
> nachgelesen haben.

ich kann natürlich nachschauen wie es heute ist, bei den verwendeten 
Compilern und bei dem verwendeten PC ist und dann per quick and dirty 
was reinhacken. Das kann sich aber auch schnell ändern.

Als Bastler kann man das machen. Wenn man dann auf ein anderes System 
umsteigen muss macht man es einfach neu, ist ja schließlich Hobby.

von Peter D. (peda)


Lesenswert?

Ich weiß ja nicht, ob union aus float und long definiert ist, dann 
könnte man die 4 Bytes auf beiden Maschinen durch Schieben extrahieren 
und zusammensetzen.
Allerdings wie gesagt, aufm ATTiny2313 wirds recht eng mit der 
float-LIb.

von Draco (Gast)


Lesenswert?

Sooo... also langsam bin ich echt am Verzweifeln hier. Ich kann mir auch 
keinen Rat mehr geben.

Ich übergebe nun die "Zahl" als String Quasi "312"... lese diese mit dem 
Interrupt Vektor für die UART ein und übergebe sie an:
1
volatile char dataUART;
2
3
ISR(USART_RX_vect)
4
{
5
  char data = UDR;
6
  dataUART = data;
7
}

Das klappt auch, aber ich bekomme diesen verdammten String nicht zu 
einer Int16 gewandelt. Mit z.b. atoi bekomme ich nur Fehlermeldungen um 
die Ohren gehauen weil es ja nicht const char ist:
1
int main(void) {
2
  
3
  inituart();
4
    sei();
5
  
6
  uint16_t Rechner = 0;
7
8
  while(1) {
9
    
10
    Rechner = atoi(dataUART);
11
    
12
    if(Rechner >= 80)
13
    {
14
      transmitbyte(dataUART);
15
    }  else {
16
      transmitbyte('o');
17
    }
18
    _delay_ms(100);
19
  }
20
  return 0;
21
}


Sehe ich da den Wald vor lauter Bäumen nicht mehr?!
1
expected 'const char *' but argument is of type 'char'
2
passing argument 1 of 'atoi' makes pointer from integer without a cast

von Codix (Gast)


Lesenswert?

Draco schrieb:
> expected 'const char *' but argument is of type 'char'

atoi will einen Pointer auf einen String! Dein dataUART ist nur ein 
char,
das ist das Problem!

Siehe:
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html

von Draco (Gast)


Lesenswert?

Hmm... aber selbst einen Pointer mag er nicht. Ich glaub ich geh lieber 
ins Bett :D
1
int main(void) {
2
  
3
  inituart();
4
    sei();
5
  
6
  uint16_t Rechner = 0;
7
  char *ptr;
8
  
9
  while(1) {
10
    
11
    ptr = dataUART;
12
13
    Rechner = atoi(*ptr);
14
    
15
    if(Rechner >= 80)
16
    {
17
      transmitbyte(dataUART);
18
    }  else {
19
      transmitbyte('o');
20
    }
21
    _delay_ms(100);
22
  }
23
  return 0;
24
}

von Walter S. (avatar)


Lesenswert?

Draco schrieb:
> Ich glaub ich geh lieber
> ins Bett :D

und lies ein gutes C-Buch ;-)

von Draco (Gast)


Lesenswert?

Sooo... fertsch. Ich lasse es mir nun einfach als ByteArray schicken, 
brauche ja sowieso noch ein StatusByte welches dem µC mitteilt um 
welchen Wert es sich überhaupt handelt.

Das ist auf der C# Seite etwas nunja: "umständlich", da man an ein Array 
nicht einfach noch ein Byte "ansetzen" kann, ich aber das StatusByte 
brauche, sowie noch ein NewLine ('\n'). Deswegen musste ich da den Umweg 
über zwei Arrays gehen:
1
byte[] ListTemp = new byte[5];
2
byte[] byteArray = BitConverter.GetBytes(Convert.ToUInt16(BitConverter.ToSingle(OutBytes, 148) * 10));
3
byte[] newline = Encoding.ASCII.GetBytes(Environment.NewLine);
4
5
ListTemp[0] = 0x01;
6
ListTemp[1] = byteArray[0];
7
ListTemp[2] = byteArray[1];
8
ListTemp[3] = newline[0];
9
10
ComPort.Write(ListTemp, 0, 3);

Einmal dann zum µC geschickt kann ich da einfach die Bytes shiften und 
hab mein uint10_t:
1
dataUART = uart_string[1] + (uart_string[2]<<8);

Somit ist zwar der Rechner etwas belastet aber ich bekomme den ganzen 
Klateratatsch vom Tiny weg.

von Wolfgang (Gast)


Lesenswert?

Draco schrieb:
> ... ich aber das StatusByte brauche, sowie noch ein NewLine ('\n').

Damit handelst du dir einen überflüssigen Overhead von 15 Bit. Bei 13,3 
Bit Nutzdaten ist das schon eine ganze Menge, wenn man daraus keinen 
weiteren Gewinn zieht.
Es würden locker reichen, 3 Byte zu übertragen, in denen jeweils ein Bit 
als Synchronisationsinformation fungiert. Dann hättest du noch 21 Bit, 
i.e. 14 Bit für die Daten (0..10000) und 7 freie Bit zur beliebigen 
Verwendung.

von Draco (Gast)


Lesenswert?

Wolfgang schrieb:
> Damit handelst du dir einen überflüssigen Overhead von 15 Bit. Bei 13,3
> Bit Nutzdaten ist das schon eine ganze Menge, wenn man daraus keinen
> weiteren Gewinn zieht.
> Es würden locker reichen, 3 Byte zu übertragen, in denen jeweils ein Bit
> als Synchronisationsinformation fungiert. Dann hättest du noch 21 Bit,
> i.e. 14 Bit für die Daten (0..10000) und 7 freie Bit zur beliebigen
> Verwendung.

Die Idee ist nicht verkehrt. Da ich weiß, das die Daten ja eh nie in 
solche höhen gehen, kann ich ja zweiten Byte, welches die Integer schon 
belegt, 3Bit als Statusbits zu "missbrauchen" und dann im Tiny einfach 
die drei Bits wieder mit nullen füllen. Die Idee werde ich mal umsetzen, 
danke.

Und wenn ich gerade so meinen letzten Post gesehen habe, sehe ich auch 
gerade das ich den NewLine garnicht mitsende, sonder ja bloß die ersten 
3Byte aus ListTemp. Da kann ich das ja quasi auch weglassen, muß ich mal 
probieren wie das unter Stress aussieht.

von Rolf M. (rmagnus)


Lesenswert?

Draco schrieb:
> Hmm... aber selbst einen Pointer mag er nicht.

Der mag den Pointer deshalb nicht, weil du ihn nicht übergibst. Du 
übergibst weiterhin einen char, nur diesmal noch falscher.

> int main(void) {
>
>   inituart();
>     sei();
>
>   uint16_t Rechner = 0;
>   char *ptr;

Hier definierst du einen Zeiger.

>   while(1) {
>
>     ptr = dataUART;

Hier interpretierst du das vom UART empfangene Byte als Adresse und 
schreibst diese in den Pointer. Wenn also vom UART z.B. 0x57 kommt, 
zeigt der Zeiger nun auf die Speicher-Adresse 0x57.

>     Rechner = atoi(*ptr);

Hier dereferenzierst du nun den Pointer, liest also das Byte aus, was an 
der Adresse im Speicher steht, auf die er gerade zeigt, also in unserem 
obigen Beispiel die Speicherstelle 0x57. Sagen wir mal, da steht im 
Speicher 0x20 drin.
Das übergibst du an atoi, welches dieses Byte wiederum als Adresse 
interpretiert und versucht, dort aus dem Speicher einen String zu lesen.
atoi erwartet also nun an Speicheradresse 0x20 einen String, den es 
umwandeln kann.

Das zeigt (no pun intended), dass du noch nicht ansatzweise verstehst, 
wie Zeiger funktionieren. So wirst du damit aber kein funktionierendes 
Programm zustande bekommen. Also solltest du dich erstmal mit den 
Grundlagen vertraut machen.

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.