Forum: Compiler & IDEs Unterschiedliche Architekturen und Structe


von Ulrich P. (uprinz)


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

von Klaus W. (mfgkw)


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.

von Matthias L. (Gast)


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

von Simon K. (simon) Benutzerseite


Lesenswert?

1
__attribute__((packed))
Wie schon oben angedeutet (beim GCC).

Zu dem LSB/MSB Problem finde ich die Variante mit den 
Konvertierungsfunktionen nicht schlecht. Du kannst zum Beispiel einfach 
sagen, dass deine Schnittstelle die Daten immer als Little Endian 
schickt. Die PC Software muss dann entsprechend sich anpassen.

von Klaus W. (mfgkw)


Lesenswert?

andersrum, PC ist (dank Intel) little endian

von Simon K. (simon) Benutzerseite


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.

von Klaus W. (mfgkw)


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

von (prx) A. K. (prx)


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.

von Simon K. (simon) Benutzerseite


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.

von Johann L. (gjlayde) Benutzerseite


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

von Simon K. (simon) Benutzerseite


Lesenswert?

Nett! Kannte ich noch gar nicht. Für 16 Bit ist aber nichts da, kann das 
sein?

von Ulrich P. (uprinz)


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

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.