Makros versus Funktionen

Gast #3333210
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?
Gast #3333239
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
#3333246
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
Angehängte Dateien:
#3333309
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);
Gast #3334094
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;)
Gast #3334281
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.
Gast #3334504
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren