Ich glaube, mein Ansatz ist gut, denn er wurde perfekt optimiert. In
einem echten Projekt würde ich die #defines für die Pins in eine
separate Header Datei schreiben.
Was haltet ihr davon?
Stefan U. schrieb:> Ich glaube, mein Ansatz ist gut, denn er wurde perfekt optimiert. In> einem echten Projekt würde ich die #defines für die Pins in eine> separate Header Datei schreiben.>> Was haltet ihr davon?
Er wird nur in diesem einen Fall perfekt optimiert. Unter anderen
Bedingungen (sehr viele Port-Operationen) sag ich, dass die
Funktionssprünge bleiben, d.h. es braucht einfach länger. Warum keine
komplett Lösung über #defines? Dann kriegst du die gleiche Effizienz, es
halt nicht dynamisch
> Ich glaube, mein Ansatz ist gut, denn er wurde perfekt optimiert.
Das ist nur die halbe Miete. Und auch der unwichtigere Teil.
> Was haltet ihr davon?
Nichts. Der Code ist nach wie vor schlecht lesbar. Z.B. muß man wissen,
wie die LED angeschlossen ist. Sonst kann man nicht entscheiden ob das
Schreiben einer 1 die LED nun an- oder ausschaltet. Und man muß auch
wissen, welche Bedeutung die LED mit der Nummer 3 denn nun hat. Viel
besser finde ich in diesem Zusammenhang Makros mit sprechendem Namen:
1
#define LED_error PA1
2
#define LED_error_on PORTA |= (1<<LED3)
3
#define LED_error_off PORTA &= ~(1<<LED3)
Das Makro für LED_error dient dabei nur der Bequemlichkeit - wenn ich
die LED doch mal an einen anderen Pin verschiebe, muß ich nur dieses
eine Makro ändern. Allerdings habe ich da nicht nur einfach "1"
hingeschrieben, sondern "PA1" (was am Ende auch zu 1 evaluiert). Warum?
Damit ich, wenn ich die LED doch mal an einen anderen Port verschiebe,
daran erinnert werde mir die anderen beiden Makros anzusehen. Man kann
das sicher mit ein bißchen mehr Breakdance noch weiter optimieren, daß
man die LED.._on und LED.._off Makros gar nie mehr anfassen muß.
Andererseits wird das ja eher selten passieren und man muß es mit dem
Aufwand nicht übertreiben.
Diese ganzen hardwareabhängigen Makros landen am Ende alle in einer
ports.h oder hal.h und werden in 99% der Fälle nie wieder angefaßt. Und
im Code muß ich dann nie wieder nachschauen (oder nachdenken) was
> set_pin_high(PORTA,LED1)
Das gefällt mir nicht, weil ich dann wissen muss, dass LED1 an Port A
hängt.
Genau deswegen hatte ich versucht, den Port und den Pin in eine
Definition zu packen.
Axels bedenken, dass man wissen muss, wie die LED angeschlossen ist,
kann ich nachvollziehen. Das halte ich für ein gute Argument für seinen
Vorschlag, der mir gefällt:
> #define LED_error PA1> #define LED_error_on PORTA |= (1<<LED_error)> #define LED_error_off PORTA &= ~(1<<LED_error)
HabSelbstNullAhnung schrieb:> Fabian F. schrieb:>> usw>> Warum nicht gleich so, wie Axel es vorschlägt?
Unterschiedliche Anwendungen. Das define wie in meinem Letzten Post
binde ich immer in jedem Projekt ein.
Das was Axel macht ist sehr Projektspezifisch und kommt daher in eine
andere Header.
Wenn ich einen Pin häufig verwende (>4-5mal) mach ich mir eine
Definition wie die von Axel. (SPI-Chip select, LED On Off usw.)
Wenn ich einen Pin selten ändere nutze ich die allgemeine Definition und
schreib bestenfalls in einen Kommentar dahinter was das ist
(CAN-Controller anable, Initialisierungen...).
Stefan U. schrieb:> Axels bedenken, dass man wissen muss, wie die LED angeschlossen ist,> kann ich nachvollziehen.
Ich kennzeichne solche Pins mit einem führenden x, z.B.:
Axel S. schrieb:> Viel> besser finde ich in diesem Zusammenhang Makros mit sprechendem Namen:> #define LED_error PA1> #define LED_error_on PORTA |= (1<<LED3)> #define LED_error_off PORTA &= ~(1<<LED3)
gefällt mir auch am Besten, genauer gesagt so mache ich es auch
auch durch die Vergabe der Namen LED_ finde ich im Code alle LED_ immer
wieder und mit _error kann ich deren Bedeutung im Code sofort
nachvollziehen auch nach Jahren.
LED_error_on;
LED_error_off; ist im Code selbsterklärend, was interessiert mich im
Code wo die Led an welchem Pin angeschlossen ist?
Dafür gibt es Header welche ich mir baue mit pins.h und dort habe ich
die Übersicht welche Pins belegt sind welche frei, welche geeignet sind
weil nicht anders nutzbar (PWM IRQ TxD RxD INT0) und durch pins.h ist es
auch leicht den AVR zu tauschen ohne den Code zu ändern.
Stefan U. schrieb:>> set_pin_high(PORTA,LED1)>> Das gefällt mir nicht, weil ich dann wissen muss, dass LED1 an Port A> hängt.
Wenns daran scheitert:
#define LED1 PORTA,PA2
set_pin_high(LED1);
> Genau deswegen hatte ich versucht, den Port und den Pin in eine> Definition zu packen.>> Axels bedenken, dass man wissen muss, wie die LED angeschlossen ist,> kann ich nachvollziehen.
Hat durchaus seine Existenzberechtigung solche defines zu benutzten,
aber ich bevorzuge meistens die allgemeine Definition, weil man die
leichter automatisieren kann:
void LED_bar(uint8 delay)
{
for (unint8 i=0, i<8, i++)
{
set_pin_high(LED_ARRAY01, i);
sleep_ms(delay);
}
}
Anstatt:
void LED_bar(uint8 delay)
{
LED1_on;
sleep_ms(delay);
LED2_on;
sleep_ms(delay);
LED3_on;
sleep_ms(delay);
LED4_on;
sleep_ms(delay);
LED5_on;
sleep_ms(delay);
LED6_on;
sleep_ms(delay);
LED7_on;
sleep_ms(delay);
LED8_on;
sleep_ms(delay);
}
@Peter:
Diese sbit.h kenne ich, sie wurde hier vor einigen Monaten schon einmal
ausführlich vorgestellt.
Allerdings ist sie für meinen Geschmack zu ausgefuchst - zu groß und
nicht leicht nachvollziehbar, wie man sie richtig anwendet.
> Wenns daran scheitert:> #define LED1 PORTA,PA2> set_pin_high(LED1);
Dann sind wir wieder auf meinen ersten Vorschlag zurück gekommen. Denn
dieses Konstrukt erfordert IMHO, dass set_pin_high() eine in C
geschriebene Funktion ist (kein Makro). Und dann muss man wissen, ob die
LED bei High oder bei Low leuchtet.
Fabian F. schrieb:> HabSelbstNullAhnung schrieb:>> Fabian F. schrieb:>>> usw>>>> Warum nicht gleich so, wie Axel es vorschlägt?>> Unterschiedliche Anwendungen. Das define wie in meinem Letzten Post> binde ich immer in jedem Projekt ein.> Das was Axel macht ist sehr Projektspezifisch und kommt daher in eine> andere Header.
Warum sollte man unterhalb des hardwarespezifischen Makros noch eine
Abstraktionsschicht einfügen wollen? Da dieser Krempel definitionsgemäß
spezifisch für eine bestimmte Hardware ist, darf er auch gerne
spezifisch für eine Architektur sein. Ich habe da überhaupt keine
Skrupel, direkt auf Hardware-Register zuzugreifen.
Ich halte es im Gegenteil für ausgesprochenen Humbug, Abstraktionen und
APIs aufeinander zu stapeln wie Bauklötze. Das sehe ich viel zu häufig
(und es ist in vielen Fällen Ursache von Performanceproblemen).
Fabian F. schrieb:> Hat durchaus seine Existenzberechtigung solche defines zu benutzten,> aber ich bevorzuge meistens die allgemeine Definition, weil man die> leichter automatisieren kann:>> void LED_bar(uint8 delay)> {> for (unint8 i=0, i<8, i++)> {> set_pin_high(LED_ARRAY01, i);> sleep_ms(delay);> }> }> Anstatt:> void LED_bar(uint8 delay)> {> LED1_on;> sleep_ms(delay);> LED2_on;> sleep_ms(delay);> LED3_on;
...
Ganz schlechtes Beispiel. Wenn man 8 LED hat, die man einzeln ein- und
ausschalten können will, dann muß man keineswegs ein Makro pro LED
schreiben, sondern es reichen nach wie vor zwei Makros (die man im
Anwendungscode verwendet). Makros können schließlich Parameter haben.
Und wenn die LEDs keine individuellen Namen haben, sondern eine Nummer,
dann paßt ein INT Parameter für die Nummer der LED ja prima.
Generell sollte man aber vermeiden Funktionen hinter Makros zu
verstecken.
Gründe:
- Schlecht debugbar
- Sprache wird redefiniert (C bietet genau für sowas Funktionen)
- Fehlendes Typechecking (bei Makros mit Argumenten)
- Eventuell Nebeneffekte (Klammerung usw.)
- Autoformater in Eclipse kommt mit LED_error_on(); nicht zurecht ;) -
macht daraus immer folgendes:
1
LED_error_on()
2
;
1
voidLED_error_on(void)
2
{
3
PORTA|=(1<<PA1);
4
}
oder
1
voidLED_error_on(void)
2
{
3
LED_error_Port|=(1<<LED_error_Pin);
4
}
Normalerweise sollte das auch vom Compiler in sbi und cbi umgewandelt
werden (kann sein, dass dafür ein Compilerswitch notwendig ist). Aber
das funktioniert nur innerhalb EINER Datei, sonst wird ein
Funktionsaufruf daraus gemacht! Wenn du LED_error_on() dann also in der
main.c verwendest msust du es auch in der main.c definieren und nicht in
einer led.c oder so!
> Aber das funktioniert nur innerhalb EINER Datei, sonst wird ein> Funktionsaufruf daraus gemacht!
Oh, das ist ein guter Hinweis. Habe ich noch gar nicht bedacht.
Ich schrieb:> Aber> das funktioniert nur innerhalb EINER Datei, sonst wird ein> Funktionsaufruf daraus gemacht!
Mit Link Time Optimisation nicht unbedingt.
Ich schrieb:> Aber> das funktioniert nur innerhalb EINER Datei, sonst wird ein> Funktionsaufruf daraus gemacht!
Wenn Funktionen geinlined werden sollen, dann sollte man das auch so
hinschreiben. Und dann gehören sie natürlich in die *.h-Datei.
Stillschweigend vom Compiler irgendwas zu erhoffen, gehört sich nicht.
Peter D. schrieb:> Ich schrieb:>> Aber>> das funktioniert nur innerhalb EINER Datei, sonst wird ein>> Funktionsaufruf daraus gemacht!>> Wenn Funktionen geinlined werden sollen, dann sollte man das auch so> hinschreiben. Und dann gehören sie natürlich in die *.h-Datei.> Stillschweigend vom Compiler irgendwas zu erhoffen, gehört sich nicht.
In C gibt es kein "inline", das gibt es nur in C++, daher bleibt einem
nichts anderes übrig. Der GCC kann das zwar aber da ist es nur ein
Hinweis an den Compiler, der nicht zwangsweise umgesetzt wird. Verlässt
man sich aber darauf, kann das schief gehen. Wenn die Funktion in einer
anderen Datei definiert ist kann der Kompiler das ja ohnehin nicht
inlinen, da die Implementierug der Funktion nicht bekannt ist.
Ich schrieb:> Generell sollte man aber vermeiden Funktionen hinter Makros zu> verstecken.
War ja klar, daß so ein Kommentar noch kommen würde. Doch, man will
das mit einem Makro machen. Genau dafür sind die Makros da.
> Gründe:> - Schlecht debugbar
?
> - Sprache wird redefiniert (C bietet genau für sowas Funktionen)
Und Makros.
> - Fehlendes Typechecking (bei Makros mit Argumenten)
In diesem Kontext irrelevant.
> - Eventuell Nebeneffekte (Klammerung usw.)
Kann man lernen. Zugegeben, meinen Makros fehlten die Klammern. Ich
wollte aber auch einen ganz anderen Punkt demonstrieren: sprechende
Namen und eine Abstraktion davon was aus Sicht des µC passiert (ein Bit
auf 1 setzen) und was aus Anwendungssicht passiert (eine LED geht an).
> - Autoformater in Eclipse kommt mit LED_error_on(); nicht zurecht ;)
Eclipse will man ohnehin vermeiden. Bloat ohne Ende.
Und überhaupt. Wenn ein Werkzeug (in dem Fall eine IDE bzw. deren
Editorkomponente) nicht mit dem Standard-Sprachumfang zurecht kommt,
dann ist sie schlicht und einfach kaputt.
Leider haben viele Anfänger Angst vor Makros. Und das nicht zuletzt
deswegen, weil "Experten" wie du ihnen diese Angst einreden.
> das funktioniert nur innerhalb EINER Datei, sonst wird ein> Funktionsaufruf daraus gemacht! Wenn du LED_error_on() dann also in der> main.c verwendest msust du es auch in der main.c definieren und nicht in> einer led.c oder so!
Noch ein Punkt der für Makros spricht. Denn tatsächlich will man eine
solche Abstraktionsschicht auslagern. Klar kann man die Funktionen dann
static definieren und in eine zentrale Headerdatei stecken. Aber IMHO
ist das wieder nur ein Workaround den man eigentlich gar nicht bräuchte.
Axel S. schrieb:> Klar kann man die Funktionen dann> static definieren und in eine zentrale Headerdatei stecken. Aber IMHO> ist das wieder nur ein Workaround den man eigentlich gar nicht bräuchte.
Das ist kein Workaround sondern der richtige Weg. Außerdem sollte damit
gleich eine gescheite Abstraktionsschicht aufbauen. Z.B.
static inline void SetErrorLED(uint8_t state) { ... }
Sobald in diesen Funktionen ein bisschen mehr drin steht, willst du die
nicht in Makros kapseln. Das ist unleserlich, fehleranfällig und
schlecht zu debuggen, weil man eben Makros nicht debuggen kann. Makros
braucht man für solche Sachen einfach nicht. Die sind an anderen Stellen
praktisch. Beispielsweise für komfortables Assert-Zeugs in C.
Axel S. schrieb:>> - Schlecht debugbar>> ?
Schon mal durch ein Makro gestept?
>> - Sprache wird redefiniert (C bietet genau für sowas Funktionen)>> Und Makros.
Nein.
>> - Fehlendes Typechecking (bei Makros mit Argumenten)>> In diesem Kontext irrelevant.
Hier ja, sonst nein. Hab es der Vollständigkeit halber erwähnt.
>> - Eventuell Nebeneffekte (Klammerung usw.)>> Kann man lernen. Zugegeben, meinen Makros fehlten die Klammern. Ich> wollte aber auch einen ganz anderen Punkt demonstrieren: sprechende> Namen und eine Abstraktion davon was aus Sicht des µC passiert (ein Bit> auf 1 setzen) und was aus Anwendungssicht passiert (eine LED geht an).
Ja, sprechende Namen sollte man verwenden. Auf der Klammerung wollte ich
eigentlich gar nicht rumreiten aber das ist z.B. einer der Gründe warum
bei Makros vorsicht geboten ist. Wenn du schon häufiger mit Makros zu
tun gehabt hättest, hättest du die Klammern automatisch gesetzt, weil du
über die Fallstricke Bescheid wüsstest.
> Eclipse will man ohnehin vermeiden. Bloat ohne Ende.
Geschmacksache. Wenn ein Tool hilfreich ist, kann man es auch verwenden.
Ob das der Fall ist, muss jeder für sich selber entscheiden. Ich
persönlich finde Eclipse sehr hilfreich.
> Und überhaupt. Wenn ein Werkzeug (in dem Fall eine IDE bzw. deren> Editorkomponente) nicht mit dem Standard-Sprachumfang zurecht kommt,> dann ist sie schlicht und einfach kaputt.
Der Autoformater ist nur eine Convenience Funktion. Man beachte auch den
Zwinkersmiley.
> Leider haben viele Anfänger Angst vor Makros. Und das nicht zuletzt> deswegen, weil "Experten" wie du ihnen diese Angst einreden.
Der Präprozessor ist hilfreich, man kann sich aber auch ins Knie
schießen damit, deshalb ist hier auch eine gewisse Vorsicht geboten, das
ist schon ok so.
>> das funktioniert nur innerhalb EINER Datei, sonst wird ein>> Funktionsaufruf daraus gemacht! Wenn du LED_error_on() dann also in der>> main.c verwendest msust du es auch in der main.c definieren und nicht in>> einer led.c oder so!>> Noch ein Punkt der für Makros spricht. Denn tatsächlich will man eine> solche Abstraktionsschicht auslagern. Klar kann man die Funktionen dann> static definieren und in eine zentrale Headerdatei stecken. Aber IMHO> ist das wieder nur ein Workaround den man eigentlich gar nicht bräuchte.
Nein, man kann es doch auslagern ohne Makros. Dein Vorschlag mit Code in
der Headerdatei ist genau so Murks.
Ich schrieb:> Axel S. schrieb:>>> - Schlecht debugbar>>>> ?>> Schon mal durch ein Makro gestept?
Wieviele Steps willst du für ein Makro brauchen, das genau ein
C-Statement lang ist?
>>> - Sprache wird redefiniert (C bietet genau für sowas Funktionen)>>>> Und Makros.>> Nein.
Aber doch.
>>> - Fehlendes Typechecking (bei Makros mit Argumenten)>>>> In diesem Kontext irrelevant.>> Hier ja, sonst nein.
Eben.
>> Leider haben viele Anfänger Angst vor Makros. Und das nicht zuletzt>> deswegen, weil "Experten" wie du ihnen diese Angst einreden.>> Der Präprozessor ist hilfreich, man kann sich aber auch ins Knie> schießen damit, deshalb ist hier auch eine gewisse Vorsicht geboten, das> ist schon ok so.
Nur daß in dem Kontext in dem wir hier gerade sind, deine Einwände
allesamt für die Katz sind. Das ist doch genau mein Punkt.
Kann man sich mit Makros in den Fuß schießen? Ja, man kann. Wird es
unübersichtlich, wenn ein Makro zu 100 Zeilen C-Code expandiert und man
den Code dann debuggen will? Ja, wird es. Ist es relevant im Rahmen
dessen, was wir gerade diskutieren? Nein, ist es nicht.
Du darfst gerne vor Makros warnen, wenn es angebracht ist. Aber laß doch
bitte die Kirche im Dorf. Ein Makro ohne Paramter, das zu genau einem
Statement expandiert, soll gefährlich sein? Wer es schafft, sich damit
in den Fuß zu schießen hat kein Problem mit Makros, der hat ein Problem
damit, überhaupt erstmal zu laufen ...
>> Noch ein Punkt der für Makros spricht. Denn tatsächlich will man eine>> solche Abstraktionsschicht auslagern. Klar kann man die Funktionen dann>> static definieren und in eine zentrale Headerdatei stecken. Aber IMHO>> ist das wieder nur ein Workaround den man eigentlich gar nicht bräuchte.>> Nein, man kann es doch auslagern ohne Makros. Dein Vorschlag mit Code in> der Headerdatei ist genau so Murks.
Blöderweise braucht es der Compiler so. In C gibt es kein verbindliches
Inlining. Wenn der Compiler sich entscheidet, die Funktion nicht zu
inlinen, kriegst du wunderschöne duplicate symbol Fehler von Linker wenn
du die Funktion nicht static definierst. Und mich würden derart
duplizierte inline-gemeint aber vom Compiler dann doch lieber
nicht-inline generierte static Funktionen schon irgendwie stören. Und
weil wir dabei sind: noch mehr würde mich stören, wenn der Compiler
statt des hinreichenden sbi/cbi da einen echten Call und eine Funktion
mit zig Taken Prolog und Epilog generieren würde. Da schreibe ich dann
doch lieber ein paar redundante Klammern in meine Makros.
Marc S. schrieb:> Ich schrieb:>> In C gibt es kein "inline">> Doch, seit C99
Ja, aber. Ja, gibt es. Aber die Semantik ist kaputt. Hatten wir gerade
nebenan. Man muß dann "static inline" schreiben. Sonst geht es karpott,
wenn dem Compiler gerade danach ist.
Und das ist der Haß schlechthin. Wenn sich mein Code auf einmal
verschieden verhält, je nachdem welche Laune der Compiler beim
Übersetzen gerade hatte ...
Axel S. schrieb:
irgendwas über Makros
Ach vergiss es. Es gibt keinen einzigen Grund Makros einzusetzen. Und
nur weil du bei deinem Spezialfall keinen Grund dagegen siehst, sind sie
noch lange nicht besser als das entsprechende Konstrukt in C. Makros
sind eben zu vermeiden. Gerade dann wenn sie einfach ersetzbar sind. Die
Gründe kannst du oben nachlesen. Wie ich schon oben schrieb, finde ich
eine SetErrorLed(uint8_t state) Funktion sauberer und da kann man eben
wieder die Typsicherheit gebrauchen.
Übrigens: Spätestens seit LTO kann man diese Funktionen auch in normale
C-Dateien packen und der Linker optimiert das genau so weit wie Makros
oder eben static inline Funktionen. Viel sauberer als dein Makrozeugs.
Stefan U. schrieb:> ich möchte gerne den Zugriff auf I/O Pins gut leserlich schreiben. Ist> der folgende Code fachlich korrekt?> [...]> Was haltet ihr davon?
Ich persönlich finde dieses Mixen von #define und Funktionsaufruf nicht
sehr elegant, weil die Funktionssignatur mit drei Parametern, der Aufruf
jedoch mit nur zwei Parametern arbeitet. Mit C++11 (ja, ich habe Jehova
gesagt) und einer Struktur geht das IMHO übersichtlicher:
1
#include<stdint.h>
2
#include<avr/io.h>
3
4
typedefstruct{
5
volatileuint8_t*port;
6
uint8_tbit;
7
}portbit_t;
8
9
inlinevoidwritePin(portbit_tpb,uint8_tvalue){
10
if(value>0){
11
*pb.port|=(1<<pb.bit);
12
}else{
13
*pb.port&=~(1<<pb.bit);
14
}
15
}
16
17
intmain(void){
18
19
portbit_tled3{&PORTA,1};
20
21
while(1){
22
writePin(LED3,1);
23
writePin(LED3,0);
24
}
25
}
Beim Herumspielen mit Deinem Code ist mir aufgefallen, daß der gcc die
writePin()-Funktion nur dann wegoptimiert, wenn -flto eingeschaltet ist.
Dein Code kompiliert ohne -flto zu 158 Byte, mit zu 118 Byte. Bei meinem
Code wird die Funktion auch ohne -flto wegoptimiert, so daß er immer zu
118 Byte kompiliert, egal ob mit oder ohne -flto.
Außerdem haben unsere Codes einen dummen Fehler, der mir aufgefallen
ist, als ich mit C++-Klassen für Pins und Register gespielt und bemerkt
habe, daß der generierte Code zwei Byte größer ist als jener für: der
Pin wird dabei nämlich gar nicht als Output gesetzt. Das macht die
C++-Klasse für den Pin jedoch im Konstruktor automatisch und kompiliert
daher, egal ob mit oder ohne -flto, zu denselben 120 Bytes, zu denen
auch die vorherigen Codes kompilieren, wenn wir den Fehler beheben
würden:
1
#include<Pin.hpp>
2
#include<Reg.hpp>
3
4
intmain(void){
5
6
OutputPinled3(PINDEF(A,1));
7
8
while(true){
9
led3.setHigh();
10
led3.setLow();
11
}
12
13
return0;
14
}
Das erscheint mir persönlich nicht nur die lesbarste, sondern auch die
am wenigsten fehleranfällige Version zu sein -- so ein Konstruktor hat
schon den durchaus beabsichtigten Vorteil, daß man sich um die
Initialisierung keine großen Gedanken mehr machen muß. YMMV. ;-)
PS: Die Headerdateien "Reg.hpp" und "Pin.hpp" findest Du im Anhang. Die
Größen wurden mit avr-size ermittelt, kompiliert wurde mit "-Os
-mmcu=atmega32", mit und ohne "-flto" sowie für C++ mit "-std=c++11".
@ Axel Schwenke (a-za-z0-9)
Ich halte es im Gegenteil für ausgesprochenen Humbug, Abstraktionen und
>APIs aufeinander zu stapeln wie Bauklötze. Das sehe ich viel zu häufig>(und es ist in vielen Fällen Ursache von Performanceproblemen).
MEINE REDE!!!
K.I.S.S.
Falk B. schrieb:> @ Axel Schwenke (a-za-z0-9)>> Ich halte es im Gegenteil für ausgesprochenen Humbug, Abstraktionen und>>APIs aufeinander zu stapeln wie Bauklötze. Das sehe ich viel zu häufig>>(und es ist in vielen Fällen Ursache von Performanceproblemen).>> MEINE REDE!!!>> K.I.S.S.
Sehe ich genauso.
Gerade die Makro-Definitionen vereinfachen das Ganze. Solche Dinge wie
einfache Portzugriffe bleiben damit überschaubar. Probleme mit dem
Debugger gibt es auch keine, da man solche Einzeiler, die ein Bit setzen
oder löschen, gar nicht debuggen braucht.
Mehrere aufeinander gestapelte Abstraktionsschichten hingegen vernebeln
einen einfachen Portzugriff derart, dass man dann tatsächlich einen
Debugger braucht, um herauszubekommen, ob das verdammte Ding das Bit nun
wirklich setzt oder nicht.
Stefan U. schrieb:> Ja schön, aber jetzt sind wir bei C++, da wollte ich eigentlich nicht> hin.
Du wolltest doch die Zugriffe leserlicher machen, und das geht mit C++
nun einmal ganz besonders gut. ;-)
Frank M. schrieb:> Mehrere aufeinander gestapelte Abstraktionsschichten hingegen vernebeln> einen einfachen Portzugriff derart, dass man dann tatsächlich einen> Debugger braucht, um herauszubekommen, ob das verdammte Ding das Bit nun> wirklich setzt oder nicht.
Jaja, also du meinst tatsächlich dass ein
#define name xxx
weniger fehleranfällig ist als das:
void name(void)
{
xxx
}
Im Übrigen hast du das Prinzip der Abstraktion nicht verstanden. Daher
müssen wir hier gar nicht erst weiterdiskutieren. Bei mehreren
Abstraktionsschichten kann mn kann jede Schicht einzeln für sich testen.
Man kann sogar die unterste durch eine andere ersetzen und das ganze am
PC testen. Aber laut dir hat das ganze ja keine Vorteile und weil man
LTO deaktiviert, merkt man auch nicht dass es genau so schnell wie
andere Lösungen ist.
Mir geht es um sauberes und einheitliches Softwaredesign. Und dabei
sollten die Portzugriffe auf die gleiche Art wie andere Peripherie
abstrahiert werden. Einheitlich bedeutet auch, dass sie keine
Extrawürste mit Makros bekommen. Das braucht man auch gar nicht, da die
einheitliche Lösung keine Nachteile hat. Auch keine
Geschwindigkeitseinbußen. Der GCC kann ohne Probleme mit LTO mehrere
Schichten zusammenfassen.
Bei kleinen Projekten kann man natürlich alles machen. Aber sobald das
Projekt größer wird, bietet eine sauber aufgebaute Abstraktionsschicht
eine Menge an Vorteilen.
Naja, man kann ja statt setHigh/setLow auf setOn/setOff umstellen und
dann zwei unterschiedliche Klassen verwenden, die das korrekt auf die HW
umsetzen.
avr schrieb:> #define name xxx>> weniger fehleranfällig ist als das:>> void name(void)> {> xxx> }
ich bin hin und hergerissen, beides hat seinen Charme, was besser ist
müssen die Profiprogger entscheiden.
Frank M. schrieb:> avr schrieb:>> Daher müssen wir hier gar nicht erst weiterdiskutieren.>> Freut mich. Bin nämlich gerade nicht in Stimmung.
aber die wollen nicht.......
als ich noch "ProfiProgger" war hatte ich Projekte am DOS Rechner in C
und habe die zu Hause auch weiterprogrammiert wenn es zu Abgabe eng
wurde, @work auf dem PC, @home auf dem AtariST, weil aber daheim die
Testumgebung fehlte, ein ganzer Prüfturm mit Messgeräte musste ich mir
Dummys einfallen lassen, das ging in Unteroutinen besser als in
#defines.
Die Unteroutinen (functions()) konnten bleiben wie sie sind, @work haben
sie Messwerte aus dem Gerät geliefert, @home eben Dummyausgaben.
von daher würde ich > void name(void) bevorzugen und im > void
name(void) #defines @home or NOT
LG jar
Stefan U. schrieb:> inline void writePin(volatile uint8_t* port, uint8_t bit, uint8_t value)Stefan U. schrieb:> Naja, man kann ja statt setHigh/setLow auf setOn/setOff umstellen und
Tja, wenn du deine Firmware(n) eben genau so aufbauen willst, dann mußt
du es halt so tun - ABER: ich halte eigentlich garnichts von sowas.
Wozu sollte eigentlich eine Funktion gut sein, die aus einem fetten Satz
Argumente heraus nix weiter tut als ein Portpin hi oder lo zu stellen?
Das ist doch keine Hardware-Abstraktion, sondern nur eine
Verbürokratisierung.
Mein Stil ist es eher, Funktionen zur Hardware-Abstraktion zu schreiben,
die diesen Namen auch tatsächlich verdienen. In deren Headerfile kommt
dann ein Portpin überhaupt nicht mehr vor.
Also nicht writePin(welches, wie, wohin) sondern etwa sowas:
extern bool AnlasserStarten(void);
extern bool Gangeinlegen(int welcher);
extern bool InitSerial(int id_Port,long baudrate);
extern bool IsCharAvailable(int id_Port);
extern char SendChar(int id_Port, char c);
na und so weiter.
Also problembezogene Schnittstellen zum eigentlichen Hauptprogramm hin,
damit man sich dort nicht über so niedere Dinge wie das Setzen eines
Pins kümmern muß. Sowas macht das Ganze auch portabel, denn wenn man die
niedrigste Treiberschicht an eine andere Architektur angepaßt hat, dann
kann man sich auf sein eigentliches Vorhaben konzentrieren. Mit
irgendwelchen writePin(xyz) in höheren Schichten der Firmware oder gar
in main geht das nicht.
W.S.
> Das ist doch keine Hardware-Abstraktion
Danach hatte ich auch gar nicht gefragt. Mir ging es darum, die Zeilen
besser lesbar zu machen. Lies nochmal meinen initialen Beitrag.
Stefan U. schrieb:> Danach hatte ich auch gar nicht gefragt. Mir ging es darum, die Zeilen> besser lesbar zu machen. Lies nochmal meinen initialen Beitrag.
Warum ist denn für dich z.B.
1
PORTB|=(1<<PB1);
nicht gut lesbar? PORTB und PB1 kann man sich ja auch durch #defines
umdefinieren? Da habe ich bisher noch keine Ambitionen verspürt hier was
zu ändern.
1
.
2
.
3
.
4
#define STATUSPORT PORTB
5
#define STATUSLED1 PB3
6
.
7
.
8
.
9
STATUSPORT|=(1<<STATUSLED1);
10
.
11
.
12
.
13
STATUSPORT&=~(1<<STATUSLED);
14
.
15
.
16
.
Das finde ich eigentlich sehr gut lesebar und ist auf viele verschiedene
Fälle anwendbar.
Stefan U. schrieb:> Danach hatte ich auch gar nicht gefragt.
Erstens hast du gefragt "Was haltet ihr davon?" und zweitens klingt dein
letzter Beitrag reichlich unverschämt.
Michael K. schrieb:> STATUSPORT &= ~(1 << STATUSLED);> Das finde ich eigentlich sehr gut lesebar und ist auf viele verschiedene> Fälle anwendbar.
Ja, lesbar ist das schon, aber du mußt dich jedesmal daran erinnern, ob
deine Statusled gegen VCC oder gegen GND geht oder ob davor noch ein
Transistor ist und so weiter.
Sowas wie "void Led_ein(void)" (notfalls als inline) ist da ein ganzes
Stück lesbarer, weil es dir gestattet, dich auf deine eigentlichen
Probleme zu konzentrieren ohne dir Gedanken zu machen, ob du nun Hi oder
Lo applizieren mußt, um die verdammte Lampe einzuschalten.
W.S.
W.S. schrieb:> Ja, lesbar ist das schon, aber du mußt dich jedesmal daran erinnern, ob> deine Statusled gegen VCC oder gegen GND geht oder ob davor noch ein> Transistor ist und so weiter.
Das hatte weiter oben schon mal jemand geschrieben:
1
.
2
.
3
.
4
#define STATUSLED_EIN (PORTB |= (1 << PB1))
5
#define STATUSLED_AUS (PORTB &= ~(1 << PB1))
6
.
7
.
8
.
So kann man es auch machen wenn es beliebt. Aber: Wenn ich ja schon
vorher weiß wie mein Code zum Ein- und Ausschalten ausschaut (und das
weiß man immer) kann ich ja die Hardwareverdrahtung entsprechend
erstellen sodass z.B. ein Pin auf High schalten immer Einschalten
bedeutet (das ist in meinen Projekten in 100% aller Anwendungen der
Fall).
@W.S. (Gast)
>> STATUSPORT &= ~(1 << STATUSLED);>> Das finde ich eigentlich sehr gut lesebar und ist auf viele verschiedene>> Fälle anwendbar.>Ja, lesbar ist das schon, aber du mußt dich jedesmal daran erinnern, ob>deine Statusled gegen VCC oder gegen GND geht oder ob davor noch ein>Transistor ist und so weiter.
Eben. Der Sinn von Kapselung und Abstraktion ist, das man sich als
"Anwender" von Funktionen, und seinen sie noch so trivial, eben NICHT
darum kümmern muss WIE etwas gemacht wird sondern einfach nur sagt WAS
gemacht werden soll.
STATUS_LED_ON
Mehr will ich gar nicht wissen und mich um nichts weiter kümmern!
@Michael Köhler (sylaina)
>So kann man es auch machen wenn es beliebt. Aber: Wenn ich ja schon>vorher weiß wie mein Code zum Ein- und Ausschalten ausschaut (und das>weiß man immer) kann ich ja die Hardwareverdrahtung entsprechend>erstellen sodass z.B. ein Pin auf High schalten immer Einschalten>bedeutet (das ist in meinen Projekten in 100% aller Anwendungen der>Fall).
Nö! Genau anders herum wird ein Schuh draus! Die Hardware baut man so,
wie es für die Hardware am besten ist, ggf. auch mit wilden
Pinkreuzungen etc. Bestimmte Pegel kann man nicht immer selber
festlegen, die sind von den äußeren Schaltungen abhängig. Und in
Software kann man das bliebig schalten und abstrahieren! OK, es gibt
Ausnahmen, wo die Hardware passend zu Software gemacht werden
muss/sollte. Z.B. ist ein SPI oder UART an feste Pins gebunden, eine
Softwareemulation ist möglich aber deutlich leistungsschwächer.
Ähnliches gilt für Timer- und Interrupteingänge, Output Compare, Input
Capture etc.
Ausserdem, die typische Schreibweise
PORTx = (1<<PIN);
ist natürlich auf dem AVR gewachsen, weil die Includefiles für ASM und C
gemeinsam benutzt wurden. Ein
PORTx = PA0;
wobei PA0 als (1<<0) definiert ist, ist übersichtlicher, einfacher und
wenige Schreibarbeit. In C braucht man die BITNUMMER so gut wie nie, die
war nur beim AVR ASM für die Bitbefehle sbi/cbi bzw. sbic/sbis etc.
nötig.
Zum Glück ist das beim ATXmega mittlerweile anders, dort hat man die
Bitmasken bzw. Gruppenmasken für die Bits. Hier mal ein Ausschnitt aus
der
iox128a1.h
1
#define PORT_OPC_gm 0x38 /* Output/Pull Configuration group mask. */
2
#define PORT_OPC_gp 3 /* Output/Pull Configuration group position. */
3
#define PORT_OPC0_bm (1<<3) /* Output/Pull Configuration bit 0 mask. */
4
#define PORT_OPC0_bp 3 /* Output/Pull Configuration bit 0 position. */
5
#define PORT_OPC1_bm (1<<4) /* Output/Pull Configuration bit 1 mask. */
6
#define PORT_OPC1_bp 4 /* Output/Pull Configuration bit 1 position. */
7
#define PORT_OPC2_bm (1<<5) /* Output/Pull Configuration bit 2 mask. */
8
#define PORT_OPC2_bp 5 /* Output/Pull Configuration bit 2 position. */
Falk B. schrieb:> STATUS_LED_ON>> Mehr will ich gar nicht wissen und mich um nichts weiter kümmern!
Ich will nicht mal wissen, ob die LED an einem Portpin hängt oder ob ein
Bit in einem Array gesetzt wird (das gelegentlich in ein Schieberegister
rausgetaktet wird) oder ob ein printf("status_led 1\n") dahintersteckt,
wenn ich die Logik vorher auf dem PC teste.
@W.S.
>> Danach hatte ich auch gar nicht gefragt. Mir ging es darum, die Zeilen>> besser lesbar zu machen. Lies nochmal meinen initialen Beitrag.> klingt dein letzter Beitrag reichlich unverschämt.
Was ist daran unverschämt? Verstehe ich nicht.
Falk B. schrieb:> Die Hardware baut man so,> wie es für die Hardware am besten ist
Ja genau, und da ich weiß wie mein µC arbeitet bin ich immer bemüht es
so zu bauen, dass ein High auch einschaltet und ein Low ausschaltet.
Falk B. schrieb:> Bestimmte Pegel kann man nicht immer selber> festlegen, die sind von den äußeren Schaltungen abhängig.
Wenn das mal nicht geht kann ich immer noch die #defines tauschen, wohl
wahr.
Okey, ich bin auch nicht soo der Hardware/Software-Entwickler, ich mach
das eher nur im Bastelkeller und nur ab und an auch mal im Beruf zur
Zeit.
Peter D. schrieb:> Sheeva P. schrieb:>> led3.setHigh();>> led3.setLow();>> Hier hat man aber wieder das Problem ist nun setHigh LED an oder aus?
Da hast Du Recht, aber dieses Problem hast Du mit "PORTA |= ~(1 << PA3)"
doch genauso -- und da siehst Du nichtmal, was an PA3 hängt. Allerdings
erhöht es die Lesbarkeit in Deinem Sinne und kostet gar nichts, es noch
ein wenig hübscher zu gestalten:
Michael K. schrieb:> Stefan U. schrieb:>> Danach hatte ich auch gar nicht gefragt. Mir ging es darum, die Zeilen>> besser lesbar zu machen. Lies nochmal meinen initialen Beitrag.>> Warum ist denn für dich z.B.>>
1
>PORTB|=(1<<PB1);
2
>
>> nicht gut lesbar?
Ich kann (und will) nicht für Stefan sprechen, aber daran ist eigentlich
so ziemlich alles unlesbar. Was sind "PORTB" und "PB1"? Ok, aus dem
Kontext von AVRs weiß ich, daß es dabei um einen Port und einen Pin
geht, aber für einen der noch nie mit AVRs zu tun hatte, ist das völlig
unverständlich. Und die Bitoperationen in C sind einfach potthäßlich,
weshalb viele -- und auch die Autoren der AVR-Libc anfangs -- sie in
Funktionen oder Makros wie sbi() und cbi() verpacken. Ohne Schaltplan,
Datenblatt und Übung mit C-Bitoperationen versteht dabei niemand, was
dort passiert.
Bei
1
led3.on()
versteht jeder sofort, daß dort offensichtlich die LED mit der Nummer 3
eingeschaltet wird. Man kann die Instanz dabei sogar noch sprechender
nach ihrer Funktion benennen, etwa "ledError". Ein
1
ledError.on();
versteht sogar jemand, der noch nie eine Zeile C oder C++ geschrieben
hat -- und zwar sogar ohne Schaltplan oder Datenblatt.
Sheeva P. schrieb:> Was sind "PORTB" und "PB1"? Ok, aus dem> Kontext von AVRs weiß ich, daß es dabei um einen Port und einen Pin> geht, aber für einen der noch nie mit AVRs zu tun hatte, ist das völlig> unverständlich.
Jemand, der sich noch nie mit AVRs und Co beschäftigt hat wird auch
nicht wissen was mit ADC und Co gemeint ist. Ich glaube nicht, dass sich
jemand, der noch nie mit Mikrocontrollern gearbeitet hat, alleine am
Code sehen wird was was ist. Ganz egal wie leserlich man es gestaltet.
Ein bisschen Grundwissen gehört schon dazu und zumindest das Datenblatt
des Mikrocontrollers gehört dann daneben und dann sieht man auch als
Unwissender sehr schnell was PORTB und PB1 bedeutet. Diese Bezeichnungen
sind ja nicht aus der Luft gegriffen sondern kommen aus dem Datenblatt
;)
Michael K. schrieb:> Ein bisschen Grundwissen gehört schon dazu und zumindest das Datenblatt> des Mikrocontrollers gehört dann daneben und dann sieht man auch als> Unwissender sehr schnell was PORTB und PB1 bedeutet.
Um zu verstehen, was
1
errorled_on()
oder
1
errorled(ON)
bedeutet, muß ich noch nicht mal wissen, ob der Prozessor möglicherweise
mit Dampf betrieben wird.
MfG Klaus
Klaus schrieb:> bedeutet, muß ich noch nicht mal wissen, ob der Prozessor möglicherweise> mit Dampf betrieben wird.
Das muss ich hier:
Michael K. schrieb:> .> #define STATUSLED_EIN (PORTB |= (1 << PB1))> #define STATUSLED_AUS (PORTB &= ~(1 << PB1))> .
auch nicht aber du musst wissen was eine Error-LED ist und was es
bedeutet wenn da ON oder OFF steht. ;)
Michael K. schrieb:> Klaus schrieb:>> bedeutet, muß ich noch nicht mal wissen, ob der Prozessor möglicherweise>> mit Dampf betrieben wird.> Das muss ich hier:> Michael K. schrieb:>> .>> #define STATUSLED_EIN (PORTB |= (1 << PB1))>> #define STATUSLED_AUS (PORTB &= ~(1 << PB1))>> .> auch nicht aber du musst wissen was eine Error-LED ist und was es> bedeutet wenn da ON oder OFF steht. ;)
Was ist mit:
1
if(PORTB==STATUSLED_EIN)
2
{
3
;
4
}
Es könnte ja auch sein dass das makro einen Status oder nur eine Zahl
ist oder eine Maske.
Wenn das ein Makro ist, meckert da kein Compiler.
Wenn es eine void Funktion ist dagegen schon, mal davon abgesehen, dass
man es an den Klammern () sehen würde, die bei der Funktion nicht fehlen
dürfen.
-> Funktion für eine Funktion nehmen und kein Makro
Michael K. schrieb:> Michael K. schrieb:>> .>> #define STATUSLED_EIN (PORTB |= (1 << PB1))>> #define STATUSLED_AUS (PORTB &= ~(1 << PB1))>> .
Ich hätte gerne mal ein richtiges Argument für Makros... Warum sollte
man überhaupt Makros verwenden, wenn man sie durch Funktionen ersetzen
kann, die keinen Nachteil haben? Ich kann die API meines HAL in einer
Headerdatei definieren und dann je nach Plattform die c-Dateien
austauschen. Das ist für mich ein elegantes und einheitliches Design.
Geschwindigkeit und Speicherbedarf ist mit LTO nicht mehr.
In C haben meiner Meinung nach Makros eigentlich nur sehr sehr selten
etwas verloren. Für Konstanten, weil es kein richtiges const gibt, und
für Assertion, weil man da _LINE__ und __FILE_ nutzen kann.
Argumente wie Debuggen braucht man nicht und Typsicherheit auch nicht,
sind keine! Deswegen sind Makros nicht besser.
avr schrieb:> Ich hätte gerne mal ein richtiges Argument für Makros... Warum sollte> man überhaupt Makros verwenden, wenn man sie durch Funktionen ersetzen> kann, die keinen Nachteil haben?
Weil schon in dieser Annahme ein Fehler steckt. Funktionen haben
Nachteile. Z.B. daß man sie in C nicht zuverlässig inlinen kann. Und nur
weil avr-gcc jetzt endlich(!) auch LTO kann, kann das noch lange nicht
jede andere C-Toolchain auch. Womöglich weiß auch schon die IDE nichts
davon und schaltet es nicht ein.
Natürlich gibt es Gründe die gegen bestimmte Arten von Makros sprechen.
Makros mit Parametern. Makros die zu größeren Mengen Code expandieren.
Nur trifft eben keiner dieser Gründe auf die Art von Makros zu die wir
hier diskutieren. Ich finde es engstirnig, Makros zwanghaft zu
vermeiden. Warum sollte man sich freiwillig eines so mächtigen
Sprachmittels berauben?
Für mich ist es vielmehr diese irrationale generelle Abneigung gegen die
Verwendung von Makros, die einer Erklärung bedarf.
Axel S. schrieb:> Für mich ist es vielmehr diese irrationale generelle Abneigung gegen die> Verwendung von Makros, die einer Erklärung bedarf.
Makro ist halt ne andere Programmiersprache als C. Sie ist eigentlich
nicht wirklich brauchbar, da sie als Sprache nicht turing-vollständig
ist. Und warum sollte man in zwei Sprachen C und Makro gleichzeitig
programmieren, wenn man alles zwar in C aber nicht alles in Makro
erledigen kann.
Und wen interresiert schon "inline" beim Programmieren? Die Vorstellung,
daß man die Ablaufgeschwindigkeit von Software damit merklich
beinflussen kann, stammt doch aus der Steinzeit. Die µCs mit denen ich
zu tun habe, trommeln 16 Bit breit mit 70MHz, das ist mindestens 10 Mal
so schnell, wie ein original IBM AT. Und der war gut für Wordprozessing,
Spreadsheets und auch CAD SW. Und selbst ein ESP8266, der zusammen mit
seinem Flashspeicher weniger als 2€ kostet, hat mindestens 40MHz und 32
Bit.
Taktgenaues Programmieren ist bei den modernen Strukturen mit
unterschiedlichen internen Bussen und Caches sowieso nicht möglich. Die
überschüssige Power, die moderne µCs haben, setze ich für saubere,
leicht nachvollziehbare Softwarestrukturen ein. Ne Funktion ist halt ne
Funktion, mit allen ihren Eigenschaften wie lokalen Variablen und der
Möglichkeit, sie rekursiv einzusetzen. Sie ist getrennt übersetzbar und
kann auch in einer Objektlibrary stecken. Da passt "inline" nun
garnicht.
MfG Klaus
@ Klaus (Gast)
>Und wen interresiert schon "inline" beim Programmieren?
Leute, die schnelle Low Level Aufgaben bearbeiten.
> Die Vorstellung,>daß man die Ablaufgeschwindigkeit von Software damit merklich>beinflussen kann, stammt doch aus der Steinzeit.
Nö, die stimmt heute immer noch. Vor allem weil man das GEGENTEIL jeden
Tag sieht, wo auch im Profibereich nur allzuoft MASSIV CPU Power sinnlos
verbrannt wird. Nein, ich bin kein ASM-Prediger.
> Die µCs mit denen ich>zu tun habe, trommeln 16 Bit breit mit 70MHz, das ist mindestens 10 Mal>so schnell, wie ein original IBM AT. Und der war gut für Wordprozessing,>Spreadsheets und auch CAD SW. Und selbst ein ESP8266, der zusammen mit>seinem Flashspeicher weniger als 2€ kostet, hat mindestens 40MHz und 32>Bit.
Schön, aber darum muss man CPU-Power nicht sinnlos verheizen.
>Taktgenaues Programmieren ist bei den modernen Strukturen mit>unterschiedlichen internen Bussen und Caches sowieso nicht möglich.
Stimmt. Und in den meisten Fällen auch gar nicht nötig, auch nicht auf
kleinen Architekturen. ABER!!!
> Die>überschüssige Power, die moderne µCs haben, setze ich für saubere,>leicht nachvollziehbare Softwarestrukturen ein.
Dagegen hat doch keiner argumentiert. Es ging hier vor allem um KLEINSTE
Funktionalitäten und Makros!
> Ne Funktion ist halt ne>Funktion, mit allen ihren Eigenschaften wie lokalen Variablen und der>Möglichkeit, sie rekursiv einzusetzen. Sie ist getrennt übersetzbar und>kann auch in einer Objektlibrary stecken. Da passt "inline" nun>garnicht.
Das ist Schwarz-Weiß Denken. Nix für mich.
Klaus schrieb:> Axel S. schrieb:>> Für mich ist es vielmehr diese irrationale generelle Abneigung gegen die>> Verwendung von Makros, die einer Erklärung bedarf.>> Makro ist halt ne andere Programmiersprache als C.
Häh? Makros sind keine eigene Programmiersprache, sondern ein fester
Bestandteil von C. Kennst du dein Werkzeug eigentlich?
> Und warum sollte man in zwei Sprachen C und Makro gleichzeitig> programmieren, wenn man alles zwar in C aber nicht alles in Makro> erledigen kann.
Ich lese das jetzt einfach mal als "warum sollte man in C Makros
verwenden, wenn man es auch ohne machen könnte?". Und dann steht die
Antwort oben in meinem Post.
> Und wen interresiert schon "inline" beim Programmieren?
Jeden? Also zumindest jeden, der nicht 90% (oder mehr) der verfügbaren
Performance wegwerfen will. Und zwar ohne guten Grund wegwerfen.
Zähl halt einfach mal die CPU-Zyklen für ein SBI im Vergleich zu einem
Funktionsaufruf. Womit rechtfertigst du gleich nochmal, daß das jetzt 20
mal so lange dauert? Und welche Garantie kannst du mir eigentlich dafür
geben, daß du an anderer Stelle nicht genau so liederlich mit Ressourcen
umgehst?
> überschüssige Power, die moderne µCs haben, setze ich für saubere,> leicht nachvollziehbare Softwarestrukturen ein. Ne Funktion ist halt ne> Funktion, mit allen ihren Eigenschaften wie lokalen Variablen und der> Möglichkeit, sie rekursiv einzusetzen. Sie ist getrennt übersetzbar und> kann auch in einer Objektlibrary stecken. Da passt "inline" nun> garnicht.
Aha. Du hast also nicht nur Makros nicht verstanden, sondern verstehst
auch "inline" nicht. Jeder blamiert sich halt so gut wie er kann ...
Falk B. schrieb:> @ Klaus (Gast)>>>Und wen interresiert schon "inline" beim Programmieren?>> Leute, die schnelle Low Level Aufgaben bearbeiten.
Jajaja, mit LTO kein Problem. Außerdem gehören zeitkritische Sachen
DIREKT in das HAL.
>> Die Vorstellung,>>daß man die Ablaufgeschwindigkeit von Software damit merklich>>beinflussen kann, stammt doch aus der Steinzeit.>> Nö, die stimmt heute immer noch. Vor allem weil man das GEGENTEIL jeden> Tag sieht, wo auch im Profibereich nur allzuoft MASSIV CPU Power sinnlos> verbrannt wird. Nein, ich bin kein ASM-Prediger.
Anscheinend doch. Es wird nämlich KEINE CPU Power verbrannt. NULL.
>> Die µCs mit denen ich>>zu tun habe, trommeln 16 Bit breit mit 70MHz, das ist mindestens 10 Mal>>so schnell, wie ein original IBM AT. Und der war gut für Wordprozessing,>>Spreadsheets und auch CAD SW. Und selbst ein ESP8266, der zusammen mit>>seinem Flashspeicher weniger als 2€ kostet, hat mindestens 40MHz und 32>>Bit.>> Schön, aber darum muss man CPU-Power nicht sinnlos verheizen.
Tut man auch nicht.
>>Taktgenaues Programmieren ist bei den modernen Strukturen mit>>unterschiedlichen internen Bussen und Caches sowieso nicht möglich.>> Stimmt. Und in den meisten Fällen auch gar nicht nötig, auch nicht auf> kleinen Architekturen. ABER!!!>>> Die>>überschüssige Power, die moderne µCs haben, setze ich für saubere,>>leicht nachvollziehbare Softwarestrukturen ein.>> Dagegen hat doch keiner argumentiert. Es ging hier vor allem um KLEINSTE> Funktionalitäten und Makros!
Ja und? Die Funktionen haben keinen Nachteil. Absolut keinen.
>> Ne Funktion ist halt ne>>Funktion, mit allen ihren Eigenschaften wie lokalen Variablen und der>>Möglichkeit, sie rekursiv einzusetzen. Sie ist getrennt übersetzbar und>>kann auch in einer Objektlibrary stecken. Da passt "inline" nun>>garnicht.>> Das ist Schwarz-Weiß Denken. Nix für mich.
Aha. Und dein Makroonly Denken ist nicht schwarz-weiß?
Axel S. schrieb:> Jeden? Also zumindest jeden, der nicht 90% (oder mehr) der verfügbaren> Performance wegwerfen will. Und zwar ohne guten Grund wegwerfen.
90%? In jedem meiner Programme machten Portzugriffe nicht einmal 1% aus.
Axel S. schrieb:> Weil schon in dieser Annahme ein Fehler steckt. Funktionen haben> Nachteile. Z.B. daß man sie in C nicht zuverlässig inlinen kann.
Das kann man beim gcc schon. Und bei anderen besseren Compilern
sicherlich auch. Man muss nur sein Tool kennen.
> Und nur> weil avr-gcc jetzt endlich(!)
Das geht schon länger.
> auch LTO kann, kann das noch lange nicht> jede andere C-Toolchain auch. Womöglich weiß auch schon die IDE nichts> davon und schaltet es nicht ein.
Dann kennst du dein Tool wohl nicht. Das ist doch kein Argument.
> Für mich ist es vielmehr diese irrationale generelle Abneigung gegen die> Verwendung von Makros, die einer Erklärung bedarf.
Ich verwende durchaus Makros. Z.B. für Assertion. Das mache ich sogar in
C++. Aber an Stellen wo man keine Makros braucht, muss man sie nicht
nutzen. Es geht mir um eine eine einheitliche Softwarearchitektur, die
zu meinem HAL passt. Das vermeidet Fehler und macht die Architektur
übersichtlicher. Übersichtlichkeit vermeidet auch Fehler. Makros sind
aber eher Fehleranfällig. Es ist halt einfach eine Textersetzung. Und
spätestens wenn du mal im Safety-Bereich arbeitest, kann du dir deine
ganzen Makros sowieso sparen.
Also ich weiß ja nicht wie das bei euch ist aber bei mir stell ich den
avr-gcc mit dem Makefile ein und die IDE schubst nur das Makefile an.
However, meint ihr nicht dass ihr mit euerer Diskussion schon ziemlich
weit weg seit vom Threadthema?
Axel S. schrieb:>> Und wen interresiert schon "inline" beim Programmieren?>> Jeden? Also zumindest jeden, der nicht 90% (oder mehr) der verfügbaren> Performance wegwerfen will. Und zwar ohne guten Grund wegwerfen.> Zähl halt einfach mal die CPU-Zyklen für ein SBI im Vergleich zu einem> Funktionsaufruf.
Um bei deinen 90% zu bleiben: mehr als 90% des Codes eines modernen µC
gesteuerten Gerätes ist das Userinterface, wenn nicht mehr. Wieviel
Zyklen die CPU einer Waschmaschine braucht, die Laugenpumpe
einzuschalten, ist doch absolut unerheblich. Da wird mehr Zeit
verbraucht, das ganze Umfeld diese Vorgangs ins Log zu speichern.
> Womit rechtfertigst du gleich nochmal, daß das jetzt 20 mal so lange> dauert?
Es ist egal, es interessiert niemand, niemand merkt es. Es macht das
Produkt nicht besser, billiger sowieso nicht. Time to Market wird nicht
besser und als Alleinstellungsmerkmal kann man das dem Kunden noch nicht
mal erklären. Es streichelt höchsten das Ego des Programmierers.
Eigentlich sollte man es noch langsamer machen, in dem man nicht in C
programmiert, sondern eine wesentlich höhere, möglicherweise sogar
interpretierte Sprache einsetzt. Wo Geschwindigkeit bei der Software
wirklich wichtig ist, ist bei der Erstellung von möglichst fehlerfreiem,
portablen und wartbaren Code. Das bringt einem Time to Market,
Produktvielfalt und Weiterentwicklung mit geringstem Aufwand. Und da muß
man soviel Unterstützung von Tools in Anspruch nehmen, wie möglich. Und
jeder Trick, der diese Tools ausbremst, ist schädlich.
Un zu guter letzt: wenns mal richtig schnell gehen soll, wirklich
Real-Time, sind CPUs zu langsam. Cycle by Cycle Stromüberwachung in
einem Switcher, Umrichter o.ä. im Megaherz Takt braucht einen anderen
Ansatz, z.B. ein FPGA. Wärend die Bitfummler noch nach einem
verbesserten "SBI" suchen, verdoppelt sich gerade die Geschwindigkeit
der Leistungsschalter. Schon wieder zu langsam.
MfG Klaus
@ Klaus (Gast)
>Um bei deinen 90% zu bleiben: mehr als 90% des Codes eines modernen µC>gesteuerten Gerätes ist das Userinterface, wenn nicht mehr. Wieviel>Zyklen die CPU einer Waschmaschine braucht, die Laugenpumpe>einzuschalten, ist doch absolut unerheblich. Da wird mehr Zeit>verbraucht, das ganze Umfeld diese Vorgangs ins Log zu speichern.
Du lenkst ab! Das man auf den höheren Ebenen nicht zum CPU-Takt
Erbsenzähler werden muss ist unbestritten. Für eine Status-LED und
ähnlichen Kram auch, da kann man auch Arduino-Style digital_write()
nehmen!
Aber wenn es ans Eingemacht geht und es mal FLOTT gehen soll/muss,
sollte man schon effizient arbeiten.
>Eigentlich sollte man es noch langsamer machen, in dem man nicht in C>programmiert, sondern eine wesentlich höhere, möglicherweise sogar>interpretierte Sprache einsetzt.
Ich vermisse einen Smily.
>Wo Geschwindigkeit bei der Software>wirklich wichtig ist, ist bei der Erstellung von möglichst fehlerfreiem,>portablen und wartbaren Code. Das bringt einem Time to Market,>Produktvielfalt und Weiterentwicklung mit geringstem Aufwand.
Schon wieder Schwarz-Weiß Denken. Das ist auch der Grund, warum ein
Maustreiber heute 10MB++ groß ist . . .
>Un zu guter letzt: wenns mal richtig schnell gehen soll, wirklich>Real-Time, sind CPUs zu langsam. Cycle by Cycle Stromüberwachung in>einem Switcher, Umrichter o.ä. im Megaherz Takt braucht einen anderen>Ansatz, z.B. ein FPGA. Wärend die Bitfummler noch nach einem>verbesserten "SBI" suchen, verdoppelt sich gerade die Geschwindigkeit>der Leistungsschalter. Schon wieder zu langsam.
Jaja.
Falk B. schrieb:> Ich vermisse einen Smily.
Sollte auch keiner hin
Falk B. schrieb:>>Wo Geschwindigkeit bei der Software>>wirklich wichtig ist, ist bei der Erstellung von möglichst fehlerfreiem,>>portablen und wartbaren Code. Das bringt einem Time to Market,>>Produktvielfalt und Weiterentwicklung mit geringstem Aufwand.>> Schon wieder Schwarz-Weiß Denken.
Dann den letzten Satz noch mal etwas länger:
Das bringt einem Time to Market, Produktvielfalt und Weiterentwicklung
mit geringstem Aufwand und verdient so das Gehalt des Programmierers.
MfG Klaus
avr schrieb:>> Leider hat er recht. Auch wenn du das nicht glauben willst.
Na da haben sich ja die zwei Richtigen gefunden. Ihr seit genau die
Leute, denen wir Mobiltelefone "verdanken" die Vierkern-CPU mit
Gigahertz Takt brauchen und deren Akkus nur mit Mühe und Not einen Tag
halten. Aber es war ja ohnehin alles gesagt. Überlassen wir diesen
Thread dem Thread-Kaperer und dem Troll.
Ich bin dann mal weg
Axel S. schrieb:> Na da haben sich ja die zwei Richtigen gefunden. Ihr seit genau die> Leute, denen wir Mobiltelefone "verdanken" die Vierkern-CPU mit> Gigahertz Takt brauchen und deren Akkus nur mit Mühe und Not einen Tag> halten.
Dann mach es halt besser. Deine Software hätte bestimmt so viele Bugs
dass kein Mensch sie benutzen wollte. Ich schätze du hast einfach keine
Ahnung von komplexere Software. Der Linux Kernel alleine hat 20
Millionen LOC. Bugs zu vermeiden fängt schon bei einfachen Sachen an.
Z.B. ein einheitliches Design, das jeder auf den ersten Blick versteht.
Einheitlich bedeutet auch nicht Makros und Funktionen für die Hardware,
sondern nur Funktionen. Das beschleunigt auch das Codereview. Jede
Vereinfachung macht den Code verständlicher. Und damit sinken auch im
Schnitt die Bugs/LOC.
Die Pro Funktion, Pro SW-Design, Pro SW-Architecture, Pro einheitliche
Umsetzung Fraktion hat Recht.
Man hat relativ schnell festgestellt, das eine Software die performant
und ressourcensparend ist zwar entwickelt werden kann, aber in keinem
Verhaeltnis zum Aufwand und der Entwicklungszeit steht. Auch das
optimieren von komplexen System hat sich als grosser Aufwandtreiber
herausgestellt.
Heute ist dem Kunden eine 90% Loesung lieber als eine 100% Loesung die
dafuer doppelt so lange zum Entwickeln braucht und dreimal soviel
kostet.
Um jetzt dennoch ein hohes SW Niveau halten zu koennen werden die
Parameter fuer schnelles Entwickeln und Kostenreduktion optimiert.
Resultierend wird sehr viel Platformentwicklung betrieben, sowie dem zu
Grunde liegenden Code-Reuse. Weitere Methoden wie Continous Integration
und Automated Testing unterstuetzen genau diesen "neuen" Ansatz.
Was dazu kommt ist, dass Entwickler in einem Projekt oft wechseln. D.h.
Einarbeit ist ein ganz wichtiger Punkt heutzutage. Eine Software in der
ein neuer Mitarbeiter nach 3 Wochen schon produktiv arbeiten kann weil
das Konzept und die Ausfuehrung sehr einfach gestaltet ist hat eindeutig
Vorteile gegenueber einer Software die mehr Aufwand fuer die Einarbeit
braucht.
In den meisten SW lastigen Unternehmen sind diese Paradigmen schon
angekommen und werden gelebt. Aber wir stossen durchaus auch noch auf
kleinere Unternehmen (meist so ~10 Personen Unternehmen mit ueberwiegend
E-Technikern als SW Entwickler) die gerne performante,
ressourcensparende Software als Ziel haben. Diese Klagen IMMER, dass
der Kunde nicht verstehe das ordentliche SW Geld kostet und das die
Konkurenzsoftware "Murks" sei.
Diese Unternehemen werden aber immer weniger. Der Preisdruck und vor
allem mit der Geschwindigkeit mit der neue Features von der Konkurenz
entwickelt werden kann sind hier ausschlaggebend.
Alles in allem wird eine Software die Funktionen statt Macros benutzt
nicht die Welt veraendern. Aber der Gedanke dabei ist der Richtige.
Das ist der Hintergrund warum Themen wie
/Einheitlichkeit/Abstraktion/Kapselung/Portabilitaet hoeher gewichtet
werden als der schnellste, kleine Code. Ist im Moment der aktuelle Stand
der Technik und wird auch genau so hochschulseitig auch gelehrt.
mfg
euer G4st
Es ist mal wieder schön zu sehen, wie ihr groben Unfug (nach)labert.
Niemand hat behauptet, dass man auf eine ordentliche Softwarearchitektur
im Namen der Performance verzichten muss. Niemand hat behauptet, dass
Geschwindigkeit über alles geht. Trotzdem argumentiert ihr gegen diesen
Strohmann. Seid ihr so blöde, oder wollt ihr nur trollen?
Aber schießt mal weiter in den Nebel, so bindet ihr eure Zeit und stört
nicht woanders.