Unterschiedliche Architekturen und Structe

OP (Firma: DC3AX) #1277775
Lesenswert?

Hallo!

Ich habe einen AVR Microcontroller, der etwas steuert und die dafür 
nötigen Laufzeitvariablen in einem Struct vorhält. Daneben hat er auch 
eine USB Schnittstelle und über diese kann eine Linux oder Windows 
Software die Laufzeitvariablen abfragen. Dazu sende ich das ganze Struct 
einfach als Block über den USB an den PC und empfange es dort. 
Ameinfachsten wäre es natürlich gleich das so zu gestalten:
1
typedef struct {
2
  uint8_t  status;  /* Status of fan */
3
  int32_t  esum;    /* PI esum */
4
  uint16_t pwm;    /* PWM factor [0..255] */
5
  uint16_t rpm;    /* RPM [1/min] */
6
  uint16_t speed;    /* actual speed of fan */
7
  uint8_t  man;    /* Manual Setpoint [0..100%] */
8
  uint8_t  down;    /* Countdown for error */
9
  uint8_t  timer;    /* Timer for fan stop */
10
} fan_t;
11

12
fan_t fanvar[3];  // es gibt 3 Lüfter
13
usb_send_buffer( (U8*)fanvar, sizeof( fanvar));

Der PC hat nun das identische Struct und liest dieses über
1
fan_t fans[3];
2

3
ret = usb_bulk_read( udev, IN_ENDPOINT, (char*)&fans, sizeof(fans), TIMEOUT);
ein.

Leider scheitert die Einfachheit an zwei Dingen.

1. Die Variablen scheinen unterschiedlich im MSB/LSB Ordering zu sein.
2. Ein PC hat andere Vorstellungen von der Länge eines bytes, als ein 
AVR.

1) lässt sich mit MSB/LSB defines eventuell noch ganz brauchbar lösen.
2) Hier ist mein Problem. Wärend der AVR bei einem sizeof( fanvar) 14 
zurück gibt, sind es beimi PC 42... Wenn ich mir jedoch mal die 
verschiedenen sizeof(uint8_t) oder sizeof(uint16_t) ausgeben lasse, so 
stimmen diese mit 1 resprektive 2 sauber mit dem vom AVR überein. Also 
scheint der gcc diese bei der Ablage im struct zu optimieren oder er 
füllt irgendwo etwas auf. Wenn er mit 32 Bit Breite ablegen würde, 
müsste aber schon das struct fan_t alleine 64 Bytes belegen. Ein 
sizeof(fans) ergibt aber 60 Bytes Größe, also 20 Bytes für das struct ( 
welches ich drei mal brauche).

Wie kann ich auf der PC Seite den gcc dazu überreden, das Struct 
byte-aligned abzulegen? Leider führt ein -fpack-struct zu einem 
unerwünschten Nebenefekt: libusb stürzt mit einem Segmentation fault ab.
Kann man das Packen eines Structs im Code angeben, also conditional?

Gibt es elegante Funktionen um das LSB/MSB Problem zu lösen?

Danke schon mal für Tips, Links ...
Ulrich
#1277830
Lesenswert?

zum Thema packed: 
http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes

Für die LSB/MSB-Geschihcte fällt mir nichts automatisches ein.
Binäre Daten 1:1 schicken geht eh schief.
Zum Konvertieren gibt es Funktionen wie ntohs() etc.; die muß man
aber halt auf jedes betroffene Element anwenden.

Zu Überlegen wäre natürlich, gleich die Struct in einen
Zeichenstrom zu verwandeln und beim Empfänger wieder zurück.
Gast #1277888
Lesenswert?

>Gibt es elegante Funktionen um das LSB/MSB Problem zu lösen?

Naja, was elegantes weiß ich nicht.


>Zu Überlegen wäre natürlich, gleich die Struct in einen
>Zeichenstrom zu verwandeln und beim Empfänger wieder zurück.


Ich habs letztens so genacht, das ich händisch alles in Bytes zerlegt 
habe, und diese in einer (wahllos) festgelegten Reihenfolge als 
Bytestrom versendet. Beim Empfänger hab ich das dann händisch wieder 
zusammengebaut..
Persönliche Seite #1277978
Lesenswert?

Klaus Wachtler schrieb:
> andersrum, PC ist (dank Intel) little endian

Andersrum macht es aber keinen Sinn. Du willst doch nicht dauernd den 
Prozessor umprogrammieren, weil der PC die Endianess der Schnittstelle 
vorgibt.

