Variante 1 ist denke ich mal das üblichste oder ? Dabei sehe ich
allerdings zum einem Einschränkungen bei der Portierbarkeit des Codes
und zum anderen wird es schnell unübersichtlich. Beispiel invertierte
Led. Liest man im Quelltext LEDPORT |= (1<<LED_PIN) weiß man also nicht
ob die Led wirklich ein- oder ausgeschaltet wird.
Variante 2 würde das Problem umgehen und man könnte das SET_POWERLED
Define an die entsprechende Polarität anpassen. Allerdings ist es schon
ein ganzer Batzen mehr Schreibarbeit.
Variante 3 würde zumindest die Portierbarkeit erleichtern das
invertierte Logik Problem bleibt.
Wie macht man es am schlausten ?
Wird noch viel lustiger, wenn du den Port zwischen Ein- und Ausgang
umschalten musst...
Man erspart sich allerdings vieles, wenn man vorher mal überlegt, wo die
Abstraktion überhaupt sinnvoll eingezogen wird. D.h., was überhaupt und
wie weit portabel sein soll.
Eine Status-LED gehört meistens nicht dazu, sodass es sich da nicht
lohnt, den Zugriff auf den entsprechenden Pin projektweit mit Makros zu
veröffentlichen. Eher würde ich den gesamten Statusakt wieder in ein
Modul verpacken und dort dann zwei statische Funktionen led_on() und
led_off() einbauen -- kostet ja nichts, wird ja als inline realisiert.
Die LED wird dann indirekt gesteuert über Funktionen, die eine Ebene
höher/abstrakter ansetzen.
Eine Mischform wäre, den Port fest vorzugeben und nur die Pinnummer bzw.
Bitmaske zu übergeben. Statt Makros sind Inline-Funktionen schöner:
1
#define LED_PORT PORTA
2
#define LED_POWER (1 << PA0)
3
#define LED_BUSY (1 << PA1)
4
// ...
5
6
staticinlinevoidleds_on(uint8_tmask)
7
{
8
LED_PORT|=mask;
9
}
10
11
staticinlinevoidleds_off(uint8_tmask)
12
{
13
LED_PORT&=~mask;
14
}
15
16
staticinlinevoidleds_toggle(uint8_tmask)
17
{
18
LED_PORT^=mask;
19
}
Verwendung:
1
leds_on(LED_POWER|LED_BUSY);
2
leds_toggle(LED_BUSY);
Das kann man immer portieren. Falls die LEDs in einem anderen Projekt
mal an verschiedenen Ports hängen sollten, braucht man in den Funktionen
zwar Fallunterscheidungen, der Compiler optimiert die aber weg.
Vielen Dank für die Reihe nützlicher Tipps. Die Möglichkeit von inline
Funktionen war mir garnicht bekannt. Das könnte an der ein oder anderen
Stelle wirklich nützlich sein.
Jetzt hat es mir allerdings die sbit.h wirklich angetan. Auch wenn mir
die Benennung über PORT_XX wieder zu distanziert ist. Aussagekräftigere
Namen würden das ganze wirklich perfekt machen.
Klaus schrieb:> define POWERLED _LATA0> POWERLED = 1;
Je nachdem, wie die LED verschaltet ist, schaltest du sie mit der
Anweisung ein oder aus. Das kann man im Programm nicht erkennen. Nur
deshalb gibt es ja überhaupt das das Gehampel mit den Makros.
Oliver
Codingstyle schrieb:> Aussagekräftigere> Namen würden das ganze wirklich perfekt machen.
Das bleibt Dir unbenommen, die vordefinierten Namen wollen Dir nur die
Schreibarbeit erleichtern.
Statt:
@ Peter stimmt so geht es natürlich auch.
Die Variante beseitigt die invertierte Logik aber auch nicht. Macht
lediglich den Zugriff schön elegant.
Klar man könnte sowas machen:
1
#define POWER_LED SBIT(PORTA, PA0)
2
#define LED_ON 0
3
#define LED_OFF 1
4
5
POWER_LED=ON;
6
POWER_LED=OFF;
Dann wäre wieder klar was ein und was aus ist.
Wie handhabst du das für gewöhnlich. Einfach mit der invetierten Logik
leben man selber weiß ja was man tut. Oder einen kleinen Kommentar
hinter ?
Fast vergessen habe noch eine weitere Frage zu den inline Funktionen.
Wie "wild" kann man es da denn wirklich treiben damit die Funktion auch
wirklich inline umgesetzt wird ?
Funktioniert inline auch bei Funktionen mit Rückgabewert und auch bei
Funktionen mit mehr als einem Parameter ?
Sowas beispielsweise:
Codingstyle schrieb:> Funktioniert inline auch bei Funktionen mit Rückgabewert und auch bei> Funktionen mit mehr als einem Parameter ?
Ja.
Codingstyle schrieb:> // Leds mit invertierter Logik> static inline void set_led(uint8_t port, uint8_t pin)> {> port &= ~(1 << pin);> }
Das geht, hat aber den Nachteil, daß der Compiler das nicht mehr zu
einem einfachen sbi oder cbi optimieren kann. Da musst du schon bei der
dem Makro entsprehenden Variante mit echten Konstanten bleiben:
1
staticinlinevoidpower_led_on()
2
{
3
POWER_LED_PORT&=~(1<<POWER_LED_PIN);
4
}
Das wäre dann auch meine bevorzugte Alternative.
Oliver
Bei der Taster Auslesevariante hat man das selbe Problem nehme ich an ?
also müsste auch dort dann für jeden Taster eine Inline Methode her ?
Wobei mir nicht ganz klar ist wo das Problem für den Compiler besteht.
Unter Inline hätte ich jetzt die Komplette Auflösung der Funktion
erwartet. Und dabei müsstem dem Compilert die weiten doch bereits
bekannt sein ?
Codingstyle schrieb:> Funktioniert inline auch bei Funktionen mit Rückgabewert und auch bei> Funktionen mit mehr als einem Parameter ?
Ja und ja.
Ob "geinlined" wird oder nicht, hängt u.a. vom Optimierungsgrad ab, der
beim Compilieren gewählt wird. Mit -Os beispielsweise wird inlining
WEITGEHEND vermieden.
Nicht eingebettete inlines kann man sich mit -Winline als Warnung
ausgeben lassen.
Wenn man Inlining unabhängig vom Optimierungslevel erzwingen will, muß
man die entsprechende Funktion mit
__attribute__((always_inline))
deklarieren.
Da sieht man schön das er die get_io trotz Parameter und und
Rückgabewert in ein einfaches SBIS übersetzt hat. Was er da bei set_io
macht kann ich allerdings nicht ganz nachvollziehen.
Versteht jemand was beim set_io passiert ?
Codingstyle schrieb:> Wobei mir nicht ganz klar ist wo das Problem für den Compiler besteht.> Unter Inline hätte ich jetzt die Komplette Auflösung der Funktion> erwartet. Und dabei müsstem dem Compilert die weiten doch bereits> bekannt sein ?
Nich alles glauben was hier geschrieben wird. Wenn der Compiler die
Werte bei einem Makro kenn, dann kennt er sie auch in einer
entsprechenden inline-Funktion.
Erklärt aber trotzdem nicht was mit dem set_io() in obigem Code passiert
oder ?
Das ret am Ende des Codes wird der Rücksprung aus der main sein nehme
ich jedenfalls stark an.
Zumindest sollte aus der set_io() aber in jedemfall doch entweder ein
sbi oder ein or resultieren. Keines von beidem kann ich im assambler
finden. Wo ist das hin verschwunden ?
Die Krücke mit dem 'sbit' wird insgesamt auch nicht besser, wenn man sie
andauernd wiederholt.
Mit den Bit-Feldern nimmt man sich schließlich jegliche Möglichkeit, den
Zugriff auf das Bit irgendwie durchzureichen. Es gibt nämlich keine
Zeiger auf Bitfelder und man kann auch nur schwer wieder eine Bitmaske
aus der Definition gewinnen.
Für mehr als den Zugriff auf die Pins sollte man das ohnehin nicht
benutzen, andernfalls schleppt man sich schnell lustige Nebeneffekte
ein.
was war jetzt nochmal der unschlagbare Vorteil von inline functionen im
Vergelich zu klassischen Makros ?
Über die "Pointerproblematik" die sich dadurch ergibt das eine Inline
Funktion nach außen hin natürlich erstmal nichts anderes als eine
klassiche Funktion ist hatte ich garnicht mehr nachgedacht.
Führt dann natürlich auch zu einem entsprechenden Aufruf mit
set_io(&port, pin) das ist dann schonwieder eine merkwürdige Eigenart
wenn nicht konsequent durchgezogen.
Ist dann nicht letzlich ein Makro in folgender Form doch wieder schöner
?
1
#define POWER_LED_PORT PORTA
2
#define POWER_LED PA0
3
4
#define STATUS_LED_PORT PORTA
5
#define STATUS_LED PA1
6
7
// Led setzen invertierte Logik
8
#define SET_LED(port, pin) (port &= ~(1<<pin))
9
#define RESET_LED(port, pin) (port |= (1<<pin))
10
11
//-------------- main
12
13
SET_LED(POWER_LED_PORT,POWER_LED);
14
RESET_LED(POWER_LED_PORT,POWER_LED);
Bei der Variante hätte man den Vorteil für alle vorhanden Leds eine
einzige Set und Reset "Funktion" zu haben.
Würde man es per inline umsetzen müsste man ja wie gesagt einen Pointer
auf den Port übergeben. Und das hat man ja im obigen Beispiel schon
schön gesehen ist man einfach nicht gewohnt. Da würde ich vermutlich
einfach durch den Tunnelblick der klassichen Variante PORT |= (1<<pin)
auch immer set_led(port,pin) schreiben und würde somit anstelle der
Adresse den aktuellen Registerwert übergeben und dabei nichtmal
Compilerseitig gewarnt.
Das wikte zwar anfangs wie eine für mich schöne Lösung nun nach der
Einführung von Pointern aber nicht mehr.
Bliebe bei Inline wirklich nur die Variante für jede Led einzelne Set_
und Reset_ und eventuell Toggle_ inline Funktionen zu schreiben. Hat man
5 Leds sind das aber schon 15 inline Methoden. Das sieht dann irgendwo
auch nicht mehr wirklich schick aus. Also doch wieder zurück zum
klassischen Makro ?
Sven P. schrieb:> Die Krücke mit dem 'sbit' wird insgesamt auch nicht besser, wenn man sie> andauernd wiederholt.
Das muß auch nicht besser werden, das ist schon gut genug.
Es ist halt nur ziemlich unbekannt, daß es diese Möglichkeit in C gibt.
Es ist auch nicht auf IO-Pins beschränkt, man kann so auch interne
Bitvariablen definieren.
Ich nehme es daher auch gerne in Steuerungen, wo logische Verknüpfungen
auszuwerten sind. Denn oft braucht man nicht die Geschwindigkeit eines
FPGA dafür.
Hi,
ich habe mich jetzt für meinen Fall für folgende Variante entschieden.
Für Module die als solche einen eigenen Wert haben, so das man Sie in
anderen Projekten wiederverwendet oder eventuell weitergibt verwende ich
die klassische Vorgehensweise der standard Bitmanipulation. Da hat man
Sicherheit das es allgemein akzeptiert und verstanden wird.
Für die konkrete speziell Hardware bezogene Implementierung sprich
Tasterabfragen, Leds, Enable/Select Signale verwende ich die SBIT
funktion mit dem Bitfeld.
Ich denke mit der Mischung fahre ich so ganz gut.
Peter Dannegger schrieb:> Das muß auch nicht besser werden, das ist schon gut genug.> Es ist halt nur ziemlich unbekannt, daß es diese Möglichkeit in C gibt.
Es gibt sie ja auch nur so unter der Hand...
Ohne Compiler-spezifisches Wissen gibt es sie nicht, denn zu Bitfeldern
ist im Standard fast nichts festgelegt.
> Es ist auch nicht auf IO-Pins beschränkt, man kann so auch interne> Bitvariablen definieren.
Natürlich, aber da der AVR keinen bitadressierbaren Speicher hat, wird
immer ein Read-Modify-Write draus. Das kann bei SFR schonmal komische
Sachen machen.
Codingstyle schrieb:> Eine Frage noch zu SBIT. Wieso wird aus POWER_LED = 1 eine SBI 0x02,0> Anweisung aus if (TASTER) aber ein IN R24,0x00, SBRC R24,1 ?
Du nimmst den Port und castest ihn als Pointer auf eine Struct aus 8
Bits.
Die Elemente der Struct haben zufälliger Weise eine Ziffer 0..7 am Ende.
Und die Definitionen wie z.B. PINA1 entsprechen einer Ziffer (1). Diese
Ziffer wird nun mit dem Buchstaben b verbunden und dann wird daraus b1.
Der Compiler weiß nun, daß er auf das Element b1 der Struct zugreifen
muß und erzeugt den entsprechenden Code.
Und da die Elemente Bits sind, sind für sie nur die Werte 0 und 1
zulässig.
Hier kann man auf SYNCSEL als 5 Bit Integer oder auch auf SYNCSEL0 bis 4
als Einzelbits zugreifen. Da ist dann auch gleich ein Problem in der
Doku gefixt worden, FLTMD kann auch als FLTMODE geschrieben werden.
Und wenn es Sinn macht, kann man sich via #define für
OC1CON2bits.SYNCSEL einen eigenen Namen ausdenken.
Sven P. schrieb:> Es gibt sie ja auch nur so unter der Hand...> Ohne Compiler-spezifisches Wissen gibt es sie nicht, denn zu Bitfeldern> ist im Standard fast nichts festgelegt.
IMHO ist da in neueren C Standards schon etwas beschrieben. Aber wenn
das in den Headerfiles vom Chip und Compilerlieferanten so gemacht wird,
hab ich damit kein Problem. Sollte sich da etwas ändern, wird sämtlicher
(professioneller) C-Code obsolete und den Chiphersteller gibt es dann
nicht mehr.
MfG Klaus
Hm wie der Cast funktioniert war mir eigentlich schon klar soweit.
Was mir aber noch immer nicht klar ist wieso aus einem einfachen setzen
des Bits ein schneller SBI Befehl wird. Aus dem auslesen aber kein SBIS
und aus togglen kein EOR sonder sowas:
0000008A IN R25,0x02 In from I/O location
0000008B COM R25 One's complement
0000008C ANDI R25,0x01 Logical AND with immediate
0000008D IN R24,0x02 In from I/O location
0000008E ANDI R24,0xFE Logical AND with immediate
0000008F OR R24,R25 Logical OR
00000090 OUT 0x02,R24 Out to I/O location
Klar macht es für die meisten Anwendungen absolut keinen Sinn jeden
einzelnen Assamblerbefehl aufzuwiegen. Aber inperformanter als die
standard Bitschubserei ist es schon oder ?
Codingstyle schrieb:> Aus dem auslesen aber kein SBIS> und aus togglen kein EOR sonder sowas:
SBIS macht er, nur beim togglen ist er etwas umständlich:
Mit welchem Optimierungslevel hast du compiliert ?
Bei mir macht er bei if (PIN_B2) immer:
00000088 IN R24,0x00 In from I/O location
00000089 SBRS R24,1 Skip if bit in register set
Also nicht direkt SBSI aus Register sonder erst Register auslesen und
dann SBSR.
> was war jetzt nochmal der unschlagbare Vorteil von inline functionen im> Vergelich zu klassischen Makros ?
Es gibt nicht den einen unschlagbaren Vorteil, und nicht jeder greift in
diesem Use-Case.
Hier sind die, die mir gerade so einfallen:
- sie kümmern sich um namespaces. Nur für C++ relevant.
- du bekommst eine Typprüfung.
- Wenn du eine Compilerfehlermeldung bekommst, weil du sie falsch
aufgerufen hast, dann musst du nur den Aufruf und den Prototypen
anschauen. Bei Makros kanns sein, dass du viele Makros anschauen musst.
- sie werten ihre Parameter nur einmal aus (und haben nur einmal
Seiteneffekte). Hier nicht relevant weil jeder Parameter nur einmal
vorkommt.
- Der Fehler, dass du die Klammerung vergisst, passiert nicht. Die
meisten hier sinnvollen Operatoren binden stärker als <<, aber der ?:
operator könnte mal im pin vorkommen.
> #define SET_LED(port, pin) (port &= ~(1<<pin))> #define RESET_LED(port, pin) (port |= (1<<pin))