Forum: Compiler & IDEs Code für unterschiedliche Compiler


von Swen (Gast)


Lesenswert?

Hallo zusammen,

bisher habe ich nur mit dem Atmel Studio gearbeitet.

Jetzt beschäftige ich mich mit IAR. Es wird demnächst beides parallel 
zum Einnsatz kommen.

Ich hatte mir nun vorgestellt, die zukünftigen Projekte so anzulegen, 
dass sie mit beiden Entwicklungsumgebungen bearbeitet werden können.

Um erst einmal zu unterscheiden, welcher Compiler zum Einsatz kommt, 
wollte ich folgendes verwenden:
1
#if defined (__GNUC__) && defined (__AVR__)
2
  /* Mache was speziell fuer avr-gcc */
3
  #   include <avr/io.h>
4
#elif defined (__ICCAVR__)
5
  /* IAR Compile is used */
6
  #  inclide <avrio.h>
7
#else
8
  # error "Unknown compiler"
9
#endif /* aiccavr */

Für die anderen Unterschiede der Compiler wollte ich dann entsprechend 
Makros über die obige Weiche erstellen.

Bevor ich jetzt direkt an die Umsetzung einer solchen Compiler-Weiche 
gehe, wollte ich mich erst einmal noch etwas schlau machen, ob das in 
der Art, wie ich es vor habe, sonnvoll ist? Ob es andere/bessere 
Möglichkeiten gibt?

Hat hier jemand schon etwas ähnliches umgesetzt oder sollte man das 
besser sein lassen und das Projekt dann nicht für beide Compiler 
erstellen?

Vielleicht hat der ein oder andere ja schon Erfahrung damit und teilt 
sie mit uns.

Beste Grüße
Swen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Swen schrieb:
> Bevor ich jetzt direkt an die Umsetzung einer solchen Compiler-Weiche
> gehe, wollte ich mich erst einmal noch etwas schlau machen, ob das in
> der Art, wie ich es vor habe, sonnvoll ist?

Ja, ist sinnvoll, dies genau so zu machen. Aber bevor Du diese #ifdefs 
nun quer über den Quellcode verstreust, kann es sinnvoll sein, ganz oben 
(oder in include-Dateien) gleichnamige Makros für jeden Fall zu 
definieren, die Du dann im Quellcode nutzt. Dadurch bleibt der 
eigentliche Code dann lesbar.

Also:
1
#if defined (__GNUC__) && defined (__AVR__)
2
#define FOO() bar()
3
#elif defined (__ICCAVR__)
4
#define FOO() bar_anders()
5
#else
6
  # error "Unknown compiler"
7
#endif
8
9
...
10
main ()
11
{
12
  ...
13
  FOO ();
14
  ...
15
}

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Ob das überhaupt sinnvoll ist, sei mal dahingestellt. Da gibt es schon 
an einige Ecken Unterschiede, die sich nicht mit ein paar globalen 
defines erschlagen lassen. Das wird einen arg vermurksten Quelltext 
ergeben, oder du schreibst einiges vom Code in zwei Versionen.

Oliver

von Swen (Gast)


Lesenswert?

Hallo,

vielen Dank für die Antwort bis hierher.

Ich werde mich einmal in das Thema einarbeiten und mal versuchen ein 
Projekt so aufzubauen, dass es eben in beiden Entwicklungsumgebungen 
nutzbar ist.

Im Laufe der Arbeit werden sich dann sicher noch mehr Unterschiede 
heraustellen. Dann wird sich zeigen, wie praktikabel das sein wird.

Vielen Dank.

Beste Grüße
Swen

von Eric B. (beric)


Lesenswert?

Im AutoSAR wird eine ganze Reihe an Macros definiert um Quellcode so 
weit wie es geht Compilerunabhängig zu machen. Das liefert dann aber 
schon Code, der ziemlich vom "gewöhnten" C abweicht . Sieht etwa so aus:
1
FUNC(uint8_t, CODE) u8Foobar (
2
  CONST(bool_t, AUTOMATIC) bJaNein,
3
  P2VAR(uint8_t, AUTOMATIC, DATA) pu8SomePointer
4
)
5
{
6
  VAR(uint16_t, AUTOMATIC) u8SomeVariable;
7
8
  ...
9
}
Riesennachteil ist, dass IDEs bei solchem Code komplett den Faden 
verlieren und du auf Bequemlichkeiten wie Syntax-Highlighting, 
Auto-Completion usw möglicherweise verzichten musst.

Ob das aber ist was du anstreben möchtest...

Link: 
https://www.autosar.org/fileadmin/files/releases/2-0/software-architecture/implementation-integration/standard/AUTOSAR_SWS_CompilerAbstraction.pdf

von Swen (Gast)


Lesenswert?

Hallo,


ich habe einmal kurz drüber geschaut, sieht ja schon wild aus.

Mal sehen, wo wir landen werden.


Einige Sachen sind schienbar nicht ganz so einfach weitestgehend 
compilerunabhängig zu machen.
Beim EEPROM Schreiben/Lesen schient es schon größere Unterschiede zu 
geben, auf den ersten Blick. Mal schauen, wie wir es umsetzen.


Vielen Dank.

Beste Grüße
Swen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Da gibt es schon an einige Ecken Unterschiede, die sich nicht mit ein
> paar globalen defines erschlagen lassen.

Wir haben beruflich 10 Jahre lang Code geschrieben, der für IAR und
GCC funktioniert (anfangs AVR, später auch ARM).  Kann man alles
über Makros abdecken, allerdings sind diese teilweise dann etwas
komplexer.  Zwischenzeitlich kam sogar noch die Anforderung für
MSP430 mit rein, was die Komplexität weiter gesteigert hat (far
functions und noch ein weiterer Compiler).

von Swen (Gast)


Lesenswert?

Hallo,

wir sind gerade dabei, das ein oder Makro dafür zu erstellen, indem wir 
eines unserer kleineren Projekte einmal so umschreiben möchten, dass es 
eben mit beiden Compilern läuft.

Im Moment schauen wir uns das Thema eeprom an, da in dem Projekt einige 
Daten im EEPROM abgelegt werden. Mal schauen, wie komplex das ist.

Beste Grüße
Swen

von Swen (Gast)


Lesenswert?

Hallo,

vielleicht noch eine Frage zur Protierung der EEPROM-Funktionen auf IAR.

Lassen sich auch die EEPROM Funktionen wie eeprom_write_byte, 
eeprom_write_word,...usw. auch alle so per  Makro definieren, man dieses 
in beiden Umgebungen verwenden kann?


Im Moment fehlt mir der Ansatz, wie man dieses einfach in Makros 
umsetzen kann, da es diese ganzen oben genannten Funktionen im IAR nicht 
gibt, dort kann ja direkt auf eine mit __eeprom definierte Variable 
zugegriffen werden, wie auf jede andere Variable auch, wenn ich das 
richtig verstanden habe.

Ich sehs noch nicht....

Beste Grüße
Swen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Am besten exerzieren wir das mal mit einem Beispiel durch.

Schreib mal hierhin, wie der Code für avr-gcc und wie er für IAR 
aussehen würde.

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Swen schrieb:
> Lassen sich auch die EEPROM Funktionen wie eeprom_write_byte,
> eeprom_write_word,...usw. auch alle so per  Makro definieren, man dieses
> in beiden Umgebungen verwenden kann?

Der IAR hat meines Wissens nur Funktionen auf Byteniveau (__EEGET()
und __EEPUT()).  Diese kannst du einfach so abstrahieren, dass du
auch ein GCC-Pendant dafür hast.  Wenn du mehr willst, kannst du
natürlich aber auch beim IAR das Assemblieren / Disassemblieren der
Bytes (über Maskierung und Bitschiebeoperation) innerhalb einer
Makro-Ersetzung realisieren.

von Swen (Gast)


Lesenswert?

Hallo,


im AVR-GCC sollte das wie folgt aussehen:
1
uint8_t EEMEM ValueOneEEPROMAddress;
2
3
uint8_t ValueOneFromEEPROM = 0;
4
ValueOneFromEEPROM = eeprom_read_word( &ValueOneEEPROMAddress );

Beim IAR könnte das wie folgt aussehen:
1
__eeprom uint8_t ValueOneEEPROM;
2
uint8_t ValueOneFromEEPROM = 0;
3
4
ValueOneFromEEPROM = ValueOneEEPROM;

IAR ist ungetestet. Bin auch noch nicht sicher, ob das so richtig ist im 
IAR. Ganz so weit bin ich da noch nicht durchgestiegen.

>Der IAR hat meines Wissens nur Funktionen auf Byteniveau (__EEGET()
>und __EEPUT()).
Das hatte ich auch so verstanden. Wobei man doch auch ohne Funtionen auf 
diee EEPROM Variablen zugreifen kann, wie auf jede andere Variable auch, 
wie oben dargestellt?
Das würde dann weiter heißen, dass ich für eeprom_read_word usw. Makros 
oder Funktionen im IAR erstellen müsste, die eben durch 
Bitschiebeoperationen z.B. 32 Bit in 4 Schritten ins EEPROM schreibt 
oder liest?

Beste Grüße
Swen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Ich übersetze das obige mal:
1
#if defined (__GNUC__)
2
#define eeprom8_t                     uint8_t EEMEM
3
#define EEPROM_READ_BYTE(val, eeval)  do { val = eeprom_read_byte(&(eeval)); } while (0)
4
#elif defined (__ICCAVR__)
5
#define eeprom8_t                     __eeprom uint8
6
#define EEPROM_READ_BYTE(val, eeval)  do { val = eeval; } while (0)
7
#else
8
  # error "Unknown compiler"
9
#endif
10
...
11
eeprom8_t ValueOneEEPROMAddress;
12
uint8_t   ValueOneFromEEPROM = 0;
13
ValueOneFromEEPROM = EEPROM_READ_BYTE (ValueOneEEPROMAddress);

Das sollte so von beiden Compilern genommen werden.

P.S. Du hattest oben eeprom_read_word() statt eeprom_read_byte() 
geschrieben, Vorsicht.

> Das würde dann weiter heißen, dass ich für eeprom_read_word usw. Makros
> oder Funktionen im IAR erstellen müsste, die eben durch
> Bitschiebeoperationen z.B. 32 Bit in 4 Schritten ins EEPROM schreibt
> oder liest?

Es gibt auch eeprom_read_block(), damit kannst Du auch 32bit-Variablen 
lesen bzw. wegschreiben. Einfach den Adressoperator wie gehabt benutzen.

Erweitere die Makros für eeprom16_t und eeprom32_t. Bei 16_t dann 
eeprom_read_word() und bei 32_t dann eeprom_read_block() verwenden. Die 
Makros würde ich dann EEPROM_READ_WORD() und EEPROM_READ_LONG() oder 
EEPROM_READ_DWORD() benennen.

: Bearbeitet durch Moderator
von Swen (Gast)


Lesenswert?

Hallo,

>P.S. Du hattest oben eeprom_read_word() statt eeprom_read_byte()
>geschrieben, Vorsicht.

Ah ja... hatte erst uint16_T stehen.

Warum das do while Konstrukt?

Write würde dann wie folgt aussehen:
1
AVR-GCC:
2
#define EEPROM_WRITE_BYTE(add, val)  do { val = eeprom_write_byte(add, val); } while (0);
3
4
#define EEPROM_WRITE_BYTE(add, val)  do { eeval = val; } while (0);
5
6
eeprom8_t ValueOneEEPROMAddress;
7
uint8_t   ValueOneFromEEPROM = 0;
8
uint8_t   ValueOneToEEPROM = 23;
9
EEPROM_WRITE_BYTE (ValueOneEEPROMAddress, ValueOneToEEPROM);

Wäre das dann so richtig?


>ValueOneFromEEPROM = EEPROM_READ_BYTE (ValueOneEEPROMAddress);

Müsste das nicht wie folgt aussehen?
1
EEPROM_READ_BYTE(ValueOneFromEEPROM, ValueOneEEPROMAddress);


Beste Grüße
Swen

von Swen (Gast)


Lesenswert?

Hallo,

Mist.

hier nochmal. Das war ja nix. Total verpeilt.

Nun aber:
1
AVR-GCC:
2
#define EEPROM_WRITE_BYTE(val, eeval)  do { eeprom_write_byte(eeval, val); } while (0);
3
4
IAR:
5
#define EEPROM_WRITE_BYTE(val, eeval)  do { eeval = val; } while (0);
6
7
eeprom8_t ValueOneEEPROMAddress;
8
uint8_t   ValueOneFromEEPROM = 0;
9
uint8_t   ValueOneToEEPROM = 23;
10
EEPROM_WRITE_BYTE (ValueOneToEEPROM, ValueOneEEPROMAddress);

So sollte es stimmen!?

Beste Grüße
Swen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Swen schrieb:
> Warum das do while Konstrukt?

Damit wird das Statement so geklammert, dass solche Konstrtukte wie
1
   if (irgendwas)
2
      MAKROAUFRUF();
3
   else
4
      ANDERERMAKROAUFRUF();

ohne explizite Klammerung mit { ... } noch korrekt umgesetzt werden. 
Gerade dann, wenn im Makro selbst eine if-Abfrage steht, wirds hässlich 
mit einem darunterstehenden else-Statement. Viel Spaß dann noch bei der 
Zuordnung, wo das else denn hingehört ;-)

> Write würde dann wie folgt aussehen:AVR-GCC:
> #define EEPROM_WRITE_BYTE(add, val)  do { val = eeprom_write_byte(add,
> val); } while (0);
>
> #define EEPROM_WRITE_BYTE(add, val)  do { eeval = val; } while (0);

Pardon, ich hatte meinen Beitrag nochmals nachträglich editiert. Das 
Semikolon hinter "while (0)" muss weg. Ebenso sollte man eeval nochmal 
im Makro klammern, damit der Adressoperator auch auf den kompletten 
Ausdruck wirkt. Schau Dir bitte nochmal meine bearbeitete Fassung von 
oben an.

