Hi,
ich habe ein Preprozessor Problem mit dem Makro sbi.
Folgendes habe ich zusammengebaut:
#define P0_N_RESET PORTB,7
...
void init(void)
{
sbi(P0_N_RESET);
}
...
Der GCC bringt nun folgende Fehlermeldung:
my_p0.h:20:20: macro "sbi" requires 2 arguments, but only 1 given
Mir ist klar, dass er es nicht check, dass er erst P0_N_RESET ersetzen
muss. Aber was kann ich tun ?
Gute Frage, nächste Frage. Aber es gibt eine Lösung, wenn auch keine so einfache. Im Anhang ist ein Beispiel dazu. Ich finde es auch schöner, wenn man Bits mit einem Namen definieren kann. Peter
Was hälst Du denn davon:
#define P0_N_RESET (1 << 7)
[..]
PORTB |= P0_N_RESET; /* set bit */
[..]
PORTB &= ~P0_N_RESET; /* clear bit */
[..]
@OldBug, na dann fröhliches Editieren des gesamten Quelltextes, wenn mal ein Pin den Port wechselt. Peter
Stimmt auch wieder... Naja, sbi ist jedenfalls deprecated und jede andere Lösung ist besser ggg
#define SBI(p, b) do { (p) |= 1<<(b) } while(0)
#define CBI(p, b) do { (p) &= ~(1<<(b)) } while(0)
#define LED_PORT PORTx
#define LED_BIT 3
#define LED_ON() SBI(LED_PORT, LED_BIT)
#define LED_OFF() CBI(LED_PORT, LED_BIT)
Was soll eigentlich dieses do...while(0) bewirken? Da war irgendwas mit "if"s, aber kapiert hab ich das noch nicht...
Das ist mir allerdings auch unklar.
#define SBI(p, b) do { (p) |= 1<<(b) } while(0)
hätte ich so geschrieben
#define SBI(p, b) ((p) |= 1<<(b))
Man muss allenfalls den Compiler anweisen, daß p volatile ist, aber das
sollte in den entsprechenden Headerdateien ja eh' so definiert sein.
Das "do { ...} while(0)" wird benötigt, um zu erreichen dass das Makro
sich exakt gleich verhält wie ein normaler Funktionsaufruf, d.h. das
Makro stellt ein Statement da.
Übrigens habe ich einen kleinen Fehler gemacht, ich habe die
Strichpunkte vergessen (schäm).
Hier ein kleines Beispiel:
#define BAD(a) (bla = (a))
#define GOOD(a) do { bla = (a); } while(0)
int bla;
int main()
{
BAD(123), "abc"; /* Hier meckert der Compiler nicht!!! */
GOOD(123), "abc"; /* Hier bricht der Compiler mit einem Fehler ab
*/
return 0;
}
Das Makro BAD ist ein Assignment, also eine Expression. Das Makro GOOD
ist ein Statement.
@Michael (ein anderer)
"Das "do { ...} while(0)" wird benötigt, um zu erreichen dass das
Makro
sich exakt gleich verhält wie ein normaler Funktionsaufruf, d.h. das
Makro stellt ein Statement da."
Ich verstehe nur Bahnhof.
Wozu braucht man denn sowas ?
Ein Statement ist alles, was ein ; hat oder in {} eingeschlossen ist.
Statt do{...}while(0) nehme ich lieber for(;;){...break;}, ist halt
Geschmackssache.
Als einzige Anwendung für sowas kenne ich bisher nur, daß man mehrere
Abbruchbedingungen testen will, also mit if(...)break; abbricht.
@Alle
Ich habe das original Posting so verstanden, daß er den Pin nur mit
einem einzigen Namen versehen will, d.h. daß der Name die Byte- und die
Bitadresse enthält.
Und das macht nur meine Lösung.
Peter
@Michael (ein anderer)
Ich hab das mal ausprobiert:
char i;
char bla( void )
{
return 7;
}
void test( void )
{
i = bla(), "abc";
"abc";
i = 5, "abc";
do{ i = 6; }while(0), "abc";
}
Nur die letzte Zuweisung ergibt eine Fehlermeldung, d.h. sie verhält
sich nicht wie ein Funktionsaufruf !!!
Funktionsaufrufe könne durchaus sinnvoll durch Komma getrennt werden.
In allen anderen Zeilen wird "abc" vom Compiler als Nonsens
betrachtet und einfach ignoriert.
Man muß halt wissen, was man hinschreibt und ich wüßte jetzt absolut
nicht, warum jemand sowas hinschreiben sollte.
Peter
"Das "do { ...} while(0)" wird benötigt, um zu erreichen dass
das Makro sich exakt gleich verhält wie ein normaler
Funktionsaufruf, d.h. das Makro stellt ein Statement da."
Das scheint nur für Leute von Bedeutung zu sein, die mehrere durch
Semikola getrennte Dinge in eine Zeile schreiben und sich vom Compiler
auf die Finger klopfen lassen wollen, wenn sie Komma und Semikolon
verwechseln.
Es gibt Probleme, von denen man gar nicht wusste, daß Leute sie haben
können.
Der Kommaoperator wird übrigens in § 6.5.17 vom Standard beschrieben, und ist ein Sequence-Point. Man kann mehrere Funktionen etc. damit trennen, sollte aber immer daran denken, daß der rechte Operand den Gesamtausdruck bestimmt, da kann man böse auf die Fresse fallen. Man kann sich auch darauf verlassen, daß der Kommaoperator geringere Bindung als die Zuweisung (=) hat, aber Ausdrücke wie p = i = 5, "abc"; sind dadurch schwerer lesbar, man muß also wissen, daß sie als (p = i = 5), "abc"; und nicht als p = (i = 5, "abc"); ausgewertert werden. Im ersten Fall steht in p (das z.B. char* sein könnte) eine 5, im letzten Fall die Adresse von "abc", und das könnte für den Rest des Programms von entscheidender Wichtigkeit sein ;-) Spaß mit Auswertung und Sequence-Points kann man vor allem mit && und || haben, wenn man ifs sparen und den Code verkomplizieren will. a || (b = 5); als Abkürzung für if (!a) b = 5; und Faxen dieser Art. Nicht genutzte aber bewertete Statements wie obiges "abc"; sind eigentlich nur lustig, um Anfänger in den Wahnsinn zu treiben, wenn man z.B. mal ein 42; in den Code einstreut. Alle Leute mit -Werror werden sich bedanken.
> Es gibt Probleme, von denen man gar nicht wusste, daß Leute sie > haben können. Jupp, siehe auch if (i == 42) vs. if (42 == i) Ich empfinde die erste Variante beim Lesen als "logischer", viele bevorzugen die letzte, weil sie gegen Vertipper schützt. Naja, Geschmackssache eben.
Mein heutiger Dank geht an den Zotteljedi. Dir sei ein virtuelles Bier ausgegeben.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.