Deswegen sag ich ja, dass man seine Schnittstelle als zB Little Endian 
definiert (was der AVR-GCC bei seinen Variablen übrigens auch macht). 
Dann kann man in die PC Software nen Check einbauen.
#1278016
Lesenswert?

ich sagte gar nichts darüber, wo konvertiert werden soll.
Sinnvoll fände ich es auch auf der PC-Seite.

Nur: PCs mit Intel- und AMD-Prozessoren sind LE.
Wenn LE übertragen wird, gibt es auf der PC-Seite nichts zu 
konvertieren.
Bzw. muß man BE übertragen, um am PC zu konvertieren.

---

Wobei ich jetzt irgendwie ins Schleudern komme...
Denken AVRs nicht auch LE? Dann dürfte doch gar nichts zu konvertieren
sein, wenn man zwischen AVR und PC überträgt?

(Oder es ist mir heute schon zu spät; irgendwie ist es nicht mein Tag.)
#1278034
Lesenswert?

Da sowohl AVR als auch PC little endian sind, ist dieser Teil kein 
Problem und umzudrehen gibt es nichts.

Was nicht zusammenpasst ist die Justierung auf die dem Typ entsprechende 
Adressierung. Beim AVR passiert da nichts, aber beim PC werden 
beispielsweise zwischen "status" und "esum" 3 Füllbytes eingefügt.

Man kann einerseits dem jeweiligen Compiler beibringen, darauf zu 
verzichten (kostet beim PC etwas Laufzeit), was bei jedem anders geht. 
Oder das gleich im Layout der Struct durch geschickte Anordnung der 
Elemente berücksichtigen.

Der einfachste Weg das zu erreichen: Erst kommen alle 32bit Daten, dann 
alle 16bit Daten, dann alle 8bit Daten. Man muss dann nur noch darauf 
achten, dass man bei sizeof() trotzdem verschiedene Werte erhalten kann, 
wenn der PC aufrundet aber der AVR nicht.

Ein globales Packen der Structs auf dem PC gibt oft Ärger, weil die 
Structs anderer Bibliotheken dann ggf. falsch verarbeitet werden. Hast 
du ja gemerkt. Das darf nur als Attribut der jeweiligen Struct 
hinzugefügt werden. Wie das geht verrät dir das Handbuch des Compilers.
Persönliche Seite #1278065
Lesenswert?

Klaus Wachtler schrieb:
> Wobei ich jetzt irgendwie ins Schleudern komme...
> Denken AVRs nicht auch LE? Dann dürfte doch gar nichts zu konvertieren
> sein, wenn man zwischen AVR und PC überträgt?
>
> (Oder es ist mir heute schon zu spät; irgendwie ist es nicht mein Tag.)

Nein ist richtig, hab ich ja oben auch schon geschrieben. Der AVR-GCC 
passt sich der LEess vom AVR ja auch an im Speicher.

Zurzeit habe ich auch sowas am laufen. Einfach nur die Struktur gepackt 
und per UART rübergeschickt, dann kann mans auf dem PC wieder einfach 
entknöseln. Werde aber das ganze noch durch Makros abstrahieren, die, 
falls nötig, die Endianess wechseln.
Persönliche Seite #1278159
Lesenswert?

Simon K. schrieb:

> Zurzeit habe ich auch sowas am laufen. Einfach nur die Struktur gepackt
> und per UART rübergeschickt, dann kann mans auf dem PC wieder einfach
> entknöseln. Werde aber das ganze noch durch Makros abstrahieren, die,
> falls nötig, die Endianess wechseln.

Die Byte- und Word-Endianess der Plattform kann man recht einfach zur 
Laufzeit abtesten. Zum Rumwirbeln der Bytes gibt's im GCC 
__builtin_bswap32/64.

http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/Other-Builtins.html#index-g_t_005f_005fbuiltin_005fbswap32-2805

Johann
OP (Firma: DC3AX) #1278421
Lesenswert?

Feine Diskussion mit vielen Ideen. Danke!

Folgendes hat mein Problem gelöst:
1
typedef struct __attribute__((packed))
2
{ 
3
...
4
} fan_t;

Der AVR und der (Linux)PC haben die gleiche Endianess, wie oben schon 
erwähnt. Drehen/Swappen von Daten ist also zuerst einmal nicht nötig.

Vielen Dank!

Gruß, Ulrich

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren