Forum: Compiler & IDEs Makros versus Funktionen


von Stefan (Gast)


Lesenswert?

Ich habe mal den genialen Tip bekommen, für den schreibenden Zugriff auf 
einzelne I/O Pins bei AVR Makros zu verwenden:
1
#define writeBit(port,bit,value) { if ((value)>0) (port) |= (1<<bit); else (port) &= ~(1<<bit); }
Dieses Makros kann der Compiler wunderbar optimieren, wenn bit und value 
Konstanten sind. Aus "writeBit(PortA,3,1)" wird zum Beispiel "PortA|=1".

Aber Makros finde ich häßlich, vor allem wirken sie sich auf 
Fehlermeldungen störend aus. Wenn ich sie intensiv nutze, meldet mir der 
Compiler in Fehlermeldungen oft (aber komischerweise nicht immer) 
falsche Zeilen Nummern.

Deswegen erwäge ich, lieber Funktionen/Prozeduren zu verwenden. Aber 
dann klappt die Optimierung nicht mehr so gut, oder doch?
1
void writeBit(??? port, uint8_t bit, uint8_t value) {
2
  if (value>8)
3
    port |= (1<<bit);
4
  else
5
    port &= ~(1<<bit);
6
}
Wie kann ich das so verbessern, dass sowohl der Funktionsaufruf als auch 
der if-Ausdruck weg optimiert werden, sofern bit und value Konstanten 
sind?

Und was gehört an der Stelle ??? hin?

von Oliver (Gast)


Lesenswert?

Stefan schrieb:
> Aus "writeBit(PortA,3,1)" wird zum Beispiel "PortA|=1".

Hoffentlich nicht, dann wäre der Conmpiler kaputt.

Aber was gefäät dir an
1
PortA |= 1<<3;
nicht?

Wenn man mehrere Bits gleichzeitig setzen will, was ja durchaus mal 
vorkommt, passt dein Makro eh nicht mehr.

Oliver

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ich finde extra Funktionen für Zuweisungen zu umständlich.
Ich belasse es daher bei Zuweisungen.
Eine Zuweisung an eine Bitvariable geht einfach über ein Macro:
1
#include "sbit.h"
2
3
#define LED0 PORT_D5
4
#define TASTE0 PIN_B6
5
6
if( TASTE0 == 0 )
7
  LED0 = 0; // LED0 aus
8
else
9
  LED0 = 1; // LED0 ein

von (prx) A. K. (prx)


Lesenswert?

Stefan schrieb:
> Wie kann ich das so verbessern, dass sowohl der Funktionsaufruf als auch
> der if-Ausdruck weg optimiert werden, sofern bit und value Konstanten
> sind?

Sollte circa so gehen:
1
static inline void writeBit(volatile uint8_t *port, uint8_t bit, uint8_t value) {
2
  if (value>8)
3
    *port |= (1<<bit);
4
  else
5
    *port &= ~(1<<bit);
6
}
und Aufruf mit
1
writeBit(&PORTA, 6, 1);

von Yalu X. (yalu) (Moderator)


Lesenswert?

Die Zeile
1
  if (value>8)

sollte noch in
1
  if (value != 0)

oder einfach
1
  if (value)

geändert werden.

Dann erzeugt der Compiler (ab Version 4.3) wie erwartet SBI- bzw.
CBI-Instruktionen, wenn die Argumente bit und value konstant sind.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Dann erzeugt der Compiler (ab Version 4.3) wie erwartet SBI- bzw.
> CBI-Instruktionen, wenn die Argumente bit und value konstant sind.

Allerdings ohne Garantie :-P

von holger (Gast)


Lesenswert?

>> Dann erzeugt der Compiler (ab Version 4.3) wie erwartet SBI- bzw.
>> CBI-Instruktionen, wenn die Argumente bit und value konstant sind.
>
>Allerdings ohne Garantie :-P

Und für PORTF hoffentlich nicht;)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

holger schrieb:
> Und für PORTF hoffentlich nicht;)

Der Compiler hat keine Vorstellung von PORTF — braucht er auch nicht. 
Alles was er wissen muß ist die Adresse, denn die legt fest, ob die 
Instruktionen SBI, CBI, IN, OUT, SBIS, SBIC verwendet werden dürfen oder 
nicht.

von Dr. Sommer (Gast)


Lesenswert?

Stefan schrieb:
> Aber
> dann klappt die Optimierung nicht mehr so gut, oder doch?
Funktionen sind Makros grundsätzlich und immer vorzuziehen, falls 
irgendwie möglich. Makros haben keinen Typ, keinen Scope, bringen die 
Syntax durcheinander etc. Wenn man inline-Funktionen verwendet klappt 
die Optimierung genau so gut (wenn der GCC sich trotzdem weigert die 
Funktion zu inlinen, __attribute__((always_inline)) dranschreiben).

Headerfiles mit Tausenden #defines (siehe CMSIS...) sind wie ein Feld 
voller Tretminen, verwendet man einen "falschen" (per #define 
vorbelegten) Bezeichner gibt es obskure Fehlermeldungen. Versehentliche 
Doppel-Verwendung von Funktionsnamen funktioniert entweder (je nach Art 
der Verwendung & Scope) oder führen zu sinnvollen Fehlermeldungen.

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


Lesenswert?

Dr. Sommer schrieb:
> Makros haben keinen Typ, keinen Scope, bringen die Syntax durcheinander
> etc.

Naja, einen Scope haben sie.  File scope halt. :)

von Dr. Sommer (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Naja, einen Scope haben sie.  File scope halt. :)
Nö:
1
#define foo1 7
2
const int foo2 = 7;
3
4
int main () {
5
  double foo1; // funktioniert nicht, gibt lustige Compilerfehler
6
  double foo2; // funktioniert; die globale foo2-Variable wird "unsichtbar"
7
}
Deklarationen in einem inneren Scope (hier der Funktionskörper von main) 
können Deklarationen im äußeren Scope (hier der globale Scope) 
überdecken; bei Macros geht das nicht.

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


Lesenswert?

Dr. Sommer schrieb:
> Deklarationen in einem inneren Scope (hier der Funktionskörper von main)
> können Deklarationen im äußeren Scope (hier der globale Scope)
> überdecken; bei Macros geht das nicht.

OK, überredet. ;-)

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.