Forum: Mikrocontroller und Digitale Elektronik Suche Lösung für Syntax Unterschiede zwischen Compiler


von Manuel (Gast)


Lesenswert?

Hallo

Ich habe ein MSP430 und ein PIC24 die ich über UART miteinander 
verbunden habe. Die Daten die ich senden will habe ich in Strukturen 
angelegt die ich in einem H-File definiert habe. Dieses H-File will ich 
in beiden Codes verwenden. Leider legen die beiden Compiler (IAR, C30) 
die Strukturen unterschiedlich im Speicher ab. Daher ist es nötig die 
Strukturen als "pack(1)" oder "packed" zu definieren. Und da fängt mein 
Problem an. Die Compiler habe unterschiedliche Syntax.

Beispiel PIC24 (C30):
1
//---------------------------------------------------------
2
//! Definition Telegramm
3
typedef struct __attribute__ ((__packed__)) COM_TEL_Test1_s
4
  {
5
  uint8_t geraeteTyp;
6
  uint8_t telTyp;
7
  uint8_t length;
8
  uint32_t id;
9
  uint8_t counter;
10
  uint16_t swVersion;
11
  } COM_TEL_Test1_t;     
12
//----------------------------------------------------------

Beispiel MSP430 (IAR)
1
//---------------------------------------------------------
2
//! Definition Telegramm
3
#pragma pack(1)
4
typedef struct COM_TEL_Test1_s
5
  {
6
  uint8_t geraeteTyp;
7
  uint8_t telTyp;
8
  uint8_t length;
9
  uint32_t id;
10
  uint8_t counter;
11
  uint16_t swVersion;
12
  } COM_TEL_Test1_t;
13
#pragma pack()
14
//----------------------------------------------------------

Meine Lösung "bis jetzt" ist ein Compiler Switch zu nutzen:
1
//---------------------------------------------------------
2
//! Definition Telegramm
3
#if defined(__ICC430__)
4
  #pragma pack(1)
5
  typedef struct COM_TEL_Test1_s
6
#elif defined(__C30__)
7
  typedef struct __attribute__ ((__packed__)) COM_TEL_Test1_s
8
#else
9
  #error 'Compiler ???'
10
#endif
11
  {
12
  uint8_t geraeteTyp;
13
  uint8_t telTyp;
14
  uint8_t length;
15
  uint32_t id;
16
  uint8_t counter;
17
  uint16_t swVersion;
18
  } COM_TEL_Test1_t;
19
#if defined(__ICC430__)
20
  #pragma pack()
21
#endif
22
//----------------------------------------------------------

Diese Lösung funktioniert aber sie ist mühsam weil der Compiler Switch 
bei jeder Strukturen eingefügt werden muss. Meine ursprüngliche Idee war 
ein Makro zu definieren habe aber Probleme mit den # in dem Makro.
// define PACK_S  #if defined(_ICC430_) #pragma pack(1) ...

Kennt jemand von euch eine bessere Lösung?

Gruss Manuel

von Stephan (Gast)


Lesenswert?

Morgen,

schau dir mal die Lösungen von veröffentlichten Projekten wie "lwIp" 
usw. an.
die machen das ungefähr so:

// im config Headerfile
// (config der CPU und des Compilers)
// ka für was das Beispiel ist!!!
1
/* Compiler hints for packing structures */
2
#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
3
#define PACK_STRUCT_STRUCT __attribute__((packed))
4
#define PACK_STRUCT_BEGIN
5
#define PACK_STRUCT_END

und dann im allg. Programm Headerfile,
steht dann folgendes:
1
PACK_STRUCT_BEGIN
2
struct ip_hdr {
3
  /* version / header length / type of service */
4
  PACK_STRUCT_FIELD(u16_t _v_hl_tos);
5
  /* total length */
6
  PACK_STRUCT_FIELD(u16_t _len);
7
  /* identification */
8
  PACK_STRUCT_FIELD(u16_t _id);
9
  /* fragment offset field */
10
  PACK_STRUCT_FIELD(u16_t _offset);
11
  /* time to live */
12
  PACK_STRUCT_FIELD(u8_t _ttl);
13
  /* protocol*/
14
  PACK_STRUCT_FIELD(u8_t _proto);
15
  /* checksum */
16
  PACK_STRUCT_FIELD(u16_t _chksum);
17
  /* source and destination IP addresses */
18
  PACK_STRUCT_FIELD(ip_addr_p_t src);
19
  PACK_STRUCT_FIELD(ip_addr_p_t dest); 
20
} PACK_STRUCT_STRUCT;
21
PACK_STRUCT_END

vielleicht gefällt dir das besser.

mfg
Stephan

von (prx) A. K. (prx)


Lesenswert?

Stephan schrieb:

> #define PACK_STRUCT_FIELD(x) x __attribute__((packed))
> #define PACK_STRUCT_STRUCT __attribute__((packed))

Nur scheint, dem obigen Text nach zu schliessen, der IAR MSP430 Compiler 
ebendiese GCC-spezifische Attributsyntax nicht zu goutieren. Und man 
kann, wie Manuel ebenfalls feststellen musste, in Präprozessormakros 
auch keine keine Präprozessoranweisungen verbuddeln.

Man erkennt hier, weshalb die GCC Autoren es vorziehen, für solche 
Konstrukte keine Präprozessor-Syntax zu nutzen - die dem 
ursprünglichen Konzept eines dem Compiler vorgeschalteten 
Text-Präprozessors ohnehin eklatant widerspricht.

von STK500-Besitzer (Gast)


Lesenswert?