Swen schrieb:
> Nun aber:AVR-GCC:
> #define EEPROM_WRITE_BYTE(val, eeval)  do { eeprom_write_byte(eeval,
> val); } while (0);

Ja, aber auch hier das Semikolon am Zeilenende weg.

>
> IAR:
> #define EEPROM_WRITE_BYTE(val, eeval)  do { eeval = val; } while (0);
>
> eeprom8_t ValueOneEEPROMAddress;
> uint8_t   ValueOneFromEEPROM = 0;
> uint8_t   ValueOneToEEPROM = 23;
> EEPROM_WRITE_BYTE (ValueOneToEEPROM, ValueOneEEPROMAddress);
>
> So sollte es stimmen!?

Sieht fast gut aus! Du hast beim Aufruf nur die Argumente vertauscht:

EEPROM_WRITE_BYTE (ValueOneEEPROMAddress, ValueOneToEEPROM);

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Swen schrieb:
> Warum das do while Konstrukt?

Weil man so einen Makro direkt nach einem "if" benutzen kann:
1
if (foo)
2
  BAR();
3
else
4
  MUMBLE();

Das Semikolon am Ende des Makros ist aber natürlich unsinnig.

Edit: Frank war schneller. ;-)

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Sieht fast gut aus! Du hast beim Aufruf nur die Argumente vertauscht:
>
> EEPROM_WRITE_BYTE (ValueOneEEPROMAddress, ValueOneToEEPROM);

Jedenfalls würde ich eher

  #define EEPROM_WRITE_BYTE(eeval, val)  .....

schreiben, also das Target links - wie es nicht nur bei einer Zuweisung, 
sondern auch bei Standardfunktionen wie strcpy() oder memcpy() der Fall 
ist.

: Bearbeitet durch Moderator
von Swen (Gast)


Lesenswert?

Hallo,

ich bin gerade dabei, mir die Sachen noch einmal anzuschauen.

Warum ist der Aufruf hier falsch herum?

>>
>> IAR:
>> #define EEPROM_WRITE_BYTE(val, eeval)  do { eeval = val; } while (0);
>>
>> eeprom8_t ValueOneEEPROMAddress;
>> uint8_t   ValueOneFromEEPROM = 0;
>> uint8_t   ValueOneToEEPROM = 23;
>> EEPROM_WRITE_BYTE (ValueOneToEEPROM, ValueOneEEPROMAddress);
>>
>> So sollte es stimmen!?

>Sieht fast gut aus! Du hast beim Aufruf nur die Argumente vertauscht:

>EEPROM_WRITE_BYTE (ValueOneEEPROMAddress, ValueOneToEEPROM);

Ich übergebe den Wert, der ins EEPROM geschrieben werden soll (val -> 
ValueOneToEEPROM) und die Adresse, bzw. die EEPROM-Variable (eeval -> 
ValueOneEEPROMAddress).

Mit eeval = val schiebe ich doch dann den Wert von ValueOneToEEPROM in 
ValueOneEEPROMAddress, also ins EEPROM.

Zumindest ist das bisher so, da die Reihenfolge der Argumente im Makro 
und in den Funktionen wie eeprom_read_byte usw. gedreht sind.

Oder habe ich da jetzt einen Denkfehler drin?


Vielen Dank!

Beste Grüße
Swen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Swen schrieb:
> Warum ist der Aufruf hier falsch herum?

Sorry, ich hatte "falsch herum" gelesen. Ich konnte meinen Beitrag aber 
nicht mehr korrigieren und hatte es dann in einem nachfolgenden Beitrag 
versucht zu erklären, was ich meinte.

>>> #define EEPROM_WRITE_BYTE(val, eeval)  do { eeval = val; } while (0);

Eben genau das: Bei Zuweisungen, strcpy(), memcpy und anderen ähnlichen 
Funktionen (auch eeprom_write_xxxx()) steht das Target immer links. 
Daher würde ich das auch so herum schreiben, also:
1
#define EEPROM_WRITE_BYTE(eeval, val)  do { eeval = val; } while (0);

und für den AVR:
1
#define EEPROM_WRITE_BYTE(eeval, val)  do { eeprom_write_byte(eeval, val); } while (0);

Also: eeval immer links, val immer rechts.

Entschuldige bitte die Verwirrung.

von Swen (Gast)


Lesenswert?

Hallo,

kein Problem,

vielen Dank. Ich hatte den letzten Post auch gelesen, aber irgendwie 
dann noch nicht mit der Reihenfolge der Argumente in Verbindung gesetzt.

Nur der vollständigkeithalber. Bei dem Beispiel müssen die Semikolon am 
Ende dann wieder weggelassen werden!?

>Entschuldige bitte die Verwirrung.
So denkt man wenigstens mit und schreibt nicht nur ab!

Beste Grüße
Swen

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.