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
typedefstruct{
2
uint8_tstatus;/* Status of fan */
3
int32_tesum;/* PI esum */
4
uint16_tpwm;/* PWM factor [0..255] */
5
uint16_trpm;/* RPM [1/min] */
6
uint16_tspeed;/* actual speed of fan */
7
uint8_tman;/* Manual Setpoint [0..100%] */
8
uint8_tdown;/* Countdown for error */
9
uint8_ttimer;/* Timer for fan stop */
10
}fan_t;
11
12
fan_tfanvar[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
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
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.
>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..
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.
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.
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.)
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.
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.
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
Feine Diskussion mit vielen Ideen. Danke!
Folgendes hat mein Problem gelöst:
1
typedefstruct__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