Ich würde an anderer Stelle ansetzen:
Bei der Datenübertragung.
Die Übertragungsfunktion zerlegt die Struct in ihre Einzelteile und 
überträgt sie mit einem Protkoll, das beide Controller verstehen.
Fertig ist die Laube.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

STK500-Besitzer schrieb:

> Die Übertragungsfunktion zerlegt die Struct in ihre Einzelteile und
> überträgt sie mit einem Protkoll, das beide Controller verstehen.

Das sehe ich genauso. Wenn der nächste Controller dann auch noch einen 
anderen Endian hat, klappt da gar nichts mehr.

Die Schnittstellen müssen halt passen. Wie der einzelne Controller 
intern die Daten speichert, kann einem schnurzegal sein.

> Fertig ist die Laube.

Eben.

Gruß,

Frank

von Stephan (Gast)


Lesenswert?

Hi,

Manuel:
>Diese Lösung funktioniert aber sie ist mühsam weil der Compiler Switch
>bei jeder Strukturen eingefügt werden muss.
Es scheint doch zu funktionieren.
womit er Probleme hat war dies:

>// define PACK_S  #if defined(ICC430) #pragma pack(1) ...
und sowas hab ich noch nie gesehen!!!
Deshalb auch der Vorschlag, so wie es andere schon hinbekommen haben.

Frank M.:
>Die Schnittstellen müssen halt passen. Wie der einzelne Controller
>intern die Daten speichert, kann einem schnurzegal sein.

ok. dann braucht Manuel für den PIC und den MSP jeweils eine eigene
Sende- und Empfangsroutine!
Sollte bei einem gut organisierten Projekt auch kein Problem sein.

mfg
Stephan

von STK500-Besitzer (Gast)


Lesenswert?

>ok. dann braucht Manuel für den PIC und den MSP jeweils eine eigene
>Sende- und Empfangsroutine!
>Sollte bei einem gut organisierten Projekt auch kein Problem sein.

Ich bezweifle, dass er für beide Controller exakt die gleichen Routinen 
verwendet. Es sind ja völlig verschiedene Controller.
Irgendwann ist die Portierbarkeit nicht mehr gegeben.

von (prx) A. K. (prx)


Lesenswert?

Warum sollte der Controller bei der Umsetzung der Datenformate eine 
entscheidende Rolle spielen? Die Datentypen in stdint.h sind klar 
definiert, die Arbeitsweise von C Funktionen auch.

Lediglich der exakte Transport über irgendwelche I/O-Register oder 
DMA-Puffer oder was auch immer ist verschieden. Aber das geschieht, wenn 
man das Software-Design richtig macht, einen Layer darunter. D.h. bei 
der Portierung wechselt man den I/O-Layer (quasi ein Device Driver) und 
behält den Rest.

von Peter (Gast)


Lesenswert?

A. K. schrieb:
> arum sollte der Controller bei der Umsetzung der Datenformate eine
> entscheidende Rolle spielen? Die Datentypen in stdint.h sind klar
> definiert, die Arbeitsweise von C Funktionen auch.

nein ist es nicht

http://de.wikipedia.org/wiki/Byte-Reihenfolge

von (prx) A. K. (prx)


Lesenswert?

Peter schrieb:

> nein ist es nicht
> http://de.wikipedia.org/wiki/Byte-Reihenfolge

Dieses Thema wurde oben bereits von Frank erwähnt. Auch das ist eine 
Frage des Designs. Man kann die Datenübertragungskonvention so 
definieren, dass sie von der Bytereihenfolge der real verwendeten 
Maschinen unabhängig ist. Für die reale Umsetzung stehen beispielsweise 
im TCP/IP-Umfeld Funktionen wie htons/ntohs zur Verfügung, die je nach 
Byteoder die Bytes umdrehen oder einfach nur durchreichen.

von STK500-Besitzer (Gast)


Lesenswert?

>Warum sollte der Controller bei der Umsetzung der Datenformate eine
>entscheidende Rolle spielen? Die Datentypen in stdint.h sind klar
>definiert, die Arbeitsweise von C Funktionen auch.

Nur wirst du nicht einfach eine struct problemlos per fprintf o.dergl. 
zwischen zwei Controllern übertragen können. Edians sind da nur das eine 
Problem - wie der Compiler die struct zusammensetzt ein anderes.
Würde ich zumindest sagen.

von Peter (Gast)


Lesenswert?

A. K. schrieb:
> Für die reale Umsetzung stehen beispielsweise
> im TCP/IP-Umfeld Funktionen wie htons/ntohs zur Verfügung, die je nach
> Byteoder die Bytes umdrehen oder einfach nur durchreichen.

und wie willst du die funktionen mit einen struct aufrufen? Es geht ja 
gerade darum das es keinen sinn macht einen struct als einheit zu 
übertragen. Sondern jedes element für sich und dabei kann man dann auch 
gleich die htons/ntohs funktionen aufrufen.

von (prx) A. K. (prx)


Lesenswert?

Eben. Deshalb sollte nicht einfach den Speicherbereich einer struct so 
wie sie ist in den Tramnsportlayer schieben. Da bei befindet man sich 
auf recht glattem Eis und muss mit solchen compilerabhängigen Hacks wie 
gepackten Strukturen arbeiten. Wenn man, wie bereits vorgeschlagen, 
einen passenden Datenformatlayer einschiebt, dann ist Ruhe. Und der 
lässt sich, richtig gemacht, auch wiederum weitgehend portabel 
gestalten.

von STK500-Besitzer (Gast)


Lesenswert?

>Deshalb sollte nicht einfach den Speicherbereich einer struct so
>wie sie ist in den Tramnsportlayer schieben.

Der eine macht es über eine Transportlayer, der ander über eine 
angepasste Tansportfunktion.
Dann sind wir uns ja einig.

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.