Hans-Georg Lehnard schrieb:> sonst hast du deinen mühsam gespaarten> Speicher schnell, durch eine lib die automatisch dazugelinkt wird,> wieder zunichte gemacht.
… wie z.B. bei GoTo ^^ mit "boost::thread". ;-)
Einige der "header-only Boost libraries" gibt es ja wohl auch für den
AVR, aber natürlich ohne boost::thread.
Ich wage mich da mit meinem ATtiny1634
ganz bestimmt nicht ran:
http://academic.cleardefinition.com/2012/09/21/using-c-on-the-arduino-a-mainstream-c-standard-library-port-for-the-avr/
Oder hat damit etwa schon mal jemand gute Erfahrungen gemacht?
Trotzdem nochmal: Falls jemand einen Vorschlag hat, wie man "class
Pin"^^ ohne Pointer machen könnte, das wäre dann noch eleganter.
Torsten C. schrieb:> Ralf G. schrieb:>> DDRx, PORTx, PINx sind keine Pointer>> Und wozu ist der Stern in "public: volatile uint8_t* ddr;"^^?>> Oder hast Du einen Vorschlag, wie man das ohne Pointer macht?
Wenn man hier auch folgendes verwenden kann:
1
volatileuint8_t*constddr;
Sind auch referenzen möglich:
1
volatileuint8_t&ddr;
PS: In dem Bespiel, welches ich ganz weit oben Postete, habe ich dafür
Referenzen verwendet.
kopfkratz
Also nochmal langsam und zum mitmeisseln (oder wird das mit ß
geschrieben ?) ...
Man muß die Abstraktionsstufen im Auge behalten.
Entweder man definert eine vorgegebene Struktur die immer die gleichen
Variablen nutzt aber für jeden µC entsprechend angepaßt ist (so wie ich
das schon erwähnt habe).
Oder man geht den Weg der Vererbung und muß dann für jeden µC die
passende Basisklasse schreiben ebenfalls wie im obigen Fall.
Der Weg über Templates ist der meiner Meinung nach sinnvollste und
flexibelste, denn dann spart man sich die Vererbung bzw. Einbindung
verschiedener Definitionen.
Damit kommt man aber von einer Basisdefinition des µCs auch nicht herum.
Von der Compilerseite könnte man das auch im makefile erledigen bzw.
beim linken.
Torsten C. schrieb:> Falls jemand einen Vorschlag hat, wie man "class> Pin"^^ ohne Pointer machen könnte, das wäre dann noch eleganter.
???
Na so:
Beitrag "Re: C++ auf einem MC, wie geht das?"
Die Registeradressen würde man allerdings über die Port-Adresse
ermitteln lassen. Den Rest macht der Compiler. Der kann das.
Vorteil:
- geht für alle AVRs einheitlich.
- es sind automatisch auch die mit im Boot, wo die Register nicht im
IO-Bereich liegen.
Ralf G. schrieb:> Ich habe> allerdings noch keinen Einfall, wie man solche Objekte universell in> andere Objekte einbinden kann (ohne diese <>-Klammerlisten).
Da wird man wohl nicht drum herumkommen...
Daniel A. schrieb:> Wenn man hier auch folgendes verwenden kann:
^^ Das alles sind Beispiele, wie sie schon seit Jahren immer einmal,
hier im µC-net vorgeschlagen/ diskutiert werden. Ob Pointer, Referenzen,
'const' oder nicht 'const'...
Ist egal. Kann man so machen. Macht man auch so! Wenn man weiß und das
toleriert, dass da nur direkte Zugriffe draus werden, wenn der Compiler
den kompletten Code sehen kann.
Hallo Torsten,
Torsten C. schrieb:> Nun habe ich nur noch ein Problem mit Instanzen statt Templates: Kann> man irgendwie verhindern, dass ein Pin doppelt instanziiert wird? Ich> kenne dafür nur Singletons. Sollte man versuchen, Singletons umzusetzen?
Ach, Torsten... Jedes Mal, wenn Du etwas idiotensicher machen willst,
dann kommt die Natur und erfindet bessere Idioten.
Kurz gesagt: Man kann nicht verhindern, daß Idioten idiotische Sachen
tun. Das ist auch nicht die Aufgabe einer Bibliothek. Was dabei heraus
kommt, wenn Du es versuchst, siehst Du an den Arduino-Libraries:
funktioniert zwar, ist aber ein riesiger Source- und Codebloat. Und
trotzdem gibt es genug Idioten, die idiotische Sachen damit machen --
schau mal in dieses Forum! ;-)
Mit dem Anspruch, jeden noch so bescheuerten Fehler mit Compiler- und
Sprachmitteln abzufangen, stehst Du Dir daher einerseits selbst im Weg,
andererseits verhinderst Du wahrscheinlich exotische Dinge, die wir uns
vielleicht nur nicht vorstellen können, die aber trotzdem sinnvoll sind.
Es ist nicht Deine Aufgabe, die Anwender Deiner Bibliothek zu
bevormunden, und darauf läuft die ganze Sache letztendlich hinaus (ja,
ich weiß, das ist nicht Deine Intention, aber "gut gemeint" ist
bekanntlich die kleine Schwester von "schlecht gemacht"). Wenn Anwender
idiotische Sachen machen wollen, ist das ihr gutes Recht. Etliche gute
Ideen und Produkte sind aus etwas entstanden, das andere zuvor als
sinnlos, idiotisch abgetan haben.
Ich schlage daher vor, daß wir diesen Quatsch endlich lassen und uns
darum kümmern, ordentlichen, funktionierenden Code zu schreiben -- und
zwar auch auf die Gefahr hin, daß Idioten nun einmal idiotische Sachen
damit tun.
Liebe Grüße,
Karl
>> Warum eigentlich drei getrennte Pointer? Einen einzigen fände ich
übersichtlicher.
Weil die Hardware der AVRs das nun einmal so abbildet. Ansonsten gibt es
in dem von mir geposteten Code ein sehr übersichtliches Makro, das
trotzdem für alle aktuellen und zukünftigen AVRs korrekt funktioniert.
Liebe Grüße,
Karl
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> Ich habe auch immer gedacht ich kann C++ und was soll das alles mit dem> C++11 das brauch ich doch nie aber genau hier für die Embedded> Programmierung kann man es sehr gut gebrauchen. Der Nachteil ist, es ist> nicht leicht verständlich und für Anfänger in C++ völlig ungeeignet. Die> Compiler Fehlermeldungen sind alles andere als verständlich und debuggen> im single-step lässt sich das erst recht nicht.
Für PC-Programme nutze ich mittlerweile statt des g++ den LLVM-Compiler
mit dem clang++-Frontend. Das wird genauso aufgerufen wie der g++ (mit
denselben Optionen und Schaltern), gibt aber sehr viel bessere
Fehlermeldungen aus und erzeugt in oft auch besseren (iSv. kleineren
oder schnelleren) Code.
HTH,
Karl
Hallo Torsten,
Torsten C. schrieb:> Ich meine, die Pointer müssen konstant sein, damit der Compiler da> wirklich zuverlässig z.B. "sbi 0x17, 3" draus macht und nicht nur> zufällig je nach Compiler-Optimizing-Level.
Guckstu avr/io.h: DDRD, PIND, PORTB und PB4 sind Konstanten!
HTH,
Karl
Hi Torsten,
Torsten C. schrieb:> Ralf G. schrieb:>> DDRx, PORTx, PINx sind keine Pointer>> Und wozu ist der Stern in "public: volatile uint8_t* ddr;"^^?
Rein technisch betrachtet ist ein Pointer (Zeiger) etwas, das auf einen
bestimmten Speicherbereich zeigt -- meistens ist das eine Variable, hier
aber eben nicht! Dieser Zeiger hier zeigt auf einen festen
Speicherbereich, nämlich ein Register, bei dem der Inhalt des
Speicherbereichs volatil, der Zeiger auf den Speicherbereich hingegen
konstant ist. Insofern ist das aus Sicht der Programmiersprache ein
konstanter Pointer auf etwas Verändliches, und gleichzeitig aus Sicht
der Hardware eine Registeradresse.
Liebe Grüße,
Karl
kopfkratzer schrieb:> *kopfkratz*> Also nochmal langsam und zum mitmeisseln (oder wird das mit ß> geschrieben ?) ...
Es geht etwas wirr durcheinander, aber wie soll man das verhindern ..
> Man muß die Abstraktionsstufen im Auge behalten.> Entweder man definert eine vorgegebene Struktur die immer die gleichen> Variablen nutzt aber für jeden µC entsprechend angepaßt ist (so wie ich> das schon erwähnt habe).> Oder man geht den Weg der Vererbung und muß dann für jeden µC die> passende Basisklasse schreiben ebenfalls wie im obigen Fall.> Der Weg über Templates ist der meiner Meinung nach sinnvollste und> flexibelste, denn dann spart man sich die Vererbung bzw. Einbindung> verschiedener Definitionen.> Damit kommt man aber von einer Basisdefinition des µCs auch nicht herum.> Von der Compilerseite könnte man das auch im makefile erledigen bzw.> beim linken.
Wenn wir das auf den "speziellen" Fall 8Bit,AVR,SFR und Port reduzieren
geht es einfach darum ein paar 8-bit Register zusammen zu fassen und sie
für jeden Port als konstant definieren.
Konstante haben aber die Eigenschaft, das sie sofort initialisiert
werden müssen, und deshalb kommen Konstruktoren nicht in Frage.
Es sind und bleiben aber immer Konstante die nur verschieden
initialisiert werden.
Beispiel:
const int x = 4711;
const int y = 4712;
würdest du da auch y von x erben lassen ?
Karl Käfer schrieb:> Hallo Hans-Georg,> Für PC-Programme nutze ich mittlerweile statt des g++ den LLVM-Compiler> mit dem clang++-Frontend. Das wird genauso aufgerufen wie der g++ (mit> denselben Optionen und Schaltern), gibt aber sehr viel bessere> Fehlermeldungen aus und erzeugt in oft auch besseren (iSv. kleineren> oder schnelleren) Code.>
Der gibt bei Template Fehlern sehr viel bessere Fehlermeldungen aus ?
Das würde mich sehr wundern.
Karl Käfer schrieb:>> Guckstu avr/io.h: DDRD, PIND, PORTB und PB4 sind Konstanten!>
Das sind Preprocessor Definitionen und die kümmern sich nicht um
Namespaces und kollidieren mit allem möglichen, deshalb in C++ eher
ungeeignet. Dafür gibt es const und constexpr.
Für #ifdef sind sie in Ordnung aber mehr nicht.
Hans-Georg Lehnard schrieb:> Konstante haben aber die Eigenschaft, das sie sofort initialisiert> werden müssen
Das stimmt, ist ein echter Vorteil (keine ironie)
> und deshalb kommen Konstruktoren nicht in Frage
Falsch, konstruktoren haben eine Initializer list! Das geht!
Daniel A. schrieb:> Hans-Georg Lehnard schrieb:>> Konstante haben aber die Eigenschaft, das sie sofort initialisiert>> werden müssen> Das stimmt, ist ein echter Vorteil (keine ironie)>> und deshalb kommen Konstruktoren nicht in Frage> Falsch, konstruktoren haben eine Initializer list! Das geht!
Hast Recht und mit constexpr Konstruktoren gehts auch.
Hans-Georg Lehnard schrieb:> Hast Recht und mit constexpr Konstruktoren gehts auch.
Genau das meinte ich oben mit "Das geht per constexpr wunderbar. Ich
hab's gerade mit avr GCC 4.7.2 probiert."
Cool, dann sind wir uns einig?
Ich frage mich jetzt gerade, ob und ggf. worüber eine weitere Diskussion
hier noch Sinn macht. Das ganze ist ja etwas "abstrus", weil sich der OP
PeDa ofenbar aus dem Thread zurück gezogen hat, und wir hier ein Stück
weit eine "Phantom-Diskussion" führen.
Ich habe dabei viel gelernt und habe hoffentlich hier und da auch
nützliche Beiträge geleistet.
Zum Thema uCpp Frank M. schrieb:> Ausserdem halte ich eine "plattformübergreifende Lösung" für> ein Unterfangen, dass sehr viel Arbeit bedeutet, bei dem das Ziel in so> weiter Ferne liegt, dass - wenn eine Lösung denn irgendwann existieren> sollte - die Hardware dafür dann gar nicht mehr existieren wird.> Kurz: ich halte diesen Anspruch für ein Fass ohne Boden.
Das mag - je nach Anspruch - darauf hinaus laufen und eine Fortführung
des Themas wäre in diesem Thread wahrscheinlich falsch platziert.
Gibt es Einsprüche? ;-)
TriHexagon schrieb:>> Chris D. schrieb:>>> trivial aussehende Probleme eben nicht trivial sind>>>> ... denn die von Peter D. skizzierten Problemchen SIND u.a. in Asm>> trivial zu lösen.>> Dann zeig doch mal.
Mach ich gerne noch wenn Bedarf besteht- obwohl solcherlei Trivialitäten
nun wirklich jeder mittelmäßige Asm-Progger hinbekommt. Denn man gewinnt
ja fast den Eindruck, daß manche C++ Experten vor dem Herrn scheinbar
gar keinen Schimmer mehr davon haben, wie die einfachste Problemlösung
für
LED0 = KEY0.state; // an solange Taste gedrückt
LED1.toggle = KEY1.press // wechsel bei jeder Drückflanke
LED2.off = KEY2.press_short; // kurz drücken - aus
LED2.on = KEY2.press_long; // lang drücken - an
ausschaut.
Torsten C. schrieb:> Ich habe dabei viel gelernt und habe hoffentlich hier und da auch> nützliche Beiträge geleistet.
Das von Peter D. gesuchte fehlt immer noch: Wie mit C++ eine einfache
Lösung obiger Anweisungen gelingt.
Torsten C. schrieb:> Das mag - je nach Anspruch - darauf hinaus laufen und eine Fortführung> des Themas wäre in diesem Thread wahrscheinlich falsch platziert.
Also Kapitulation auf der ganzen Linie. Wie zu erwarten, sind die
hochtrabenden Bemühungen an der Komplexität der Sprache erstickt.
Torsten C. schrieb:> Hans-Georg Lehnard schrieb:>> Hast Recht und mit constexpr Konstruktoren gehts auch.>> Genau das meinte ich oben mit "Das geht per constexpr wunderbar. Ich> hab's gerade mit avr GCC 4.7.2 probiert.">> Cool, dann sind wir uns einig?>
Ich hatte damit nie Probleme ;)
> Ich frage mich jetzt gerade, ob und ggf. worüber eine weitere Diskussion> hier noch Sinn macht. Das ganze ist ja etwas "abstrus", weil sich der OP> PeDa ofenbar aus dem Thread zurück gezogen hat, und wir hier ein Stück> weit eine "Phantom-Diskussion" führen.>
Die Diskussion finde ich weder "Phantom" noch "abstrus" eher etwas
offtopic, weil von der ursprünglichen Frage eines Praktikers durch viele
theoretische Beiträge nicht viel übrig geblieben ist ...
Das ist jetzt nicht abwertetend gemeint.
> Ich habe dabei viel gelernt und habe hoffentlich hier und da auch> nützliche Beiträge geleistet.>
So sehe ich das auch.
> Zum Thema uCpp Frank M. schrieb:>> Ausserdem halte ich eine "plattformübergreifende Lösung" für>> ein Unterfangen, dass sehr viel Arbeit bedeutet, bei dem das Ziel in so>> weiter Ferne liegt, dass - wenn eine Lösung denn irgendwann existieren>> sollte - die Hardware dafür dann gar nicht mehr existieren wird.>> Kurz: ich halte diesen Anspruch für ein Fass ohne Boden.>> Das mag - je nach Anspruch - darauf hinaus laufen und eine Fortführung> des Themas wäre in diesem Thread wahrscheinlich falsch platziert.>
Das mag nicht nur das ist garantiert so.
> Gibt es Einsprüche? ;-)
Gegen die Einstellung einer Lib Entwicklung aus den genannten Gründen
gibt es nichts zu sagen. Es wäre ähnlich xpcc geworden, da kann man das
auch gleich benutzen wenn man will.
Das Thema an sich ist für mich noch nicht erledigt aber da kann ich auch
alleine weiter im stillen Kämmerlein werkeln.
Den Thread kann man, meiner Meinung nach, aber offen lassen !.
Bastler schrieb:> Noch hab ich keine ASM-Lösung schimmern sehen.
Ist auch besser so, es geht hier um etwas anderes als Rechthaberei mit
ASM.
Macht doch für sowas einen anderen Thread auf!
Das war doch nur die Antwort auf "ihr seit wegen der Sprache immer noch
nicht fertig". Wollte sagen "der Einzeller aber auch nicht". Sonst: ich
kenne ASM. Viele verschiedene. Und will das deshalb nicht mehr selber
machen. So nun warte ich wieder gespannt auf C++?
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> Karl Käfer schrieb:>>>> Guckstu avr/io.h: DDRD, PIND, PORTB und PB4 sind Konstanten!>>> Das sind Preprocessor Definitionen
Das sind Präprozessor-Makros für Präprozessor-Makros, die dann letzten
Endes in Konstanten aufgelöst werden. Das Makro "DDRB" wird in
avr/iotn13.h durch "_SFR_IO8(0x17)" ersetzt. "_SFR_IO8(io_addr)"
wiederum ist ein Makro für "((io_addr) + __SFR_OFFSET)", und da in
diesem Falle io_addr (0x17) und __SFR_OFFSET (je nach AVR-Architektur
0x00 oder 0x20) bekannte Konstanten sind, ist das Ergebnis dieser
Addition eine Konstante, die der Compiler zur Compilezeit berechnet --
genau wie wenn Du "int a = 3 + 4" in Deinen C-Code schreibst.
Es ist ein wenig verkürzt, zugegeben -- aber im Ergebnis stehen diese
genannten Präprozessor-Makros also für Konstanten, die zur Compilezeit
bekannt sind, ohne den Compiler explizit darauf hinweisen zu müssen.
> und die kümmern sich nicht um Namespaces und kollidieren mit allem> möglichen, deshalb in C++ eher ungeeignet. Dafür gibt es const und> constexpr.
Irgendwie wirst Du konstante Registeraddressen halt mit symbolischen und
verständlichen Namen versehen müssen. Mir erscheinen Bezeichner wie DDRD
und Co. da schon deswegen sinnvoll, weil sie so auch in den
Datenblättern der MCUs verwendet werden. Wenn Du eine bessere Lösung
kennst, die ebenso wenig kostet und ebenso verständlich ist: sehr gerne,
her damit. ;-)
Liebe Grüße,
Karl
Ausgangspunkt:
Beitrag "Re: C++ auf einem MC, wie geht das?"Karl Käfer schrieb:> eine Konstante, die der Compiler zur Compilezeit berechnet
Und die berechneten Werte werden in dem Code aus Deinem o.g. Beitrag im
ctor der "class Pin" den folgenden Pointern zugewiesen:
1
volatileuint8_t*constddr;
2
volatileuint8_t*constport;
3
volatileuint8_t*constpin;
Wie man sieht: Die gewohnten Namen sind wiedererkennbar!
Ich habe nur ein "const" ergänzt, wegen "ichbinmaldiesmaldas"^^.
Ich dachte, das wäre geklärt.
Man könnte dem ctor die Makros "DDRB" usw. übergeben oder die Adressen
anders berechnen. Bei "anders" muss man - je nach Methode - "constexpr"
benutzen, bei enums^^ wäre man sogar typsicher. Beides geht.
U. a. Weil #define nicht typsicher ist, sind z.B. in C# mit #define
keine Wert-Zuweisungen mehr möglich, sondern nur noch "bedingte
compilierung".
Wenn Dir das egal ist, nimm Macros.
Hans-Georg Lehnard schrieb:> Das Thema an sich ist für mich noch nicht erledigt aber da kann ich auch> alleine weiter im stillen Kämmerlein werkeln.
Zur Frage des OP fehlen m.E. nur noch ein "Zustandsautomat" und ein
"Timer".
Ein Timer wäre ja einfach 'ne weitere Klasse, die ohne template<> nach
dem gleichen Schema wie "class Pin"^^ aufgesetzt wird.
Ralf G. schrieb:> Ralf G. schrieb:>> Ich habe>> allerdings noch keinen Einfall, wie man solche Objekte universell in>> andere Objekte einbinden kann (ohne diese <>-Klammerlisten).> Da wird man wohl nicht drum herumkommen...
Das man um "diese <>-Klammerlisten" herum kommt, falls mann will,
wurde ja an "class Pin" von Karl Käfer gezeigt, oder sind da noch Fragen
offen?
Hans-Georg Lehnard schrieb:> weil von der ursprünglichen Frage eines Praktikers durch viele> theoretische Beiträge nicht viel übrig geblieben ist ...
Meinst Du z.B. die theoretischen Beiträge um den Zustandsautomaten?
Wollen wir uns dazu auch noch auf ein gemeinsames Ergebnis einigen?
Ich kann dazu erstmal wenig sagen, weil ich erstmal die einfache
Delegates^^-Variante ohne <>-Klammerlisten umgesetzt habe und die
Variante mit Templates "Boost_sm.png"^^ erst noch ausprobieren müsste.
Hallo Karl,
wir wollen schon das gleiche .. "sprechende Namen"
Beispiel:
in avrio.h
#define PINA _SFR_IO8(0x19)
#define DDRA _SFR_IO8(0x1A)
#define PORTA _SFR_IO8(0x1B)
#define PINB _SFR_IO8(0x16)
#define DDRB _SFR_IO8(0x17)
#define PORTB _SFR_IO8(0x18)
#define PINC _SFR_IO8(0x13)
#define DDRC _SFR_IO8(0x14)
#define PORTC _SFR_IO8(0x15)
#define PIND _SFR_IO8(0x10)
#define DDRD _SFR_IO8(0x11)
#define PORTD _SFR_IO8(0x12)
usw.
Und ich fasse die Port Register einmalig in einer Struktur zusammen und
weil es auch Ports mit einem weiteren Register für pull up gibt habe ich
2 Typen definiert
struct AvrPortRegsTyp1 {
uint8_t in; // PINx input register
uint8_t dir; // DDRx input register
uint8_t out; // DATAx output register
};
struct AvrPortRegsTyp2 {
uint8_t in; // PINx input register
uint8_t dir; // DDRx direction register
uint8_t out; // DATAx output register
uint8_t pue; // PUEx pull up enable
};
Und definiere dann meine Ports als Konstante .
#ifdef _AVR_ATmega16_
constexpr AvrPortRegsTyp1 PORTA = {0x19,0x1A,0x1B};
constexpr AvrPortRegsTyp1 PORTB = {0x16,0x17,0x18};
constexpr AvrPortRegsTyp1 PORTC = {0x13,0x14,0x15};
constexpr AvrPortRegsTyp1 PORTD = {0x10,0x11,0x12};
#endif // _AVR_ATmega16_
#ifdef _AVR_ATtiny1634_
constexpr AvrPortRegsTyp2 PORTA = {0x0F,0x10,0x11,0x12};
constexpr AvrPortRegsTyp2 PORTB = {0x0B,0x0C,0x0D,0x0E};
constexpr AvrPortRegsTyp2 PORTC = {0x07,0x08,0x09,0x0A};
#endif // _AVR_ATtiny1634_
Der Unterschied ist aber mein PORTA definiert den Kompletten Port und
nicht nur ein Register und das gefällt mir besser und ich halte es für
stimmiger. Das lässt sich auch auf alle anderen "Module" wie USART,
TIMER usw. anwenden.
Würde ich nun avrio.h davor includen, würden meine Definitionen durch
den Preprocessor verändert werden, weil die in avrio.h als Macro
definiert sind.
Ich kann das auch nicht verhindern indem ich mein Code in einen
Namespace packe. Das wollte ich damit sagen, mehr nicht ...
Torsten C. schrieb:> Bei "anders" muss man - je nach Methode - "constexpr" benutzen
Auch mit dem Risiko weiterer "vieler theoretischer Beiträge"^^: Mich
würde mal interessieren, wie weit man das treiben kann. Vielleicht
könnte man in einer constexpr-Funktion sogar eine XML-Datei abfragen,
z.B. so ein jinja2-Headerfiletemplate^^. ;-)
OK, dann dauert das Compilieren ewig, war doof. Aber es würde zeigen,
was alles möglich wäre, wenn man constexpr-Funktionen statt #define
benutzt.
Hans-Georg Lehnard schrieb:> constexpr AvrPortRegsTyp2 PORTC = {0x07,0x08,0x09,0x0A};> … und das gefällt mir besser und ich halte es für stimmiger.
Mir auch / ich auch. Cool, Deine Arrays! :-) Genau so werde ich das in
Zukunft machen!
Eventuell als Übergang erstmal, indem ich die Start-Adressen aus avrio.h
hole. Hauptsache der Editor kennt die nicht und die ollen Macros werden
nicht dauernd vorgeschlagen , also z.B. "DDR0", wenn ich "DD" tippe!
Beim STM32 heißt die .h anders, aber das Prinzip ist das Gleiche.
Hans-Georg Lehnard schrieb:> Den Thread kann man, meiner Meinung nach, aber offen lassen!
Ja! Ich will weiter lernen! :-)
Torsten C. schrieb:>> Zur Frage des OP fehlen m.E. nur noch ein "Zustandsautomat" und ein> "Timer".>> Ein Timer wäre ja einfach 'ne weitere Klasse, die ohne template<> nach> dem gleichen Schema wie "class Pin"^^ aufgesetzt wird.>
Das sehe ich nicht ganz so ;)
Einen Zustandsautomaten kann man einsetzen aber ob der so effektiv ist
wie Pedas entprellroutine würd ich jetzt nicht unterschreiben. Und wie
kommt die Zeit in dein IO Port ? Da brauchst du eine Callback Funktion
entweder direkt aus der ISR aufgerufen oder besser über eine Klasse
SystemTimer die einen Hardware Timer verwaltet.
Dann wäre es schön, wenn der Pin sich beim SystemTimer anmelden könnte
und dabei noch sagen in welchem Intervall er gerne einen Rückruf hätte.
Vielleicht sogar während der Laufzeit umschaltbar.
Aha, höre ich dich sagen: Das ist eine Aufgabe für das Observer Pattern.
Richtig aber dann bitte in einer Embedded Version ;)
Dann Brauchst du Klassen für Tasten:
SimpleButton // taste gedrückt ja/nein
EdgeButton // Taste seit der letzten Abfrage neu gedrückt
TimedButton // Taste kurz, mittel, lang gedrückt.
Und jetzt noch eine Super Led, die alles kann // ein, aus, schnell,
langsam blinken.
Die IO Ports über die wir bisher geschrieben haben geben das nicht her.
Damit wäre für mich Pedas Frage beantwortet ;)
main
{
SimpleButton simplBtn(PORTA,P1);
EdgeButton edgeBtn(PORTA,P2);
TimedButton timedBtn(PORTA,P3);
// led ist default aus
SuperLed led1(PORTA,P4);
SuperLed led2(PORTA,P5);
SuperLed led3(PORTA,P6);
while(1) {
led1 = simplBtn // Led Ein wenn gedrückt
led2 = edgeBtn // Led togelt bei jedem Tastendruck
led3 = timedBtn // kurz gedrückt led aus, mittel gedrückt led
// blinkt schnell, lang gedrückt led blinkt
langsam
}
}
Und dann schaun wir mal in welchen MC das noch rein passt ;)
Mit dem automatischen hochzählen der Register und nur das erste
definieren wäre ich vorsichtig. Falls Atmel aud die Idee kommt, bei der
nächsten Umstellung auf einen anderen Produktionsprozess und mit einer
Maskenrevision die Reihenfolge ändert hast du verloren. Alles schon
vorgekommen !
Hans-Georg Lehnard schrieb:> Mit dem automatischen hochzählen der Register und nur das erste> definieren wäre ich vorsichtig.
Mit Startadresse meinte ich die Startadresse für die structs
"AvrPortRegsTyp1"^^ oder "AvrPortRegsTyp2". Sorry, also nicht "genau
so"^^, sondern "so ähnlch". Danke für den Hinweis!
Es könnte dann je ein Array mit UARTs, ein Array mit Timern eins mit
DMA-Kanälen usw. geben. Ich muss das in der Praxis ausprobieren, das
kann ich mir im Moment nicht 100% vorstellen.
Vielleicht wird's 'ne eigene Klasse, wo alle UARTs, Timer, DMA-Kanäle
usw. in einer static const drin sind. Diese Klasse wird ja dann nur zur
Compile-Zeit von constexpr-Funktionen gebraucht, landet also nicht im
µC.
Hallo Torsten,
Torsten C. schrieb:> Und die berechneten Werte werden in dem Code aus Deinem o.g. Beitrag im> ctor der "class Pin" den folgenden Pointern zugewiesen:>>
1
volatileuint8_t*constddr;
2
>volatileuint8_t*constport;
3
>volatileuint8_t*constpin;
Pointer, Zeiger, Adressen... nenn' es wie Du willst. Tatsache ist, daß
Du Deinem Compiler irgendwie sagen mußt, daß die Variable keine Variable
im herkömmlichen Sinne, sondern die Adresse eines Registers ist, auf das
er nur über ebendiese hart codierte, konstante Adresse zugreifen kann.
> Ich habe nur ein "const" ergänzt, wegen "ichbinmaldiesmaldas"^^.
Die Fehler, die das verhindert, erscheinen mir ziemlich hypothetisch, so
daß der echte, praktische Nutzen sich IMHO in Grenzen hält. Aber es ist
ein wenig eleganter und offensichtlicher (Lesbarkeit!) und kostet
nichts, insofern habe ich die Änderung in mein "typedef _Register"
übernommen.
> Man könnte dem ctor die Makros "DDRB" usw. übergeben oder die Adressen> anders berechnen. Bei "anders" muss man - je nach Methode - "constexpr"> benutzen, bei enums^^ wäre man sogar typsicher. Beides geht.
Ja. Man kann auch das Rad neu erfinden und sich dann in den Fuß
schießen, das geht auch. Die Frage ist aber doch, ob das auch klug ist.
;-)
Solche Konstanten werden in der Regel nur ein Mal verwendet, nämlich,
wenn eine Instanz von Pin erzeugt wird. Wenn Du das anders machen
willst, wirst Du nicht umhin kommen, eigene Libraries für verschiedene
AVRs zu schreiben und sie dauerhaft zu pflegen. Ob sich der Aufwand
dafür lohnt, um Fehlern vorzubeugen, die bei näherem Hinsehen a) relativ
unwahrscheinlich und b) ausgesprochen leicht zu finden sind, sei
dahingestellt.
Aber unter dem Stichwort der Wiederverwendbarkeit halte ich es für eine
bessere Idee, hier einfach die bereits vorhandenen Makros aus der
AVR-Libc zu benutzen. Die wird nämlich dankenswerterweise von anderen
erstellt und gepflegt, und solange es keine gravierenden Nachteile
bedingt, nehme ich diese großzügige Schenkung ausgesprochen gerne an.
Faulheit ist laut Larry Wall schließlich eine der Kardinaltugenden guter
Programmierer. ;-)
Liebe Grüße,
Karl
Karl Käfer schrieb:> Die Fehler, die das verhindert, erscheinen mir ziemlich hypothetisch
Es geht nicht um Fehler, sondern um "final und private"^^. Eigentlich
egal, denn Du hast die Änderung ja bereits übernommen.
Karl Käfer schrieb:> Die Frage ist aber doch, ob das auch klug ist.
Über Geschmacksfragen kann man nicht streiten. Mich stören die
Vorschläge vom Editor^^ und die Verschmutzung des globalen
"Namensraums", wenn ich das so nennen darf.
Karl Käfer schrieb:> unter dem Stichwort der Wiederverwendbarkeit halte ich es für eine> bessere Idee, hier einfach die bereits vorhandenen Makros aus der> AVR-Libc zu benutzen
Ja, das sehe ich genau so. Entweder man holt sich die Startadressen der
struct-Pointer (z.B. auf AvrPortRegsTyp1^^) aus der AVR-Libc oder
bastelt eine automatische Konvertierung in Klassen mit "static
constexpr".
Karl Käfer schrieb:>> Solche Konstanten werden in der Regel nur ein Mal verwendet, nämlich,> wenn eine Instanz von Pin erzeugt wird. Wenn Du das anders machen> willst, wirst Du nicht umhin kommen, eigene Libraries für verschiedene> AVRs zu schreiben und sie dauerhaft zu pflegen. Ob sich der Aufwand> dafür lohnt, um Fehlern vorzubeugen, die bei näherem Hinsehen a) relativ> unwahrscheinlich und b) ausgesprochen leicht zu finden sind, sei> dahingestellt.>> Aber unter dem Stichwort der Wiederverwendbarkeit halte ich es für eine> bessere Idee, hier einfach die bereits vorhandenen Makros aus der> AVR-Libc zu benutzen. Die wird nämlich dankenswerterweise von anderen> erstellt und gepflegt, und solange es keine gravierenden Nachteile> bedingt, nehme ich diese großzügige Schenkung ausgesprochen gerne an.> Faulheit ist laut Larry Wall schließlich eine der Kardinaltugenden guter> Programmierer. ;-)>
Hallo Karl,
natürlich hast du recht aber bei den AVR ist es nicht so schlimm,
denn die mitgelieferten xml Files parsen und in einem passenden Format
speichern dürfte eine der leichteren Übungen sein ;)
Torsten C. schrieb:> Und die berechneten Werte werden in dem Code aus Deinem o.g. Beitrag im> ctor der "class Pin" den folgenden Pointern zugewiesen:> volatile uint8_t * const ddr;> volatile uint8_t * const port;> volatile uint8_t * const pin;> Wie man sieht: Die gewohnten Namen sind wiedererkennbar!
Es resultiert daraus aber nur kurzer Code mit Direktzugriff auf die
Ports (z.B. 'sbi'/ 'cbi') in den aufgeführten Minimalbeispielen, wo
alles in einer Datei steht!
Torsten C. schrieb:> Das man um "diese <>-Klammerlisten" herum kommt, falls mann will,> wurde ja an "class Pin" von Karl Käfer gezeigt, oder sind da noch Fragen> offen?
siehe oben.
Hans-Georg Lehnard schrieb:> Mit dem automatischen hochzählen der Register und nur das erste> definieren wäre ich vorsichtig. Falls Atmel aud die Idee kommt, bei der> nächsten Umstellung auf einen anderen Produktionsprozess und mit einer> Maskenrevision die Reihenfolge ändert hast du verloren. Alles schon> vorgekommen !
Das gibt's schon!
Deshalb:
<komt morgen>
Ralf G. schrieb:> Es resultiert daraus aber nur kurzer Code mit Direktzugriff auf die> Ports (z.B. 'sbi'/ 'cbi') in den aufgeführten Minimalbeispielen, wo> alles in einer Datei steht!
Das verstehe ich nicht. Die Anzahl der Dateien doch egal, mit "const",
"private" oder "final". Hast Du die Beiträge ^^ gelesen?
Ohne "const", "private" oder "final" wären 'sbi' oder 'cbi' nicht
wirklich sicher, da hättest Du Recht, genau deshalb ja "const".
Torsten C. schrieb:> Hans-Georg Lehnard schrieb:>> Mit dem automatischen hochzählen der Register und nur das erste>> definieren wäre ich vorsichtig.>> Mit Startadresse meinte ich die Startadresse für die structs> "AvrPortRegsTyp1"^^ oder "AvrPortRegsTyp2". Sorry, also nicht "genau> so"^^, sondern "so ähnlch". Danke für den Hinweis!>> Es könnte dann je ein Array mit UARTs, ein Array mit Timern eins mit> DMA-Kanälen usw. geben. Ich muss das in der Praxis ausprobieren, das> kann ich mir im Moment nicht 100% vorstellen.>> Vielleicht wird's 'ne eigene Klasse, wo alle UARTs, Timer, DMA-Kanäle> usw. in einer static const drin sind. Diese Klasse wird ja dann nur zur> Compile-Zeit von constexpr-Funktionen gebraucht, landet also nicht im> µC.
Denk nicht so kompliziert;)
Eine Uart ist nur eine Ausprägung einer AVR USART die muss nicht immer
eine Uart sein sondern kann SPI und I2C sein oder sonst was sein.
Oder du hast eine Software Uart zusätzlich. Oder eine UART mit 9 bit
Protokoll usw. da kommst du mit deinen Arrays nicht mehr hinterher ;)
So jetzt bin ich mal wieder ruhig ... ,)
Hans-Georg Lehnard schrieb:> Eine Uart ist nur eine Ausprägung einer AVR USART die muss nicht immer> eine Uart sein sondern kann SPI und I2C sein oder sonst was sein.> Oder du hast eine Software Uart zusätzlich.
Und dann müsste ein SPI-ctor oder I2C-ctor statt eines UART-ctors
aufgerufen werden. So lange die Methoden-Bezeichner identisch sind, geht
das ohne Vererbung flexibel. Passt! Oder?
BTW, blöde Frage:
Woran erkennt der Compiler, dass 'sbi' oder 'cbi' anwendbar sind? Oder
geht das bei allen Adressen im RAM?
Torsten C. schrieb:> Woran erkennt der Compiler, dass 'sbi' oder 'cbi' anwendbar sind? Oder> geht das bei allen Adressen im RAM?
Da kann ich ja jetzt mal fragen: "Hast du das Datenblatt gelesen?" :-(
Genau darum geht es die ganze Zeit. Effizienter Code. Mit Zeigern und
virtuellen Funktionen um sich schmeißen, kann jeder!
Ralf G. schrieb:> Da kann ich ja jetzt mal fragen: "Hast du das Datenblatt gelesen?"
Das ist lange her! Ich hatte nur grob in Erinnerung, dass das nicht
überall im RAM geht, daher die Frage, ob wir was übersehen haben.
Ich hätte auch morgen nachlesen und danach posten können. Sorry, es war
ja auch nur "by the way". Aber vielleicht wichtig, wie Du selbst sagst.
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> wir wollen schon das gleiche .. "sprechende Namen"> [...]> struct AvrPortRegsTyp1 {> uint8_t in; // PINx input register> uint8_t dir; // DDRx input register> uint8_t out; // DATAx output register> };> [...]> Und definiere dann meine Ports als Konstante .>> #ifdef __AVR_ATmega16__> constexpr AvrPortRegsTyp1 PORTA = {0x19,0x1A,0x1B};> #endif // __AVR_ATmega16__> [...]> Der Unterschied ist aber mein PORTA definiert den Kompletten Port und> nicht nur ein Register und das gefällt mir besser und ich halte es für> stimmiger.
Das ist wohl Geschmackssache, aber die Idee gefällt mir. Aber dann
müssten wir die Definitionen für alle möglichen AVRs a) erstellen und b)
zukünftig pflegen... hm.
Liebe Grüße,
Karl
Karl Käfer schrieb:> Aber dann müssten wir die Definitionen für alle möglichen AVRs> a) erstellen und> b) zukünftig pflegen... hm.
Wie gesagt: Entweder in einer constexpr-Funktion (kompliziert aber sehr
dynamisch) die .xml- oder .h-Dateien parsen oder ein Script basteln:
"eine der leichteren Übungen"^^.
Letzteres muss ja nur einmal pro "Update" ausgeführt werden und das
Ergebnis könnte einer einmal in Github aktualisieren.
Weitere Ideen? Favorit?
Torsten C. schrieb:> Sorry.
Ja, Okay.
Dann mal als Zusammenfassung:
'const' ist deine eigene Absichtserklärung, den Wert (Zeiger/
Variable) nie zu verändern. Der Compiler tritt dir dafür in... (du weißt
schon). Wie der Compiler das intern umsetzt, ist sein Bier.
Die 'Daten' für den Port (DDRx, PORTx, PINx) in eine Klasse zu packen
ist erstmal sinnvoll. Jetzt kommt es darauf an wie. Zeiger, Referenzen,
gerne auch 'const'... ist völlig egal. Sobald das Projekt über mehrere
Module geht, ist es nicht mehr zu erkennen, dass es sich um Zeiger auf
den IO-Bereich handelt. Der Compiler macht 'ganz normale'
Speicherzugriffe daraus. Normalerweise macht das nichts. Dauert es eben
ein paar Takte länger. Jetzt ist aber die Frage: 'Geht das trotzdem
anders?' Da bieten sich möglicherweise! Templates an. Da sehe ich
allerdings die Grenze: Wenn man jetzt mit den IO_Pin-Klassen
weiterarbeiten will, müsste man den ganzen '<>-Rattenschwanz' mitnehmen.
Das gefällt mir nicht!!! Ich hab trotzdem mal was probiert. Stichwort
'virtuelle Funktion'. Gefällt mir auch nicht!!!: zusätzlicher
RAM-Verbrauch, zusätzlicher ROM-Verbrauch, Geschwindigkeitsvorteil
gegenüber Zeigern mit 'normalen Speicherzugriffen' sehr zweifelhaft!
Eigentlich wollte ich mal noch ein paar andere Varianten vor der
Veröffentlichung probieren, aber ich stell's heute schon mal ein:
Ralf G. schrieb:> Vielleicht hat ein Profi 'ne bessere Idee.
Ich bin kein "Profi", aber falls das niemand vor mir macht, werde ich
gelegentlich folgendes mal mit verschiedenen Compilern analysieren:
Ralf G. schrieb:> Zeiger, Referenzen, gerne auch 'const'... ist völlig egal
Ich bin fest davon überzeugt, dass sich ein "const" nicht anders
auswirkt als ein #define. Das steht jedenfalls überall im I-Net. Daher
bin ich davon ausgegangen, dass das nicht völlig egal ist. Ohne
"const", "final" oder "private" (^^gelesen?) hättest Du mit Sicherheit
Recht und sobald das Projekt über mehrere Module ginge, könnte der
Compiler 'ganz normale' Speicherzugriffe daraus machen. Aber ich würde
mich (wie mehrfach gesagt^^) nicht darauf verlassen, dass er das nie
tut, daher "const", "final" oder "private".
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> natürlich hast du recht aber bei den AVR ist es nicht so schlimm,> denn die mitgelieferten xml Files parsen und in einem passenden Format> speichern dürfte eine der leichteren Übungen sein ;)
Mitgelieferte XML Files... huch. Habe gerade mal einen Download von
Atmel Studio angeworfen, sowas hab' ich hier nämlich nicht -- ich habe
auch das Betrübssystem nicht, auf dem das läuft. Mal schauen, ob ich das
irgendwie entpackt bekomme, sonst muß ich mir morgen eine Wegwerf-VM
machen.
Gute Nacht,
Karl
Karl Käfer schrieb:> sowas hab' ich hier nämlich nicht
Kenne ich auch nicht, hatte mich auf Hans-Georg bezogen: Kläre uns mal
auf!
xpcc hätte die XML-Files und .h kann man zur Not auch selbst parsen. Für
XML gäbe es halt fertige Parser, das wäre bequemer; das ist aber am Ende
"Conchita", wie die Ösis sagen, also "Wurst"!
Karl Käfer schrieb:> Gute Nacht
Danke, wünsche ich Dir auch!
@ekiwi,
Super Sache, die ihr da macht.
@Peter Danneger
Peter Dannegger schrieb:> Ich möchte gerne folgendes erreichen: LED0 = KEY0.state; //> an solange Taste gedrückt> LED1.toggle = KEY1.press // wechsel bei jeder Drückflanke> LED2.off = KEY2.press_short; // kurz drücken - aus> LED2.on = KEY2.press_long; // lang drücken - an
Ersetze einfache
LED1.off
mit
LED1::setOutput(xpcc::Gpio::Low);
;-)
Die Syntax LED1.off beruht irgendwie auf der Annahme, daß man SFR mit
struct abstrahiert hat. Ist aber nur eine Vorstufe der
Objektorientierung. Interessant wird es erst, wenn Funktionen
dazukommen.
frischling schrieb:> Objekte kann man doch auch statisch anlegen.
Hmmm, muss man das eigentlich tun?
noreply@noreply.com schrieb:> LED1::setOutput(xpcc::Gpio::Low);
Die Variante hatten wir noch gar nicht, also ohne was statisch bzw.
als Klassen-Instanz mit const-Pointern anzulegen. Oder?
Das würde vllt. mein Singleton^^-Problem lösen, aber das blicke ich
heute N8 nicht mehr.
Torsten C. schrieb:> Es könnte dann je ein Array mit UARTs, ein Array mit Timern eins mit> DMA-Kanälen usw. geben.
Hmm... meinst du in etwa so, wie Atmel das bei den Cortex-Controlllern
gemacht hat?
Torsten C. schrieb:> noreply@noreply.com schrieb:>> LED1::setOutput(xpcc::Gpio::Low);>> Die Variante hatten wir noch gar nicht, also ohne was statisch bzw. als> Klassen-Instanz mit const-Pointern anzulegen. Oder?
Meinst du ohne Instanzierung? Das gabs in meinem Beispiel ganz weit oben
schoneinmal. Schaut sich das denn wirklich niemand an???
Daniel A. schrieb:> Schaut sich das denn wirklich niemand an???
Nein, hier geht es durcheinander wie Kraut und Rüben es dreht sich alles
im Kreis aber alle machen mit. Aber wie willst du das Online mit den
verschieden Kentnissen und Interessen lösen ?
Hans-Georg Lehnard schrieb:> Nein, hier geht es durcheinander wie Kraut und Rüben es dreht sich alles> im Kreis aber alle machen mit.
Gute Zusammenfassung. Es ist für den stillen Mitleser sehr unterhaltsam,
zu sehen, wie einige aneinander komplett vorbeireden und sich überhaupt
nicht richtig konzentrieren auf das, was der andere gerade geschrieben
bzw. schon als Lösung aufgezeigt hat.
> Aber wie willst du das Online mit den verschieden Kentnissen und> Interessen lösen ?
So jedenfalls nicht. Das sag ich Dir aus eigener Erfahrung.
Mein Vorschlag als Denkanstoß:
- Machs erstmal alleine oder schließ Dich mit einem oder maximal
zwei Leuten, die Ahnung von der Materie haben, per Mail kurz.
- Erstelle (allein oder in der kleinen Gruppe) ein Konzept, was
Hand und Fuß hat.
- Erstelle erste Ansätze von Lösungen als nachvollziehbare
Prototypen.
- Stelle das Konzept, den Prototyp und den Weg zur
Vorgehensweise hier vor.
- Gewinne damit andere, die genau diesen Weg mitgehen wollen
und lass Dir vor allem nicht mehr reinquatschen, dass man es
auch auf tausend anderen Wegen machen könnte.
- Setze dann mit den anderen im Team dieses Ziel um.
Ich habe einige (auch größere) Open-Source-Projekte aus dem Boden
gestampft und dann online mit bis zu 70 Leuten im Team umgesetzt und
gepflegt - viele Jahre lang. Ich sag Dir: Es geht nur so wie oben
beschrieben. Einer muss voranschreiten und die Richtung vorgeben.
Hans-Georg Lehnard schrieb:> Aber wie willst du das Online mit den verschieden Kentnissen und> Interessen lösen ?
1) Die Interessen gruppieren und die Gruppen in Github projekte
aufteilen.
2) Die github projekte unter eine Lizenz wie z.B. MIT stellen, damit
alle voneinander kopieren können.
3) Die vor und nachteile der verschiedenen Umsetzungen im bezug auf die
Kriterien Komplexität, Performance, Usabillity, Erweiterbarkeit, etc.
diskutieren
4) Erfahrungen für zukünftige projekte nutzen/Lieblingslib verwenden
Frank M. schrieb:> - Machs erstmal alleine oder schließ Dich mit einem oder maximal> zwei Leuten, die Ahnung von der Materie haben, per Mail kurz.>
Lieber Frank,
auch du hast nicht alles gelesen ;)
Thorsten will ein Projekt ich nicht.
Für mich stellt sich eher die Frage:
Wie kann jemand, ohne spezielle Kenstnisse über die Feinheitem von
C++(11) und Meta-Templates, diesen Thread lesen und was davon mitnehmen.
Dieser Thread motiviert doch nicht Cpp einzusetzen !!
Sondern jeder Leser denkt doch: "Alles viel zu kompliziert, nichts für
mich" ich mach weiter wie bisher.
Peda hat nach einer verständlichen Anleitung gefragt und das ist dieser
Thead mit Sicherheit, bisher jedenfalls, nicht.
Hans-Georg Lehnard schrieb:> Lieber Frank,> auch du hast nicht alles gelesen ;)
Ja, weil mich das Thema nur am Rande interessiert. Eine solche C++-Lib
ist für mich nicht lebensnotwendig, auch wenn ich ihr eine gewisse
Attraktivität nicht absprechen könnte ;-)
> Thorsten will ein Projekt ich nicht.
(BTW: Lass mal das 'h' aus Torstens Namen raus. Das irritiert mich doch
jedesmal sehr und ich denke: "Nanu, noch einer, der mitmischt?")
Okay, ich wusste nicht bzw. habe bisher nicht herauslesen können, dass
Du das Projekt gar nicht willst ;-)
Aber egal. Die oben von mir genannten Punkte sind nicht personenbezogen.
Kann man auf jeden übertragen. Ich muss aber dann noch einen Punkt
hinzufügen:
- Man muss als Vordenker von der Materie jede Menge Ahnung haben.
> Für mich stellt sich eher die Frage:>> Wie kann jemand, ohne spezielle Kenstnisse über die Feinheitem von> C++(11) und Meta-Templates, diesen Thread lesen und was davon mitnehmen.
Man kann nur Fetzen davon mitnehmen. Wenn man nicht täglich mit C++
arbeitet, sollte man erstmal die Basis lernen, bevor man sich auf die
Details von C++(11) stürzt. Sonst kommt einem viel als total neu und
aufregend vor, was tatsächlich schon ein alter Hut ist - siehe
C++-Templates im allgemeinen.
> Dieser Thread motiviert doch nicht Cpp einzusetzen !!> Sondern jeder Leser denkt doch: "Alles viel zu kompliziert, nichts für> mich" ich mach weiter wie bisher.
Eben. Das liegt in der Natur der Sache - sprich an der Natur eines
Forums. Wenn jemand einen Thread eröffnet, dann tragen 10% der Mitleser
etwas zum Thema bei, 30% versuchen, es Dir auszureden, weil man es
anders und viel besser machen könnte, weitere 30% haben es überhaupt
nicht verstanden und der Rest amüsiert sich nur über die Typen, die da
aufeinander losquasseln.
> Peda hat nach einer verständlichen Anleitung gefragt und das ist dieser> Thead mit Sicherheit, bisher jedenfalls, nicht.
Es gibt halt Leute, die finden zunächst die Fragestellung spannend und
versuchen dann, schon mal weiter zu denken. Sie kommen dann irgendwann
auf die geniale Idee, dass dieses Problem (und weitere zweitausend
ähnlich gelagerte Fälle) nur mit einem revolutionären Rundumschlag zu
lösen ist. Alsbald kommt man von Hölzchen auf Stöckchen, hat längst die
zugrundeliegende Frage bzw. Aufgabenstellung verdrängt und verkrümelt
sich in Details von Randproblemen, die plötzlich in den Vordergrund
drängen.
Danach werden dann die Prioritäten verschoben und man begeistert sich
plötzlich nur noch für diese Detailprobleme, die jetzt erstmal absoluten
Vorrang haben, weil sie so enorm spannend sind. Man mag sich nun fragen:
"Was soll das?"
Die Antwort ist einfach: Gar nichts. Irgendwann findet derjenige
"geniale Geist" ein komplett anderes Gebiet spannender ("z.B. wie
bekomme ich Bananen wieder gerade?") und das Thema und alle bisher dazu
erstellten Gedanken und Ideen werden einfach in den Orkus gespült. Und
so hangelt man sich von einer Aufgabe zur nächsten durchs Leben - ohne
irgend etwas tatsächlich fertigzustellen.
Ich gebe Dir abschließend nur einen Rat: Wenn Du dieses Projekt gar
nicht willst, dann lass es einfach. Gespräche mit Blinden über Farben
sind nicht zielführend.
Hallo Frank M,
ich zitiere mich jetzt mal selbst ;)
Ich will keine allgemeine Library schreiben, für mich ist das eine reine
private Machbarkeitsstudie und Auffrischung meiner C++ Kentnisse.
Ansonsten sehe ich das genau so wie du.
Manchmal habe ich den Eindruck, solche Foren sind reine
Selbsthifegruppen von "Nerds" ;)
Karl Käfer schrieb:> Ach, Torsten... Jedes Mal, wenn Du etwas idiotensicher machen willst,> dann kommt die Natur und erfindet bessere Idioten.
Danke für den "Gegenwind" in den darauf folgenden Argumenten. In der
Industrie werden ja auch noch solche code-review-tools eingesetzt:
http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis#C.2FC.2B.2B
Wenn man wollte, käme man damit wahrscheinlich sogar weiter, als sich
auf compiler- oder Linker-Fehler allein zu verlassen.
Ich bin eigentlich ganz froh, wenn ich mir darüber im Moment nicht auch
noch Gedanken machen muss.
Frank M. schrieb:> Gespräche mit Blinden über Farben sind nicht zielführend.
Ich hoffe, dass ich nicht mit blind gemeint bin. Ich lerne aus den
Gesprächen sehr viel.
Frank M. schrieb:> - Man muss als Vordenker von der Materie jede Menge Ahnung haben.Hans-Georg Lehnard schrieb:> Torsten will ein Projekt ich nicht.
Das ist nicht ganz falsch, denn da steht ja nicht "Torsten will ein
Projekt starten", denn für einen einen "Vordenker" reicht meine
Erfahrung mit C++ noch nicht.
Ich hätte schon gern irgendwann eine Bibliothek für AVR und ARM
gemeinsam und mit C++. Ich habe hier viele interessante Denkanstöße
bekommen, die ich erstmal ausprobieren muss, um weiter zu kommen.
Ich muss jetzt z.B. mal ausprobieren, ob sich bei "Containment" bzw.
"Objekt-Komposition" (siehe "Quadrat vs. Rechteck"^^) die neue Klasse
auch problemlos statisch anlegen lässt, wenn man nur mit const arbeitet
und im ctor alle Pointer der gesamten Komposition initialisieren will.
Torsten C. schrieb:> Frank M. schrieb:>> Gespräche mit Blinden über Farben sind nicht zielführend.>> Ich hoffe, dass ich nicht mit blind gemeint bin.
Du kannst ganz beruhigt sein. Ich habe mich lediglich allgemein über
Forum-Threads und deren dynamische Entwicklung geäußert. Das Ende war
lediglich ein Fazit, bzw. eine Lehre, die ich schon vor langer Zeit
erfahren musste.
Ob sich da jemand einen Schuh anzieht oder anziehen will, ist seine
alleinige Sache.
Frank M. schrieb:> Ob sich da jemand einen Schuh anzieht oder anziehen will, ist seine> alleinige Sache.
Und Deine "Checkliste" ist dabei sehr hilfreich. :-)
Hans-Georg Lehnard schrieb:
1
while(1){
2
led1=simplBtn// Led Ein wenn gedrückt
3
led2=edgeBtn// Led togelt bei jedem Tastendruck
4
led3=timedBtn// kurz gedrückt led aus, mittel gedrückt led
5
// blinkt schnell, lang gedrückt led blinkt
6
}
Wenn das mit Objekt-Komposition^^ klappt, würde ich eine "fest
verdrahtete" und eine dynamische Variante anlegen, wo die Zuweisung
direkt in der Klasse passiert und nicht in einer While-Schleife.
>led3=timedBtn// kurz gedrückt led aus, mittel gedrückt led
5
>// blinkt schnell, lang gedrückt led blinkt
6
>}
>> Wenn das mit Objekt-Komposition^^ klappt, würde ich eine "fest> verdrahtete" und eine dynamische Variante anlegen, wo die Zuweisung> direkt in der Klasse passiert und nicht in einer While-Schleife.
Ach Torsten,
diese while Schleife ist doch dein "Hauptpramm" in einer embedded
Anwendung.
Hans-Georg Lehnard schrieb:> Ach Torsten, diese while Schleife ist doch dein "Hauptpramm" …
Ach Hans-Georg, das ist mir doch klar!
Aber was willst Du uns damit sagen?
Torsten C. schrieb:> Hans-Georg Lehnard schrieb:>> Ach Torsten, diese while Schleife ist doch dein "Hauptpramm" …>> Ach Hans-Georg, das ist mir doch klar!>> Aber was willst Du uns damit sagen?
Torsten schrieb :
> wo die Zuweisung direkt in der Klasse passiert und nicht> in einer While-Schleife.
Eher was wolltest du uns damit sagen ?
Es gibt bereits eine weitverbreitete, erfolgreiche und halbwegs
Plattform unabhängige C++ Library für uC: Arduino.
Und die zeigt meines Erachtens bestens warum so ein Projekt für eine
breite Zielgruppe fast unmöglich ist:
- Für Anfänger kann es nicht einfach und idiotensicher genug sein,
Performance ist Zweitrangig
- ASM Programmierer wollen sowieso jedes Bit selbst kontrollieren, und
bekommen einen Herzkasper für jede unnötige Anweisung die der Compiler
generiert
- Erfahrene uC Programmierer würden schon gerne jedes noch so spezielle
Feature ihres ZieluC ausnutzen und genau darüber wissen, was das
Framework so in den Registern anstellt.
- Reine C Programmierer können sich mit einfacher Objektorientierung
noch anfreundne, aber spitze Klammern kennen die nur aus Operatoren für
Vergleich und Shift.
- Gute C++ Programmierer könnten sicherlich ein Lib aufstellen, die mit
so Sachen wie Objektorientierung, Templates, Metaprogrammierung einen
hocheffizienten Code erstellt, aber für keine der vorgenannten Gruppen
noch zugänglich wäre, weil sie in spitzen Klammern und völlig
unverständlichen Compilerfehlermeldungen ertrinken.
(Das ist jetzt alles etwas zugespitzt)
Arduino hat sich deswegen einzig und allein auf die erste Gruppe
konzentriert und sehr großen Erfolg gehabt. Und alle anderen Gruppen
schimpfen über ineffizienten, langsamen, großen Code der viele
Besonderheiten der Zielplattform versteckt oder überhaupt nicht
zugänglich macht.
vielleicht hab ich das problem nicht verstanden, aber beim programmieren
meines quadrocopters habe ich überlegt, ob luna, c, oder c++ .
als er in c dann geflogen ist, habe ich ein uml gezeichnet, und das dann
in c++ mit modellierten klassen umgeschrieben. fliegt natürlich genauso
;-)
laufzeitmessungen haben ergeben, daß der c++ - code wenige % länger
rechnet als der reine c-code, und im experiment dieser genauso lange wie
der in luna geschriebene.
die entwicklung scheint mir aber in c++ (klassenorientiert) überlegen,
z.b. die nutzung von mehreren objekten der gleichen klasse (z.b.
signalfilter) scheint mir für "nicht profis" wie mich in c++
übersichtlicher.
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> Nein, hier geht es durcheinander wie Kraut und Rüben es dreht sich alles> im Kreis aber alle machen mit. Aber wie willst du das Online mit den> verschieden Kentnissen und Interessen lösen ?
Um das Ganze womöglich wieder in etwas geordnetere Bahnen zu lenken --
und nicht zuletzt, weil konkreter Code IMHO immer noch die beste
Grundlage für diese Diskussion ist -- hier meine aktuelle Pin.hpp. Dem
geneigten Betrachter wird auffallen, daß:
- die Member-Variablen der Basisklasse jetzt nicht mehr "public",
sondern "protected" sind
- der Typ "_Register" jetzt mit dem Typ-Modifizierer "const" versehen
ist, wie angekündigt
- die Klasse "InputPin" jetzt Funktionen zum Setzen und Löschen des
Pullup-Widerstandes hat
- der Konstruktor der Klasse "InputPin" einen optionalen Parameter zum
Setzen der Pullups besitzt
- der Klasse "OutputPin" die Methode "toggle()" hinzugefügt wurde, die
den Pin durch das Schreiben auf das Register PIN umschaltet und
- daß die Klasse "InoutPin" zwar wieder enthalten, aber noch nicht um
die gerade genannten Funktionen ergänzt worden ist. Hier könnte es sein,
daß eine Art "Sicherheitsabfrage" sinnvoll ist, um zum Beispiel zu
verhindern, daß der Nutzer die Methode "toggle()" aufruft obwohl der Pin
gerade als Input konfiguriert ist. Andererseits würde das den Code
größer machen und die Frage aufwerfen, wie ein solcher Fehler abgefangen
und / oder darauf reagiert werden kann.
Bezüglich des "toggle()"-Verhaltens ist anzumerken, daß "PIN" im Prinzip
ein Input-Register ist, bei dem man üblicherweise nicht erwarten würde,
daß man darauf schreiben kann. Neuere AVRs erlauben es allerdings, durch
Schreiben auf dieses Register ein Umschalten mit nur einem
Maschinenbefehl durchzuführen; bei älteren AVRs soll das IIRC nicht
gehen. Wenn jemandem bekannt ist, bei welchen AVRs das geht und bei
welchen nicht, möge er sich bitte melden. Dann würde ich dort über
"#ifdef __AVR_ARCH__" Code einbauen wollen, der abhängig von der
AVR-Architektur den korrekten Code einfügt.
Der Bequemlichkeit halber habe ich die Datei einmal zum Download
angefügt und poste sie zusätzlich noch einmal:
1
#ifndef _PIN_HPP
2
#define _PIN_HPP 1
3
4
#include<avr/io.h>
5
6
/** expands PINDEF(B, 0) to &DDRB, &PORTB, &PINB, PB0
7
* example:
8
* OutputPin ledPin ( PINDEF(B, 0) );
9
* expands to:
10
* OutputPin ledPin (&DDRB, &PORTB, &PINB, PB0);
11
*/
12
#define PINDEF(X,Y) &DDR##X,&PORT##X,&PIN##X,Y
13
14
15
typedefvolatileuint8_t*const_Register;
16
17
18
classPin{/** base class for a pin */
19
protected:
20
_Registerddr;
21
_Registerport;
22
_Registerpin;
23
uint8_tnum;
24
25
public:
26
/** constructor
27
* @param ddr DDRn register address of pin (eg. &DDRB)
Hallo Scelumbro,
Scelumbro schrieb:> Es gibt bereits eine weitverbreitete, erfolgreiche und halbwegs> Plattform unabhängige C++ Library für uC: Arduino.> Und die zeigt meines Erachtens bestens warum so ein Projekt für eine> breite Zielgruppe fast unmöglich ist:> - Für Anfänger kann es nicht einfach und idiotensicher genug sein,> Performance ist Zweitrangig> - ASM Programmierer wollen sowieso jedes Bit selbst kontrollieren, und> bekommen einen Herzkasper für jede unnötige Anweisung die der Compiler> generiert> - Erfahrene uC Programmierer würden schon gerne jedes noch so spezielle> Feature ihres ZieluC ausnutzen und genau darüber wissen, was das> Framework so in den Registern anstellt.> - Reine C Programmierer können sich mit einfacher Objektorientierung> noch anfreundne, aber spitze Klammern kennen die nur aus Operatoren für> Vergleich und Shift.> - Gute C++ Programmierer könnten sicherlich ein Lib aufstellen, die mit> so Sachen wie Objektorientierung, Templates, Metaprogrammierung einen> hocheffizienten Code erstellt, aber für keine der vorgenannten Gruppen> noch zugänglich wäre, weil sie in spitzen Klammern und völlig> unverständlichen Compilerfehlermeldungen ertrinken.> (Das ist jetzt alles etwas zugespitzt)
Das ist alles sehr richtig, was Du schreibst, vielen Dank. Aus den von
Dir so treffend formulierten Gründen würde ich eine Bibliothek
bevorzugen, die auf der AVR-Libc aufbaut -- damit die erfahrenen
uC-Entwickler einerseits immer noch alle Features ihres Ziel-uC
ausnutzen können, wo das notwendig ist, andererseits aber ansonsten den
Komfort von C++ nutzen können. Zudem soll das Ganze idealerweise schlank
und effizient, trotzdem auch noch für weniger erfahrene C++-Entwickler
zugänglich bleiben.
Höre ich da jemanden "eierlegende Wollmilchsau" sagen? Mag sein. Aber
die von mir eben gepostete Pin-Bibliothek zeigt IMHO, daß die Ziele
zumindest für diese triviale Funktionalität durchaus vereinbar sind. Ich
persönlich bin gespannt, ob das auch bei nichttrivialen Funktionalitäten
so bleibt.
> Arduino hat sich deswegen einzig und allein auf die erste Gruppe> konzentriert und sehr großen Erfolg gehabt. Und alle anderen Gruppen> schimpfen über ineffizienten, langsamen, großen Code der viele> Besonderheiten der Zielplattform versteckt oder überhaupt nicht> zugänglich macht.
...und kreiden den wegen der Anfängerfreundlichkeit entstandenen Bloat
dann leider C++ an.
Liebe Grüße,
Karl
>Es gibt bereits eine weitverbreitete, erfolgreiche und halbwegs>Plattform unabhängige C++ Library für uC: Arduino.>Und die zeigt meines Erachtens bestens warum so ein Projekt für eine>breite Zielgruppe fast unmöglich ist:>- Für Anfänger kann es nicht einfach und idiotensicher genug sein,>Performance ist Zweitrangig
Die Arduino LIB ist gar nicht so schlecht. Wie ich schon weiter oben
erwähnt habe, kann man damit den selben Code auf AVR und ARM laufen
lassen.
Im Gegensatz zu diesem Thread hier haben die Arduino Entwickler
irgendwann angefangen, Code auszuliefern, obwohl er noch nicht perfekt
war. Wie aber in diesem Thread gezeigt, kann man bei geschickter C++
Programmierung auch die Pin-Zugriffsfunktionen wie digitalWrite hoch
performant machen. Man könnte die Erkenntnisse dieses Threads also
durchaus verwenden, um die Arduino LIB zu optimieren. Es macht dabei
durchaus Sinn, bei den gleichen Funktionsnamen zu bleiben und kein neuen
zu erfinden. Das Netz ist voll von HELP-Files und Beispielen für die
Befehle und Programme. Das ist ein Dokumentations und Erklärungsaufwand,
der mit einer neuen, anderen Lib nur sehr schwer zu verbessern ist. Man
darf die LIB auch durchaus erweitern, wenn einem die Funktionen nicht
reichen. Auch dafür gibt es viele Beispiele im Netz.
Es gibt von der iX-Developer (Heise) ein Sonderheft "Embedded Software".
Dort ein Artikel "Schlanke Embedded-Entwicklung mit Small-C++".
Der beschreibt Anhand eines kleinen Beispiels für AVR, wie man trotz C++
den Code klein hält. Also es wird an einem kleinem Beispiel von C zu C++
entwickelt und gezeigt, wie sich die Codegröße ändert.
Vielleicht interessiert es ja den TO oder auch sonst jemand.
Gruß Joachim
Karl Käfer schrieb:> Um das Ganze womöglich wieder in etwas geordnetere Bahnen zu lenken --> und nicht zuletzt, weil konkreter Code IMHO immer noch die beste> Grundlage für diese Diskussion ist -- hier meine aktuelle Pin.hpp.
Danke!
Ich verstehe aber noch nicht, wie man deine Klassen weiter nutzen soll.
Folgendes Beispiel adaptiert von
Beitrag "Re: C++ auf einem MC, wie geht das?":
Deine Pin Klasse ist doch leer. Welche Methoden soll LCD da denn
benutzen?
Also um klar zu sein: Du hast NIE eine automatische virtuelle Vererbung.
Du must schon explizit `virtual` hinschreiben damit der Compiler das
auch macht.
Nur dann weiß der Compiler was er überhaupt aufrufen kann und soll.
DualZähler schrieb:> Deine Pin Klasse ist doch leer. Welche Methoden soll LCD da denn> benutzen?> Also um klar zu sein: Du hast NIE eine automatische virtuelle Vererbung.> Du must schon explizit `virtual` hinschreiben damit der Compiler das> auch macht.
Ich denke, wir sind uns einig, dass zumindest in Hardware-nahen
Bibliotheken (Pin, UART, Device-Treiber wie z.B. für NRF24L01+, ...)
VMTs ('virtual') ein "no-go" sind.
Mit einer "Objekt-Komposition" (siehe "Quadrat vs. Rechteck"^^) macht
man das dann z.B. so:
1
classMeinPinMitMethoden{
2
InputPinpin;
3
OutputPinled;
4
Timertimer;
5
// Weitere Member (Methoden, Zustandsautomaten, ...)
6
};
Oder?
@Hans-Georg: Ich meinte, dass man irgendwie in dieser Richtung weiter
denken müsste und nicht mit "led3 = timedBtn" in der MainLoop. Wozu soll
denn andauernd diese Zuweisung wiederholt werden?
Es muss eine Art "Ereignis" ausgewertet werden, wenn ein Timer abläuft,
eine Taste gedrückt oder losgelassen wird. Also ein Callback, Delegat
oder Event-Handler, ... aufgerufen werden.
Torsten C. schrieb:> Wenn das mit Objekt-Komposition^^ klappt, würde ich eine "fest> verdrahtete" und eine dynamische Variante anlegen
Jo, geht beides, der Compiler hatte nix zu meckern:
festverdrahtet wie oben oder:
1
classMeinPinMitMethoden{
2
InputPin*constpin;// festverdrahtet
3
OutputPin*constled;// festverdrahtet
4
Timer*consttimer;// festverdrahtet
5
// Weitere Member (Methoden, Zustandsautomaten, ...)
6
};
dynamisch:
1
classMeinPinMitMethoden{
2
InputPin*pin;// dynamisch
3
OutputPin*led;// dynamisch
4
Timer*timer;// dynamisch
5
// Weitere Member (Methoden, Zustandsautomaten, ...)
Karl Käfer schrieb:> Um das Ganze womöglich wieder in etwas geordnetere Bahnen zu lenken --> und nicht zuletzt, weil konkreter Code IMHO immer noch die beste> Grundlage für diese Diskussion ist ...:
Um mal eine Alternative zu zeigen, die schon länger bei mir rumliegt:
Die main() kompiliert, wird zu zwei sbi und einem Sprung für die
while(1).
Letztendlich mag das für GPIOs ganz gut funktionieren. Wie aber bildet
man plattformübergreifend Timer, serielle Schnittstellen und
insbesondere die ISRs ab?
Überhaupt die zusammenarbeit von ISR mit C++ bereitet mir noch
Kopfzerbrechen.
Hallo Torsten,
ich geh jeztz mal davon aus, das wie beide der Meinung sind, der LED
Zustand soll sich jedesmal ändern wen sich an einer Taste was ändert.
Da hast du praktisch mehrere Möglichkeiten, eine davon ist in der
Hauptschleife die Taste zu pollen, genau das habe ich gemacht.
Du könntest für die Buttons auch z.B. Pin Change Interrupt nehmen aber
wie erfährt dann die jeweilige Led davon das sich etwas geändert hat. Da
musst du dann Listen pflegen welche LED hängt an welchem Button.
Meine "Design Entscheidung" an dieser Stelle war:
Led schau doch selber nach ob sich was geändert hat ;)
Und ich gebe ihr in der Hauptschleife genug Möglichkeit das auch zu tun.
Was man dem geposteten (Pseudo) Code nicht direkt ansieht ist folgendes:
Die SuperLed hat überladene Zuweisungsoperatoren(=), für jeden
Button(Typ) und der ruft GetState() vom jeweiligen Button(Objekt) auf.
Das sollte keine "Vorschrift" sein wie man etwas implementiert sondern
ich habe nur Peters Pseudo Code etwas umgeschrieben ... ;)
Peter Dannegger schrieb:> In C++ könnte man Zuweisungen nehmen, wenn man nur wüßte, wie man das> implementiert.>
1
>LED0=KEY0.state;// an solange Taste gedrückt
2
>LED1.toggle=KEY1.press// wechsel bei jeder Drückflanke
3
>LED2.off=KEY2.press_short;// kurz drücken - aus
4
>LED2.on=KEY2.press_long;// lang drücken - an
5
>
>
Also Peter, wenn du noch mit liest .. es geht ;)
Torsten C. schrieb:> nicht mit "led3 = timedBtn" in der MainLoop. Wozu soll denn andauernd> diese Zuweisung wiederholt werden?>> Es muss eine Art "Ereignis" ausgewertet werden
Diese Designentscheidung sollte dem User überlassen werden. Die lib kann
ja templates für events und eventQueues bereirstellen, und die
verwendung bleibt dem user überlassen.
Gleiches denke ich auch über ALLE ISRs: Templates und Makros für default
ISRs, welche der nutzer nutzen könnte, aber nicht muss. Man macht dazu
dan ein "best practice guid". Darin empfielt man dann das anlegen
Conroller und Layoutspezifischer headerfiles, mit
controllerunspezifischen .cpp files:
1
// Platine1Conf.hpp
2
#include<ucpp> // includiere lib
3
namespacehardware{
4
namespaceLED{
5
staticconstucpp::Port<0>::Pin<0>status;// declariere status led
6
staticconstucpp::Port<0>::Pin<1>error;// declariere error led
7
}
8
inlinevoidinit(){
9
LED::status.setOutput();
10
LED::error.setOutput();
11
}
12
}
13
14
// Main.cpp
15
#include CONFIG_FILE // includire config abhängig von macro
Scelumbro schrieb:> Überhaupt die zusammenarbeit von ISR mit C++ bereitet mir noch> Kopfzerbrechen.
Auf einem STM32F4 Discovery Board habe ich das so gelöst:
Also im Prinzip nicht anders als es in C gemacht würde: Die betroffenen
Methoden aus dem Interrupt-Handler direkt aufrufen. Der Unterschied ist
halt, dass man dazu die betreffenden Objekte erreichbar machen muss --
hier eben als globale Objekte.
In C++ bedeutet das allerdings, dass man sich um das Initialisieren der
statischen Objekte -- bzw. die Reihenfolge davon -- Gedanken machen
muss. Ich habe das Problem umschifft, in dem ich eben nur in einer
einzigen .cpp Datei im Projekt statische Objekte anlege.
Eine Zeit lang habe ich darüber nachgedacht, so etwas wie einen
Interrupt Handler zu modellieren. Das hätte dann bedeutet, dass sich die
Objekte, die den Interrupt behandeln wollen, beim Handler hätten
registrieren müssen. Ich habe keine Lösung gefunden, wie man das ohne
virtual Deklarationen hin kriegt. Und meinen Anspruch, dass sich die am
Interrupt interessierten Objekte schon zur Compile-Zeit auf den
Interrupt "registrieren" müssen, den wollte ich nicht aufgeben.
Und jetzt noch der unvollständige, unausgegore und wahrscheinlich
fehlerhafter Code
aber ich denke das Prinzip der Operator Überladung wird für nicht C++
Spezialisten auch klar und darum gehts ...
typedef enum
{
off,
on,
toggle,
fast_blink,
normal_blink,
slow_blink
} SUPER_LED_STATE;
Und nun die verschiedenen überladenen Operatoren
bool SuperLed::operator = (const bool value )
{
if( value)
led_State = on;
else
led_State = off;
return value;
}
Damit geht:
led = false/true;
und:
led1 = led2 = led3 = false/true;
// und das gleiche für SUPER_LED_STATE
EXT_LED_STATE SuperLed::operator = (const SUPER_LED_STATE state )
{
led_State = state;
return state;
}
Damit geht:
led = fast_blink;
und:
led1 = led2 = led3 = slow_blink;
const DigitalInput& SuperLed::operator = ( const DigitalInput& inp )
{
bool value = inp.GetInput(); // liefert nur true/false
if( value)
led_State = on;
else
led_State = off;
return inp;
}
// Ich denke ab hier macht Verkettung keinen Sinn mehr deshalb void
// Wenn man verkettung möchte muss man die Referenz auf den Button
zurückgeben
void SuperLed::operator = ( const SimpleButton& sib )
{
// liefert nur true/false
bool value = inp.GetInput();
if( value)
led_State = on;
else
led_State = off;
}
void SuperLed::operator = ( const EdgeButton& edb )
{
// liefert no_edge_detect, edge_detect
EDGE_BTN_STATE value = edb.GetInput();
if( edge_detect){
// toggle habe ich als einen eigenen state definiert, damit wenn der
blink callback noch aktiv ist er nicht ausgewertet wird
led_state = toggle;
this.Toggle();
// jetz müssen wir aber dem Edge Button irgendwie mitteilen das wir die
Flanke ausgewertet haben
// er sich auf no_edge_detect setzen soll und warten bis der Benutzer
die Taste neu gedrückt hat.
edb.Release();
}
}
void SuperLed::operator = ( const TimedButton& tib )
{
TIMED_BTN_STATE value = tib.GetState();
switch (value)
{
case not_pressed :
// nix machen;
break;
case short_pressed :
led_state = off;
break;
case normal_pressed :
led_state = fast_blink;
break;
case long_pressed :
led_state = slow_blink;
break;
}
}
Ich würde so weit gehen und die LED, den Taster und die Verbindung
zwischen beiden in separate Klassen einbauen.
Die LED ist ein GPIO_output, der Taster ein GPIO_input und beide werden
über einen "Logikbaustein" (der nichts mit der Hardware zu tun hat)
verbunden.
Soll das Ganze ein Blinklicht werden, dann muss sich eben dieser
Logikbaustein darum kümmern, dass er in jeder x-ten Timer-ISR aufgerufen
wird und entsprechend die LED blinken lässt.
Damit er das tun kann, wird im bei der Erstellung der Output, Input und
der entsprechende Timer übergeben und die Klasse bzw der Compiler
kümmert sich um den Rest.
Achim schrieb:> Ich würde so weit gehen und die LED, den Taster und die Verbindung> zwischen beiden in separate Klassen einbauen.>> Die LED ist ein GPIO_output, der Taster ein GPIO_input und beide werden> über einen "Logikbaustein" (der nichts mit der Hardware zu tun hat)> verbunden.>> Soll das Ganze ein Blinklicht werden, dann muss sich eben dieser> Logikbaustein darum kümmern, dass er in jeder x-ten Timer-ISR aufgerufen> wird und entsprechend die LED blinken lässt.>> Damit er das tun kann, wird im bei der Erstellung der Output, Input und> der entsprechende Timer übergeben und die Klasse bzw der Compiler> kümmert sich um den Rest.
Hallo Achim,
das kann jeder machen wie er will. Und du siehst ja wie die Meinungen
hier manchmal auseinander gehen. Es gibt hier kein falsch oder richtig.
Aber wenn du eine Lib für die Allgemeinheit schreiben willst solltest du
das wie Daniel schon schrieb dem Anwender deiner Lib überlassen.
Achim schrieb:> Ich würde so weit gehen und die LED, den Taster und die Verbindung> zwischen beiden in separate Klassen einbauen. …> Soll das Ganze ein Blinklicht werden, dann muss sich eben dieser> Logikbaustein darum kümmern, dass er in jeder x-ten Timer-ISR aufgerufen> wird
Den gleichen Gedanken hatte ich bei MeinPinMitMethoden^^.
Hans-Georg Lehnard schrieb:> wenn du eine Lib für die Allgemeinheit schreiben willst solltest du> das wie Daniel schon schrieb dem Anwender deiner Lib überlassen.
Bei MeinPinMitMethoden^^ hatte ich das nicht explitit erwähnt: Genau so
war das gedacht. Es war ein Versuch einer Antwort auf das,
was DualZähler schrieb:> Deine Pin Klasse ist doch leer. Welche Methoden soll LCD da denn> benutzen?
Die Antwort wäre: Die Klasse stellt nur Basis-Methoden zur Verfügung.
Das "drumherum" passiert dann durch abgeleitete Klassen (vorzugsweise
ohne 'virtual') oder Kompositionen.
Nicht nur, dass jeder Progammierer "seinen eigenen Dickkopf" hat (das
ist gar nicht böse gemeint, jeder hat halt so seine Erfahrungen).
Auch wenn man Objektmodelle verschiedener Projekte mehreren erfahrenen
Programmierern zum Review gibt, wird ein Konsesns von der Applikation
und den individuellen Randbedingungen abhängig sein, also nicht
unbedingt für alle Projekte gleich aussehen.
chris_ schrieb:> Man könnte die Erkenntnisse dieses Threads also> durchaus verwenden, um die Arduino LIB zu optimieren. Es macht dabei> durchaus Sinn, bei den gleichen Funktionsnamen zu bleiben und kein neuen> zu erfinden.
Den Gedanken finde ich sehr attaktiv, …
1. weil man damit - wie gesagt - bereits den selben Code
auf AVR und ARM laufen lassen kann und
2. weil vielen die Benennungen der Klassen und Methoden geläufig sind.
Ich habe noch nie mit der Arduino LIB gearbeitet, also bisher alls "zu
Fuß" selbst gemacht.
@All, die die Arduino LIBs kennen:
Könnte man den "riesigen Source- und Codebloat"^^ sinngemäß mit
#ifdef DEBUG / #else / #endif
ausblenden?
Oder ist das gesamte Objektmodell zu unflexibel aufgesetzt?
Hallo DualZähler,
DualZähler schrieb:> Ich verstehe aber noch nicht, wie man deine Klassen weiter nutzen soll.> Folgendes Beispiel adaptiert von> Beitrag "Re: C++ auf einem MC, wie geht das?"
Der Code in dem verlinkten Beitrag ist von Ralf und hat mit meinem
nichts zu tun.
> Deine Pin Klasse ist doch leer. Welche Methoden soll LCD da denn> benutzen?
Aber nein, meine Klasse "Pin" ist keineswegs leer, sondern sie enthält
die vier Datenfelder "ddr", "port", "pin" und "num", die übrigens bei
mir nur "protected" statt wie bei Ralf "private" sind.
Die Klasse "Pin" ist deswegen bei mir nur eine Basisklasse für die
Klassen "InputPin", "OutputPin" und "InoutPin" mit entsprechenden
Funktionen.
> Also um klar zu sein: Du hast NIE eine automatische virtuelle Vererbung.> Du must schon explizit `virtual` hinschreiben damit der Compiler das> auch macht.
Virtuelle Vererbung würde ich gerne vermeiden, weil der Compiler dann
mit hoher Wahrscheinlichkeit virtual method tables (VMTs) anlegt, die
jedoch relativ viel Speicher kosten.
> Nur dann weiß der Compiler was er überhaupt aufrufen kann und soll.
Wie gesagt: die Kind-Klassen "xxxPin" enthalten entsprechende
Funktionen, mit denen Du einen Eingabe-Pin lesen und einen Ausgabe-Pin
high oder low setzen kannst. Ich habe Dir meine Datei "main.cpp"
angehängt, die ein in diesem Thread gewünschtes Beispiel implementiert
und meine Klassen nutzt.
Liebe Grüße,
Karl
Karl Käfer schrieb:> Virtuelle Vererbung würde ich gerne vermeiden,
ich auch :-)
> weil der Compiler dann> mit hoher Wahrscheinlichkeit virtual method tables (VMTs) anlegt, die> jedoch relativ viel Speicher kosten.
Bei meinen Versuchen immer. Deshalb sollte das ein Beispiel sein, wie
man es nicht macht. Es werden nicht nur die 'benötigten' Funktionen
eingetragen, sondern auch für die nicht benutzten, für die auch noch
Code erzeugt wird.
Obwohl...
Man könnte mal sehen, was der LTO dazu sagt...
Edit:
... der schlägt bei meinem Test noch zwei Bytes drauf.
Hallo,
weitere Überlegungen zum Thema brachten mich auf die Idee, auf einer
noch tieferen Ebene anzusetzen und zunächst einmal etwas für die
komfortablere Manipulation einzelner Register zu implementieren. Denn
was ich vor allem nicht mag, ist die häßliche Syntax zur Bitmanipulation
in C/C++. Da kann ich ja gleich in Perl programmieren... ;-)
Also habe ich zunächst einmal Template-Funktionen zur Manipulation und
Abfrage von Bits in 8- und 16-Bit-Registern geschrieben, welche sich in
"Reg.hpp" finden. Danach habe ich die Pin-Klassen in "Pin.hpp" an diese
Register-Implementierung angepaßt. Die "main.cpp" ist unverändert, alle
Dateien sind angehängt. Wenn ich diese Dateien mit "avr-g++
-mmcu=atmega32 -Os" übersetze und das Kompilat mit avr-strip(1)
bearbeite, ist das Ergebnis gemäß avr-size(1) exakt 130 Byte groß.
Damit jetzt nicht gleich wieder so ein besonders kluger Mensch aufpoppt
und uns erklärt, daß C++ ein riesiger Codebloat und furchtbar
ineffizient wäre, habe ich dieselbe Funktionalität noch einmal in C
umgesetzt. Und siehe da: mit denselben Compilereinstellungen und
avr-strip(1) bearbeitet, hat der resultierende Code einen Umfang von 148
Byte, unabhängig davon, ob ich den C-Code mit avr-g++ oder avr-gcc
übersetze.
Kurz gesagt: die C++-Implementierung ist über 12% kleiner als die in C.
Wer das selbst überprüfen möchte, findet auch die "main.c" im Anhang.
Liebe Grüße,
Karl
Karl Käfer schrieb:>> Damit jetzt nicht gleich wieder so ein besonders kluger Mensch aufpoppt> und uns erklärt, daß C++ ein riesiger Codebloat und furchtbar> ineffizient wäre, habe ich dieselbe Funktionalität noch einmal in C> umgesetzt. Und siehe da: mit denselben Compilereinstellungen und> avr-strip(1) bearbeitet, hat der resultierende Code einen Umfang von 148> Byte, unabhängig davon, ob ich den C-Code mit avr-g++ oder avr-gcc> übersetze.>
Hallo Karl,
ich lasse normal dem Compiler ein leeres main übersetzen und schau mir
an wieviel Code erzeugt wird. Das ist von MC Typ abhängig, liegt
hauptsächlich an der ISR Tabelle die mal grösser oder kleiner ist und ob
static variable initialisiert werden. Das wird auch bei normalem C so
gemacht.
Dann include ich meine templates und dann sollte sich der code nicht
vergrössern sonst schleppt die Implementation irgendwas mit. Zum Schluss
lege ich in main variable an die meine Templates instanziieren und
vergleiche die code grösse mit dem leeren main.
Wenn du noch kleineren code haben willst, musst du den Startup code auch
noch selber schreiben. Das ist aber auch kein Hexenwerk.
@Karl Käfer
Was mir die größten Sorgen macht, dass man sich zwar bei den
Minimalbeispielen an der geringen Codegröße erfreuen kann. Aber der
Bereich 'hardware abstraction' ist doch im richtigen Leben viel
umfangreicher und wird deshalb schön übersichtlich in Module verteilt.
Ich hab' das mal auseinandergepflückt (siehe Anhang). Für eine
ordentliche Code-Größe hat es sich dann mit 'AVR-Studio aufmachen -
Projekt anlegen - Dateien einfügen - Compilern' erledigt!
> kann das sein, dass hier noch das return rein muss?
Noch eine Anmerkung zu dieser Schreibweise (1 << bit).
Das hat Atmel so eingeführt und jeder macht es.
Ich finde es hässlich, und wenn man mehrere Bits maskiert wird es auch
nicht schöner .. aber das ist meine private Meinung dazu.
Aber ..
wenn du in deiner funktion shiftest kannst du ihr keine maske wo mehrere
bits gleichzeitig gesetzt sind mehr übergeben. Vorschlag: direkt die
bitmaske und keine pin nummer übergeben.
und noch was lustiges ..
enum {
bit0 = 1,
bit1 = 1<<1,
...
bit30 = 1<<30, // bis hierhin alles OK
bit31 = 1<<31 // Compiler sagt Fehler !!
und warum ? weil enums 32Bit aber signed und nicht unsigned sind und du
multiplizierts dann bei dem 31sten schieben nicht mehr mit 2 sondern
schiebst die 1 in das Vorzeichen. Übrigends auch Array indices sind
signed und können negativ sein.
Bei den C++11 enums kannst du den darunterliegenden Datentyp uint32_t
angeben und dann geht es wieder ...
Hans-Georg Lehnard schrieb:> Noch eine Anmerkung zu dieser Schreibweise (1 << bit).> Das hat Atmel so eingeführt und jeder macht es.
Das glaub ich jetzt wieder nicht, dass Atmel das erfunden hat...
> Ich finde es hässlich, und wenn man mehrere Bits maskiert wird es auch> nicht schöner ..
Wie lautet dein Gegenvorschlag? (in C, ohne ++)
Ich finde das sehr praktisch, weil man in einer zeile alle Bits angeben
kann, und sofort erkennt welches Bit 1 und welches 0 ist:
Michael Reinelt schrieb:> Wie würdest du das gerne "schöner" codieren?
Über Geschmack kann man nicht streiten. Andere (lassen wir Atmel/STM/...
mal weg), definieren z.B. sinngemäß:
1
staticconstintFOC0A=(1<<0);
2
staticconstintFOC0B=(1<<1);
3
staticconstintWGM00=(1<<2);
4
staticconstintWGM01=(1<<3);
5
staticconstintWGM02=(1<<4);
6
staticconstintCS00=(1<<5);
7
staticconstintCS01=(1<<6);
8
staticconstintCS02=(1<<7);
... oder fassen mehrere Bits zusammen. Z.B. in den MSP430-Bibliotheken
ist das so.
Michael Reinelt schrieb:> Ich finde das sehr praktisch, weil man in einer zeile alle Bits angeben> kann, und sofort erkennt welches Bit 1 und welches 0 ist:
Ich find's sogar schön, Bits zusammenzufassen...
1
TCCR0B=(0<<FOC0A)|(0<<FOC0B)|(0<<WGM02)|(0b010<<CS00);// 3Bits für CS0n
Torsten C. schrieb:> Michael Reinelt schrieb:>> Wie würdest du das gerne "schöner" codieren?>> Über Geschmack kann man nicht streiten. Andere (lassen wir Atmel/STM/...> mal weg), definieren z.B. sinngemäß:>> [c]static const int FOC0A = (1<<0);> static const int FOC0B = (1<<1);> static const int WGM00 = (1<<2);
...
> ... oder fassen mehrere Bits zusammen. Z.B. in den MSP430-Bibliotheken> ist das so.
Beantwortet aber meine Frage nicht: Wie schreibst du, dass du WGM00 auf
0 setzte, WGM01 auf 1 und WGM02 auf 0? So dass auch erkennbar ist dass
WGM00 und WGM02 auf 0?
Michael Reinelt schrieb:> Hans-Georg Lehnard schrieb:>> Noch eine Anmerkung zu dieser Schreibweise (1 << bit).>> Das hat Atmel so eingeführt und jeder macht es.> Das glaub ich jetzt wieder nicht, dass Atmel das erfunden hat...>>> Ich finde es hässlich, und wenn man mehrere Bits maskiert wird es auch>> nicht schöner ..>> Wie würdest du das gerne "schöner" codieren? (in C, ohne ++)
ich mach das gerne so ..
typedef enum {
PA0 = 0x01u,
PA1 = 0x02u,
PA2 = 0x04u,
PA3 = 0x08u,
PA4 = 0x10u,
PA5 = 0x20u,
PA6 = 0x40u,
PA7 = 0x80u
}PORTA_BITS;
typedef enum {
PB0 = 0x01u,
PB1 = 0x02u,
PB2 = 0x04u,
PB3 = 0x08u,
PB4 = 0x10u,
PB5 = 0x20u,
PB6 = 0x40u,
PB7 = 0x80u
}PORTB_BITS;
Dann ist dem Compiler auch klar, das ich unsigned meine ...
(1<<5) ist für den Compiler signed int, 0x20u nicht.
Es liegt vielleicht daran, das ich mit Binärzahlen aufgewachsen bin und
wenn ich mit Leuten von der PD11 diskutierte immer im Kopf zwischen
oktal und hex hin und her umrechnen musste aber das ist wie gesagt reine
Geschmacksache;
Und wenn ich das in C++ als Datentypen für Templates nehme.
DigitalInput<PORT_A, PB> gibt dann ein Compiler Fehler.
Hans-Georg Lehnard schrieb:> ich mach das gerne so ..
Beantwortet auch nciht meine Frage. Wie codierst du mein obiges
Beispiel? So dass sofort erkennbar ist, dass ein gewisses bit gesetzt
wird, aber auf 0?
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> ich lasse normal dem Compiler ein leeres main übersetzen und schau mir> an wieviel Code erzeugt wird.
Schon klar, aber ich bin faul und mir geht es aber nur um eine schnelle
Vergleichbarkeit. Darum übersetze ich die verschiedenen Quellcodes
einfach immer für dasselbe Ziel und vergleiche die Größen der Kompilate,
das muß für einfache Betrachtungen genügen.
> Wenn du noch kleineren code haben willst, musst du den Startup code auch> noch selber schreiben. Das ist aber auch kein Hexenwerk.
Das ist kein Hexenwerk, aber ich bin wie gesagt faul und mir geht es
hier ausschließlich um den Vergleich verschiedener Quellcodes. Darum
gehe ich einfach davon aus, daß der Startcode für dasselbe Ziel immer
derselbe und damit immer gleich groß ist.
Liebe Grüße,
Karl
Michael Reinelt schrieb:> Hans-Georg Lehnard schrieb:>> ich mach das gerne so ..>> Beantwortet auch nciht meine Frage. Wie codierst du mein obiges> Beispiel? So dass sofort erkennbar ist, dass ein gewisses bit gesetzt> wird, aber auf 0?
Michael,
es ist ganz einfach bei << muss ich denken bei bits in hex codieren
nicht .. ich will niemand überzeugen. Und bei Nullen nehm ich & und
nicht | und wenn ich selber was definiere immer in positiver Logik.
Hallo Ralf,
Ralf G. schrieb:> Was mir die größten Sorgen macht, dass man sich zwar bei den> Minimalbeispielen an der geringen Codegröße erfreuen kann. Aber der> Bereich 'hardware abstraction' ist doch im richtigen Leben viel> umfangreicher und wird deshalb schön übersichtlich in Module verteilt.> Ich hab' das mal auseinandergepflückt (siehe Anhang). Für eine> ordentliche Code-Größe hat es sich dann mit 'AVR-Studio aufmachen -> Projekt anlegen - Dateien einfügen - Compilern' erledigt!
Das wundert mich nicht, denn der Optimizer des GCC arbeitet nun einmal
nur auf den einzelnen Übersetzungseinheiten. Deswegen kann er den Code
nicht mehr anständig optimieren, sobald Du ihn über mehrere Einheiten
verteilst und diese einzeln übersetzt. [1] Die kleine Codegröße meiner
Kompilate baut jedoch darauf, daß der Optimizer erkennt, daß es sich bei
meinen Klassen in Wahrheit nur um Sammlungen von Konstanten handelt, so
daß er diese elegant von dannen optimieren kann. ;-)
Nun gibt es IMHO drei Gründe, Code auf verschiedene
Übersetzungseinheiten zu verteilen. Erstens dient das dazu, daß bei
Änderungen an einer Einheit nur diese neu übersetzt werden muß, und bei
unveränderten Einheiten nur die bereits vorhandene Objektdatei dazu
gelinkt werden muß. Bei größeren Projekten spart das Übersetzungs- und
damit Entwicklungszeit, aber bei in der Regel sehr kleinen
Mikrocontroller-Projekten hält sich der praktische Nutzen dessen wohl in
Grenzen.
Zweitens kann man, wenn man kommerzielle Bibliotheken entwickelt, diese
als Headerdateien mit vorkompilierten Objektdateien ausliefern, also
ohne mehr von seinem Quellcode offenzulegen als die Headerdateien. Das
ist bei einem OpenSource-Projekt wie diesem hier wohl eher unerwünscht,
denn da geht es ja gerade um die Weitergabe des Quellcodes.
Und letztlich dient das drittens auch der Übersichtlichkeit, und dieses
Argument sticht natürlich auch hier. Aber um dieses Ziel zu erreichen,
müssen wir die Übersetzungseinheiten nicht einzeln übersetzen und dann
zusammenlinken, sondern es reicht vollkommen aus, erst den Präprozessor
über "#include"-Direktiven die Drecksarbeit machen zu lassen. Damit hast
Du den Code übersichtlich auf mehrere Dateien verteilt, aber sie werden
trotzdem zusammen als eine Übersetzungsarbeit übersetzt und optimiert.
Da spielt dann wieder die überschaubare Größe von uC-Projekten hinein.
Ja, ich weiß, das ist nicht die gängige Praxis aus dem Lehrbuch. Aber
ich vertrete die Ansicht, daß sowohl gängige Praktiken als auch
Empfehlungen aus Lehrbüchern nicht in Stein gemeißelt sind und durchaus
auch schon mal ignoriert werden dürfen, wenn es, wie hier, gute Gründe
dafür gibt.
Liebe Grüße,
Karl
[1] Das ist, am Rande bemerkt, einer der Gründe dafür, daß ich bei
PC-Projekten den LLVM/clang++ nutze, der über mehrere Einheiten hinweg
optimieren kann, wenngleich er bei einzelnen Einheiten etwas weniger gut
optimiert.
Karl Käfer schrieb:> Aber um dieses Ziel zu erreichen,> müssen wir die Übersetzungseinheiten nicht einzeln übersetzen und dann> zusammenlinken, sondern es reicht vollkommen aus, erst den Präprozessor> über "#include"-Direktiven die Drecksarbeit machen zu lassen.
OOUuuh.
Ob du damit durchkommst? ;-)
>> kann das sein, dass hier noch das return rein muss?>> Noch eine Anmerkung zu dieser Schreibweise (1 << bit).>> Das hat Atmel so eingeführt und jeder macht es.
So sehen Bitoperationen in C nun einmal aus, da ist Atmel unschuldig.
> Ich finde es hässlich, und wenn man mehrere Bits maskiert wird es auch> nicht schöner .. aber das ist meine private Meinung dazu.>> Aber ..>> wenn du in deiner funktion shiftest kannst du ihr keine maske wo mehrere> bits gleichzeitig gesetzt sind mehr übergeben. Vorschlag: direkt die> bitmaske und keine pin nummer übergeben.
Die aktuelle Version meiner "Reg.hpp" und "Pin.hpp" hat -- schon aus
Gründen der Wiedererkennbarkeit -- die Funktionstemplates "_set()" in
"_sbi()", "_clr()" in "_cbi()" und "_get()" in "_gbi()" umbenannt, und
dazu die neuen Funktion "_sms()" und "_cms()", denen man eine komplette
Maske übergeben kann. Dir Funktionen "_set()" und "_get()" arbeiten nun
auf dem kompletten Register.
Liebe Grüße,
Karl
PS: Liebe Moderatoren / Betreiber, es wäre schön, wenn auch .hpp-Dateien
eine Codeansicht bekommen könnten. Besten Dank!
Michael Reinelt schrieb:> Wie schreibst du, dass du WGM00 auf> 0 setzte, WGM01 auf 1 und WGM02 auf 0? So dass auch erkennbar ist dass> WGM00 und WGM02 auf 0?
Entweder so wie oben gesagt, mit &=~ statt |= oder eben durch das
Zusammenfassen von mehreren Bits, wie in msp430x552x.h^^:
Hallo Ralf,
Ralf G. schrieb:> Karl Käfer schrieb:>> Aber um dieses Ziel zu erreichen,>> müssen wir die Übersetzungseinheiten nicht einzeln übersetzen und dann>> zusammenlinken, sondern es reicht vollkommen aus, erst den Präprozessor>> über "#include"-Direktiven die Drecksarbeit machen zu lassen.>> OOUuuh.> Ob du damit durchkommst? ;-)
Werden wir sehen, ich lasse mich gerne überraschen. ;-) Denn wenn ich
nicht damit durchkomme, müßte es doch bessere Argumente dagegen als
meines dafür geben. Da bin ich sehr gespannt, eventuell habe ich ja
etwas übersehen.
Liebe Grüße,
Karl
Karl Käfer schrieb:> Die kleine Codegröße meiner> Kompilate baut jedoch darauf, daß der Optimizer erkennt, daß es sich bei> meinen Klassen in Wahrheit nur um Sammlungen von Konstanten handelt, so> daß er diese elegant von dannen optimieren kann. ;-)
Mal 'ne Frage: Die Lösung ist nicht schlecht, aber wenn man "const"
Deklariert, müsste mit getrennten *.cpp Dateien das Gleiche bei raus
kommen, oder?
PS zu dieser Bitmaskenorgie: Ich finde MSP430 nicht undbedingt besser,
ist halt 'ne Alternative und Geschmackssache.
Ich finde Templates oder Inline-Funktionen am besten lesbar. Man müsste
sie halt mit "mehrere Bits auf einmal" umsetzen.
Karl Käfer schrieb:> der Optimizer des GCC arbeitet nun einmal> nur auf den einzelnen Übersetzungseinheiten.
Meines Wissens nach stimmt das nicht, GCC kann seit ca. 5 jahren LTO
(Link Time Optimization) und die macht genau das.
https://gcc.gnu.org/wiki/LinkTimeOptimization
Ob er es seit 5 Jahren kann, bezweifle ich. Aber heute kann er es sehr
gut. Kein Grund den Compiler zu wechsel. Schon gar nicht, wenn man
AVR-Code erzeugen will.
Nachdem ich hier eine ganze Zeit mitgelesen habe und eigentlich nichts
klares zu erkennen ist, finde ich das alles mittlerweile eher
abschreckend mit C++ auf einem µC zu arbeiten.
Nun, in zwei bis drei Jahren möchte ich dann auch mal mit C++ anfangen
und mal sehen, ob ich das dann anders sehe.
Ok, falsch formuliert: seit wann man darauf bauen kann, daß es
funktioniert. Nur um zu vermeiden, daß jemand mit einer Anno Tobak
Version testet und flucht. Aber sonst sind wir ja auf einer Linie: der
GCC kann das!
Michael Reinelt schrieb:> Meines Wissens nach stimmt das nicht, GCC kann seit ca. 5 jahren LTO
Es ist aber auch keine Wunderwaffe. En bisschen Mitarbeit ist gefragt.
Weil:
Ralf G. schrieb:> Für eine> ordentliche Code-Größe hat es sich dann mit 'AVR-Studio aufmachen -> Projekt anlegen - Dateien einfügen - Compilern' erledigt!
Karls Beispiel
Beitrag "Re: C++ auf einem MC, wie geht das?" in Module
verteilt, dem gcc mit LTO auf die Sprünge geholfen... und? ...
funktioniert!! Das muss aber nicht immer so sein.
Falls ich mir mit C++ eine Lib aufbaue, würde ich die gern mit
unterschiedlichen Compilern vernünftig nutzen können.
Es macht offenbar viel mehr Spaß, Stundenlang über Compiler-Optimierung
zu debattieren, statt einfach mit "const", "private" oder "final" dafür
zu sorgen, dass der Kompiler ohne Debatte ordentlich optimiert.
Zumindest nach meinem Verständnis, wie gesagt, so ähnlich wie bei
'ichbinmaldiesmaldas'^^.
F. Fo schrieb:> Nachdem ich hier eine ganze Zeit mitgelesen habe und eigentlich nichts> klares zu erkennen ist, finde ich das alles mittlerweile eher> abschreckend mit C++ auf einem µC zu arbeiten
Das liegt eher geringfügig an C++. Das sind typische
Designentscheidungen und jeder hat erst mal eine andere Vorstellung im
Kopf. Das dauert dann bis man sich auf eine Variante geeinigt hat, weil
man sich gegenseitig zu überzeugen versucht. Das man hier in Schriftform
kommuniziert (schnarch langsam) tut dann sein Übriges dazu.
TriHexagon schrieb:> Das dauert dann bis man sich auf eine Variante geeinigt hat, weil> man sich gegenseitig zu überzeugen versucht.
Guter Punkt: Noch ein Grund, warum ich Euch von 'const', 'private' oder
'final' überzeugen will:
Falls man durch seine Programmierung in anderen Modulen versucht,
'const'-Member zu verändern, meckert der Compiler und man merkt es!
Sonst merkt man sowas doch gar nicht und blickt gar nicht durch, was da
im Hintergrund passiert!
Der Optimizer gibt doch kein verlässliches 'warning' aus: "Wenn Sie das
hier anders programmiert hätten, hätte ich besser optimieren können."
Ein Entfernen eines 'const' oder 'final' oder eine Änderung 'private' ->
'protected' ist dann immer eine bewusste Design-Entscheidung.
Falls ich auf dem Holzweg sein sollte: Warum?
Torsten C. schrieb:> Noch ein Grund, warum ich Euch von 'const', 'private' oder> 'final' überzeugen will:
Also, wenn ich was zu sagen hätte, dann hättest du ab sofort
'const'-'private'-'final'-Schreibverbot.
Ralf G. schrieb:> Also, wenn ich was zu sagen hätte, dann hättest du ab sofort> 'const'-'private'-'final'-Schreibverbot.
Das ist wenig konstruktiv. Ich versuche mit den anderen hier die
(erweiterte) Frage 'C++ auf einem MC, wie geht das am besten?' im
Konsens zu beantworten.
Torsten C. schrieb:> Falls ich auf dem Holzweg sein sollte: Warum?
Darauf hat bisher niemand geantwortet. Was soll ich daraus schließen?
Ein Schreibverbot ist gar nicht nötig, das klappt auch ohne.
@Ralf G.: Ich versuche hier nicht wie Moby unbeirrbar den Sinn von C++
in Frage zu stellen. Merkst Du was?
Ralf G. schrieb:> Also, wenn ich was zu sagen hätte, dann hättest du ab sofort> 'const'-'private'-'final'-Schreibverbot.
Das ist eigentlich nur die Kurzfassung für:
Mach doch mal ein kleines Demoprogramm. Mal mit, mal ohne
'const'-'private'-'final'. Was könnte das Ergebnis sein? Entweder du
siehst, dass es nichts bringt, oder alle anderen staunen, was da für ein
exquisit optimierter Code entsteht, wenn man richtig Ahnung hat. Bis
dahin braucht man das doch nicht immer wiederholen. Oder?
Torsten C. schrieb:>> Ich versuche mit den anderen hier die> (erweiterte) Frage 'C++ auf einem MC, wie geht das am besten?' im> Konsens zu beantworten.>
Ich seh das mit meinem momentanen Kenntnisstand so :
Kann man MC mit C++ programmieren ?
Ja, aber .....
Kann man das auch effektiv ?
Ja, aber ...
Kann man das auch portabel auf andere MC Familien ?
Ja, aber ...
Wie geht das am besten ?
Kann ich nicht sagen ...
Kann man diese letze Frage online ausdiskutieren ?
Nein.
Hans-Georg Lehnard schrieb:> Ja, aber ...
Das ist doch mal 'ne klare Ansage.
Also, wer will, kann hier viele Ansätze finden, einen Port
AVR-unabhängig in eine Klasse zu packen, ohne dass es großartig aufträgt
(wenn überhaupt!). Das sollte für diesen Teil reichen. Falls jetzt noch
jemand Lust hat, könnte man als nächstes darüber diskutieren, inwiefern
es sinnvoll ist, den diversen Interruptroutinen eine Klasse zu
spendieren (Ich gehe mal ins Rennen: 'Nicht sinnvoll!'). Mit 'Timer'
geht's los. (Damit das hier auch schön 'on-topic' bleibt. Peter
Dannegger soll daran seine Freude haben!)
Hans-Georg Lehnard schrieb:> Ja, aber ...
Schade. Gemeint war das anders:
Gut: Der Compiler optimiert über mehrere Module, falls er das kann.
Besser: Der Compiler meckert, weil er nicht opimieren kann.
Im ersten Fall muss man immer wieder in die *.lss schauen, um sicher zu
gehen.
Im letzteren Fall muss man nur bei einer Fehlermeldung reagieren und
kann bewusst entscheiden, was zu tun ist, ohne wiederholt in die *.lss
schauen zu müssen, …
… wobei der Compiler genau genommen nicht meckert, 'weil er nicht
opimieren kann'^^, sondern, weil man in irgendeinem Modul was gemacht
hat, was dazu führt, dass ein Wert zur Laufzeit veränderbar sein muss.
Ralf G. schrieb:> Was könnte das Ergebnis sein?
Ich dachte, das wäre ohne Demoprogramm offensichtlich,
nachdem Hans-Georg Lehnard schrieb:> … das musst du verhindern …
Nun verstehe ich das Problem: Es ist gar nicht offensichtlich.
Danke für das konstruktive Feedback.
Hans-Georg Lehnard schrieb:> Kann ich nicht sagen ...
Wie meinst Du das? Das habe ich offenbar bereits zu oft gesagt:
=> "… doch nicht immer wiederholen. Oder?"^^
Hans-Georg Lehnard schrieb:> Kann man diese letze Frage online ausdiskutieren ?> Nein.
Der Wirkungsgrad ist wirklich mies, hast Recht!
Ich habe auch manchmal das Gefühl, dass es hier einigen darum geht, zu
zeigen, wer hier der schlaueste Programmierer ist, statt konstruktiv an
einem Konsens zu arbeiten.
Ralf G. schrieb:> Falls jetzt noch jemand Lust hat …
Was mich betrifft: Ich bin mir wegen des Wirkungsgrades^^ noch unsicher.
Ralf G. schrieb:> Mit 'Timer' geht's los.
Beim AVR stehen die ISR-Vektoren im Flash-ROM. Da wäre meine erste
Fragestellung: Wie programmiert man dann am besten? Bei einem
Zustandsautomaten wäre es effizient, wenn man mit einem 'member function
pointer' arbeitet:
1
ISR(…_vect){
2
// vectors are located in flash rom
3
(….*…_ISR)();
4
}
Normalerweise ist es jedoch effizienter, wenn die Funktion in der ISR
zur Laufzeit nicht veränderbar sein muss.
Aber für die Frage von Peter Dannegger benöttigt man ja einen
Zustandsautomaten. Den hatten wir ja schon mal andiskutiert.
Ich bin gedanklich daher ziemlich bei dem,
was Ralf G. schrieb:> Ich gehe mal ins Rennen: 'Nicht sinnvoll!'
So wird das nichts. Einfach so ins Blaue hinein, kommt nix bei raus.
Es müssen konkrete Rahmenbedingungen definiert werden. Soll eine
Abstraktion nur für die AVRs gelten? Soll sie nur für 8-Bitter mit ein
paar 100 Bytes RAM gelten? Oder ist das Ziel eine Plattform mit 32 Bit
und RAM in zweistelligen Kilobyte-Bereich oder noch mehr? Möchte man ein
Plattform-Übergreifende Abstraktion mit der man möglichst universell auf
"alle" Mikrocontroller zugreifen kann? Usw.
Ohne Vorgaben wird das nichts. Die müssen erst geklärt sein.
Ralf G. schrieb:> .... könnte man als nächstes darüber diskutieren, inwiefern> es sinnvoll ist, den diversen Interruptroutinen eine Klasse zu> spendieren (Ich gehe mal ins Rennen: 'Nicht sinnvoll!'). Mit 'Timer'> geht's los. (Damit das hier auch schön 'on-topic' bleibt. Peter> Dannegger soll daran seine Freude haben!)
Hallo Ralf,
Timer sind wieder so ein wunderbares Beispiel für Ja, aber ... ;)
Ja, du hast Recht es macht keinen Sinn irgendetwas in Klassen zu
verpacken nur damit es Klassen sind. ISR lassen sich auch nicht direkt
verpacken.
Dazu gibt es hier im Forum einen schönen Artikel:
http://www.mikrocontroller.net/articles/AVR_Interrupt_Routinen_mit_C%2B%2B
Aber, ich halte eine Klasse SystemTimer, die einen Hardware Timer
kapselt, für sinnvoll weil Timer in einem MC System üblicherweise
bergrenzt sind und ich kann nicht z.B. jedem Pin zur Entprellung einen
eigenen Timer spendieren.
@Torsten
Schau dir einfach mal Pedas Entprellroutine in C an. Was spricht dagegen
die 1:1 in eine (System)Timer Callback Methode deiner InputPin Klasse zu
übernehmen die alle 20ms aufgerufen wird.
Da gilt genau das gleiche:
Ja das kann man als "richtige" Statemachine verpacken - Aber man muss
das nicht ...
Wenn ich jetzt einen Eingang nicht einzel betrachte dann macht es
vielleicht Sinn eine Entprell Klasse zu haben die gleichzeitig mehrere
Pins verwaltet, darin wäre dann vieleicht auch eine "richtige"
Statemachine die bessere Lösung.
Wenn die Eingänge an einer externen Schieberegisterkette hängen wird man
das auf alle Fälle in einer Klasse kapseln.
Du hast immer die Entscheidung Aufwand gegen Nutzen.
Hans-Georg Lehnard schrieb:> Schau dir einfach mal Pedas Entprellroutine in C an. Was spricht dagegen> die 1:1 in eine (System)Timer Callback Methode deiner InputPin Klasse zu> übernehmen die alle 20ms aufgerufen wird.
Genau! Deshalb habe ich die von mir (mehr oder weniger sinnvollen)
vorgeschlagenen Pin-Klassen-Implementierungen immer für den ganzen Port
(bzw. für 1..8 Pins) gemacht.
Theoretiker schrieb:> So wird das nichts. Einfach so ins Blaue hinein, kommt nix bei raus.>> Es müssen konkrete Rahmenbedingungen definiert werden. Soll eine> Abstraktion nur für die AVRs gelten? Soll sie nur für 8-Bitter mit ein> paar 100 Bytes RAM gelten? Oder ist das Ziel eine Plattform mit 32 Bit> und RAM in zweistelligen Kilobyte-Bereich oder noch mehr? Möchte man ein> Plattform-Übergreifende Abstraktion mit der man möglichst universell auf> "alle" Mikrocontroller zugreifen kann? Usw.>> Ohne Vorgaben wird das nichts. Die müssen erst geklärt sein.
Die Vorgaben sind wie immer ...
Wir müssen schneller auf den Markt, es darf nichts kosten und es muss
in 2 Wochen fertig sein. ;)
Hans-Georg Lehnard schrieb:> Was spricht dagegen> die 1:1 in eine (System)Timer Callback Methode deiner InputPin Klasse zu> übernehmen die alle 20ms aufgerufen wird.
Da fragst Du den Richtigen: Den, der immer 'Ja aber ...' sagt. ;-)
Ich bin inzwischen auch fest davon überzeugt, dass eine uCpp ein Fass
ohne Boden ist, selbst falls man sich z.B. nur auf 8-Bit AVR
beschränken würde.
Der eine braucht 4 GPIOs 'am Stück' um ein Display anzusteuern, der
nächste einzelne GPIOs - irgendwo verteilt - als Slave-Select für SPI,
der übernächste … usw.
Beim Timer mit 2 Kanälen ist das noch schlimmer: Der eine Kanal macht
PWM, der andere IC (Input-Compare), oder beide machen PWM oder beide
machen IC.
Im STM32 sind's noch mehr Kanäle.
Wenn man alle Varianten annäherd so effizient umsetzen will, wie mit
ASM, dann gibt es eine Inflation von Klassen oder Templates die niemand
debuggen oder nach einem Jahr noch verstehen kann.
Vorschlag für einen Ausweg (um hier zerrissen zu werden): Man baut sich
für das eine oder andere wiederkehrende Problem ein Code-Snippet (für
den Manager, siehe Bild), so wie man es gerade braucht.
Um nach diesen Überlegungen Deine Frage zu beantworten: Es gibt
Projekte, bei denen die Rahmenbedingungen keinen 'Systick' (ich hätte
beinahe 'Sysfick' geschrieben) zulassen. Nichts spricht aus meiner
Sicht ansonsten dagegen.
Falls statt einer uCpp-Lib Interesse an einer Snippet-Sammlung besteht,
könnte man eine solche als Projekt starten.
Als einer der hier interessiert mitliest, habe ich eigentlich nur auf
die Erkenntnis von Torsten gewartet.
C++ ist sinnvoll für gewisse reine Software-Themen (PT1- Glieder oder
allgemein digitale Filter als gutes Beispiel) aber nicht für die
harware-nahen Sachen.
Torsten hat eh schon ein paar Beispiele genannt; immer dann wenn die
Ausnutzung der Hardware "kreativ" wird, ists Essig mit der schönen
schnöden Abstraktion in Klassen.
Anfangen könnte man schon bei ganz simplen Dingen wie Wired-And auf den
Pins: Hier wird der Pin nicht über das PORT-Register gesteuert, sondern
über DDR. Das könnte man ja noch hinkriegen...
Timer sind aus meiner Sicht ganz schwierig: Dynamisches Umschalten
zwischen den Modi, dynamisches Weiterschalten des Output-Compare, ...
Software-Interrupts? EEPROM-Write zweckentfremdet?
Kreative Verwendung von "artfremden" Dingern als Timer? (z.B. ADC
freerunning als Timer)
Genau diese kreativen (und genialen) Verwendungen der vielfältigen
Möglichkeiten der Hardware kriegst du nie in ein Objekt-Modell.
Willst du das alles C++-mäßig simplifizieren, bist du bei Arduino. Nett,
aber beschränkt. Sobald du mehr willst, wirfst du das über Bord.
Schlimmstenfalls hast du dann ein mischmasch, das keiner mehr lesen und
verstehen kann.
Peters Frage dürfte damit beantwortet sein.
Ralf G. schrieb:> Vor allen Dingen: Das ist nicht das, was ich mir unter effizient> vorgestellt habe. Vielleicht hat ein Profi 'ne bessere Idee.
O je, ihr habt diesen Unsinn ja noch immer nicht zu Grabe getragen.
Ja, ich hab ne bessere Idee - und ich hatte sie bereits viel weiter oben
geäußert: Laßt all diesen C++ Krimskram bleiben und kommt auf den
Teppich herunter. Vernünftige Peripherie-Treiber, die eine echte Arbeit
leisten und ne hardwareunabhängige Schnittstelle zur Applikation
herstellen, sind viel effektiver, viel verständlicher und machen die
Applikation viel eher portabel, sofern das überhaupt geht. Sowas wie
"LED1.toggle = KEY1.press" oder auch "LED1::setOutput(xpcc::Gpio::Low);"
sind Mumpitz und zu nichts nütze. Das ist nur digitale Onanie.
W.S.
Und ich habe darauf gewartet das jetzt wieder einer behauptet C++ sei
dafür grundsätzlich nicht geeignet.
Das jemand die Komplexität eines Problemes unterschätzt ist doch völlig
normal, das passiert jedem und vor allen Dingen wenn er neu auf dem
(embedded) Gebiet ist. Das ist aber überall so völlig unabhängig von der
Sprache.
Und wie entwerfe ich meine Klassen gehört nicht zu den leichtesten
Übungen bei objektorientierten Sprachen.
Es war doch nicht Tomas, sondern die lieben Assembler Programmierer, die
geschrieen haben das wär doch alles trivial.
Gegen Vorurteile kämpfen ist wie gegen Windmühlen.
Und bei den SOC wie zum Beispiel dem Zynq, das weisst du ja als
Softwerker nicht mal was dein Kollege dir für eine Hardware ins FPGA
bastelt. Das wird es erst richtig Hardwarenah und da möchte ich mal
sehen wer dann noch in Assembler programmieren will.
Hallo Michael,
Michael Reinelt schrieb:> Meines Wissens nach stimmt das nicht, GCC kann seit ca. 5 jahren LTO> (Link Time Optimization) und die macht genau das.
Wenn das korrekt funktionieren würde, dann würde die Kompilatgröße nicht
explodieren, sobald man den Quellcode auf mehrere Übersetzungseinheiten
aufteilt -- aber sie tut es. Das zeigt mir, daß die LTO in diesem Falle
offenbar nicht wie gewünscht funktioniert, ein oberflächlicher Blick ins
Mapfile scheint meinen Verdacht zu bestätigen. Schade.
Liebe Grüße,
Karl
Hallo F.,
F. Fo schrieb:> Karl Käfer schrieb:>> Dahinter verbirgt sich eine Art C++,>> Arduino language is based on C/C++. It links against AVR Libc and allows> the use of any of its functions; see its user manual for details.>> Wer es selber nachlesen will:> http://arduino.cc/en/pmwiki.php?n=Reference/HomePage
Der Sinn erschließt sich, wenn Du den ganzen Satz liest. Der geht
nämlich weiter mit: "die dafür gemacht und optimiert wurde, von
absoluten Anfängern und Nichttechnikern benutzt zu werden". Die "eine
Art" bezieht sich also auf den Teil "die dafür gemacht und optimiert
wurde".
Liebe Grüße,
Karl
Mein lieber Karl, mein Englisch ist zwar nicht (mehr) auf C Niveau, aber
du liest da was (oder an anderer Stelle?) was da nicht steht.
Es ist wohl schon 30 Jahre her, dass ich mich dort mal längere Zeit
aufgehalten hatte, aber aus dem Satz,
Karl Käfer schrieb:> Arduino language is based on C/C++.,
kann man nicht mehr raus lesen, als da steht.
Da steht, dass Arduino auf C/C++ basiert. Nicht mehr und nicht weniger.
Ob dir das nun gefällt oder nicht, so steht es da.
Ich hatte ja mit Arduino angefangen, aber ich habe mir natürlich nicht
die "Basis" dazu angesehen, noch fehlt mir bis heute das Wissen das gut
genug zu beurteilen.
Ich will ja nicht behaupten, dass du unrecht hast, aber dass das da
steht wie es steht, ist für jeden zu lesen.
Hi Ralf,
Ralf G. schrieb:> Karls Beispiel> Beitrag "Re: C++ auf einem MC, wie geht das?" in Module> verteilt, dem gcc mit LTO auf die Sprünge geholfen... und? ...> funktioniert!! Das muss aber nicht immer so sein.
Oh, das funktioniert doch? Ui. Dann nehme ich alles zurück und behaupte
das Gegenteil!
Liebe Grüße,
Karl
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> Kann man diese letze Frage online ausdiskutieren ?>> Nein.
Daß wir in dieser Diskussion immer wieder festhängen, liegt primär an
ihrem rekursiven Charakter. Das betrifft vor allem Nebensächliches wie
"Templates oder nicht", aber auch "const"-, "private"- und
"final"-Deklarationen.
Ohne konkreten Code als Diskussionsgrundlage laufen diese Fragen aber
leider immer wieder ins Leere. Leider scheine ich hier aber derzeit der
Einzige zu sein, der aktiv an Code arbeitet und diesen wiederholt
gepostet hat.
Aus mir unerfindlichen Gründen wird über meinen Code aber kaum
diskutiert. Stattdessen verliert sich die Diskussion wiederholt in
Details, hängt aber mangels konkreten Codes als Diskussionsgrundlage
frei in der Luft.
Mir ist unklar, wie wir das lösen können. Aber vielleicht mag ja der
Eine oder andere einfach mal meinen Code lesen und eine fundierte Kritik
daran äußern. Vielen Dank.
Liebe Grüße,
Karl
Hallo Michael,
Michael Reinelt schrieb:> C++ ist sinnvoll für gewisse reine Software-Themen (PT1- Glieder oder> allgemein digitale Filter als gutes Beispiel) aber nicht für die> harware-nahen Sachen.
Hast Du mal einen Blick in den von mir geposteten Code geworfen? Wenn
ja, wüßte ich gerne, wie Du zu dieser Aussage kommst.
Liebe Grüße,
Karl
Hallo W.,
W.S. schrieb:> O je, ihr habt diesen Unsinn ja noch immer nicht zu Grabe getragen.>> Ja, ich hab ne bessere Idee - und ich hatte sie bereits viel weiter oben> geäußert: Laßt all diesen C++ Krimskram bleiben und kommt auf den> Teppich herunter. Vernünftige Peripherie-Treiber, die eine echte Arbeit> leisten und ne hardwareunabhängige Schnittstelle zur Applikation> herstellen, sind viel effektiver, viel verständlicher und machen die> Applikation viel eher portabel, sofern das überhaupt geht. Sowas wie> "LED1.toggle = KEY1.press" oder auch "LED1::setOutput(xpcc::Gpio::Low);"> sind Mumpitz und zu nichts nütze. Das ist nur digitale Onanie.
Ich bilde mir ein, mittlerweile ziemlich eindeutig bewiesen zu haben,
daß man mit C++ auch für Mikrocontroller elegante Programme schreiben
kann, die kein einziges Byte größer sind als ihr Äquivalent in C, dafür
aber sehr viel besser zu Lesen und zu Warten sind.
Weißt Du, labern und mosern kann jeder. Deswegen schlage ich vor, daß
wir uns die Sache hier ganz einfach machen. Wenn Du es schaffst, mit C
oder Assembler etwas zu implementieren, das a) genau so lesbar ist wie
meine angehängte "main.cpp" und b) zu nicht mehr als 146 Byte Code
kompiliert, dann laß uns einfach mal Deinen großartigen Code sehen. Wenn
Du das nicht schaffst, dann schlage ich vor, daß Du Dich irgendwoanders
konstruktiver einbringst und uns hier nicht weiter störst.
Jetzt bin ich aber wirklich mal gespannt, ob Du Manns genug bist, Dich
dieser einfachen Herausforderung zu stellen. Viel Erfolg!
Liebe Grüße,
Karl
Karl Käfer schrieb:> Hast Du mal einen Blick in den von mir geposteten Code geworfen?
Ja, habe ich.
> Wenn ja, wüßte ich gerne, wie Du zu dieser Aussage kommst.
Das bissi LED-einschalten ist aber noch lange nicht "kreative"
Verwendung von Hardware. mach mal Wired-And dazu, Entprellung, einen
Timer mit OCR-Leiter, dann schauen wir weiter.
Karl Käfer schrieb:> Leider scheine ich hier aber derzeit der> Einzige zu sein, der aktiv an Code arbeitet und diesen wiederholt> gepostet hat
Nicht wirklich der einzige! Oder meinst Du das ernst? Ich habe im Moment
den USART und einen Timer an der Backe und kann auch gern Beispiele
posten. Dadurch wird der Thread jedoch nicht übersichtlicher. Also
bleiben wir beim GPIO:
Karl Käfer schrieb:> Aus mir unerfindlichen Gründen wird über meinen Code aber kaum> diskutiert.
Schau doch bitte mal, ob bei Deiner 'class Pin' im *.lss wirklich immer
'sbi' und 'cbi' auftauchen. Ich habe da Bedenken.
Falls immer 'sbi' und 'cbi' compiliert wird, liegt der Mangel an Kritik
vielleicht daran, dass keiner was dran auszusetzen hat, das Kapitel also
abgeschlossen ist.
Hallo Michael,
Michael Reinelt schrieb:> Torsten hat eh schon ein paar Beispiele genannt; immer dann wenn die> Ausnutzung der Hardware "kreativ" wird, ists Essig mit der schönen> schnöden Abstraktion in Klassen.> [...]> Timer sind aus meiner Sicht ganz schwierig: Dynamisches Umschalten> zwischen den Modi, dynamisches Weiterschalten des Output-Compare, ...> [...]> Genau diese kreativen (und genialen) Verwendungen der vielfältigen> Möglichkeiten der Hardware kriegst du nie in ein Objekt-Modell.
Man muß nicht alles in Klassen und Objektmodelle stopfen. Und mit einer
C++-Bibliothek, die auf der AVR-Libc aufbaut, steht deren Funktionalität
auch weiterhin uneingeschränkt zur Verfügung.
> Willst du das alles C++-mäßig simplifizieren
Entschuldige, darum geht es doch gar nicht. Es reicht doch schon aus,
die vielen "normalen", "unkreativen" Dinge les- und wartbarer zu machen.
Wenn Du Deine Hardware voll ausreizen und kreativ nutzen willst, kein
Problem: dann kannst Du C und sogar Assembler innerhalb von C++-Code
nutzen.
Aber wenn Du nur "normale" Sachen machen, und zum Beispiel einen Pin
wackeln willst, was sicherlich 80 oder sogar 90% der Anwendungsfälle
abdeckt: dann hilft C++, den Code besser zu organisieren.
> Schlimmstenfalls hast du dann ein mischmasch, das keiner mehr lesen und> verstehen kann.
Was meinst Du? C ist (weitestgehend) eine Untermenge von C++. Wer C++
benutzt, muß zwangsläufig C können. Könntest Du Deinen Einwand anhand
eines Codebeispiels aufzeigen?
Liebe Grüße,
Karl
Karl Käfer schrieb:> einen Pin> wackeln willst, was sicherlich 80 oder sogar 90% der Anwendungsfälle> abdeckt
Also bei mir ist das signifikant kleiner 10%
Karl Käfer schrieb:>> Schlimmstenfalls hast du dann ein mischmasch, das keiner mehr lesen und>> verstehen kann.>> Was meinst Du? C ist (weitestgehend) eine Untermenge von C++. Wer C++> benutzt, muß zwangsläufig C können. Könntest Du Deinen Einwand anhand> eines Codebeispiels aufzeigen?
Nehmen wir mal an ich hätte eine Wunderwuzzi-Timer-Klasse. Dann hab ich
einen "kreativen" Anwendungsfall, den die Klasse überhaupt nicht
abdeckt, und ich muss erst wieder an TCCR "rummachen". Das meine ich mit
MischMasch.
Hallo,
Karl Käfer schrieb:> Wenn das korrekt funktionieren würde, dann würde die Kompilatgröße nicht> explodieren, sobald man den Quellcode auf mehrere Übersetzungseinheiten> aufteilt -- aber sie tut es.
Da Ralf festgestellt hat, daß das nicht der Fall ist, und ich dies
anhand meines Codes verifizieren konnte, ist diese Aussage widerlegt.
Liebe Grüße,
Karl
Michael Reinelt schrieb:> Nehmen wir mal an ich hätte eine Wunderwuzzi-Timer-Klasse. Dann hab ich> einen "kreativen" Anwendungsfall, den die Klasse überhaupt nicht> abdeckt, und ich muss erst wieder an TCCR "rummachen". Das meine ich mit> MischMasch.
Wieso. TCCR wird als protected definiert und die Klasse
WunderwuzziWurschtel wird von Wunderwuzzi abgeleitet. Wers braucht, kann
es doch machen.
Icn bin gerade am Timer mit zwei Kanälen und es geht mir wie dem
Beamten:
1
Ein Beamter macht Urlaub auf einem Bauernhof. Der Bauer gibt ihm
2
was zu tun: „Hier sind zwei Schüsseln, eine große und eine kleine
3
und dort hinten ist ein Sack Kartoffeln. Sortieren Sie bitte die
4
großen Kartoffeln in die große Schüssel und die kleinen Kartoffeln
5
in die kleine!“ Nach einer Stunde kommt der Bauer wieder. Da sitzt
6
der Beamte vor seinen zwei Schüsseln. In der kleinen ist eine
7
kleine Kartoffel, in der großen ist eine große Kartoffel und in
8
der Hand hält er eine mittelgroße und fragt den Bauern: „Ist die
9
jetzt groß oder klein?“
Die Methoden für Kanal A und für Kanal B müssen unterschiedlich sein.
Mache ich nun zwei Methoden mit unterschiedlichen Bezeichnungen oder
eine gemeinsame mit einem Parameter und if / else?
Man kann sich mit diesem c++-Kram ganz schön aufhalten.
'if/else' ist marginal ineffizienter aber schneller global geändert.
Bei unterschiedlichen Bezeichnungen müsste man mit 'suchen/ersetzen'
arbeiten oder doch wieder mit #define arbeiten.
Ich überlege schon länger als der Beamte. :-(
Torsten C. schrieb:> Die Methoden für Kanal A und für Kanal B müssen unterschiedlich sein.
Wiso? Was ist beim einen anders als beim anderen? Kann man es nicht per
Template parameter lösen?
Nur mal so nebenbei: ZX,Y und Z sind Pointer-Register. Wenn man sie
nicht als solche benutzt, sondern z.B. XL als 8-Bit Wert, wie hier oft
zu sehen, dann sollte man es R26 nennen. Andernfalls setzt man sich dem
Vorwurf aus, die Programmfunktion verschleiern zu wollen. Du hast da ein
tolles Beispiel geliefert für das, was viele an ASM-Programmen stört.
Ein Programm, nach meinem Verständnis, soll im Source-Code die
Problemlösung beschreiben. Das kann man mit den schon zu sehenden
Beispielen sehr gut. Wenn ich in C++
1
Led7.toggle();
schreibe, dann kann ich in der entsprechenden Klasse die HW-Detail,
alter/neuer AVR, mit PIN-Toggle HW oder ohne versteckt. Einmal getestet,
ist das gegessen. Und ob der Compiler dann 0 oder n temp. Register
braucht um das umzusetzen, darüber haben sich andere schon den Kopf
zerbrochen.
Wenn es also wirklich Bedarf nach ASM gibt (wegen Timing, etc) dann muß
da für mich deutlich mehr Kommentar als Code stehen.
Gratulation, Du hast meinen Vorurteil bestätigt. BTW, ich programmieren
kleine bis ganz große Kisten nun seit 35Jahren (und lebe gut davon), da
war viel Zeit aus Fehlern zu lernen.
chris_ schrieb:>>Led7.toggle();>> Hmm ... das hier vielleicht:>> http://playground.arduino.cc/Code/DigitalToggle
Ein schönes Beispiel wie man es nicht machen sollte ...
void digitalToggle(uint8_t P)
{
*portInputRegister(digitalPinToPort(P)) = digitalPinToBitMask(P);
}
Ich gehe jetzt mal davon aus, das wir einen Ausgang Toggeln wollen und
das der Code irgendwie funktioniert ...
portInputRegister(digitalPinToPort(P))
// das liefert uns die Adresse eines Ausganges ?
*portInputRegister(..) = digitalPinToBitMask(P) // das toggelt ?
void digitalToggle(uint8_t P)
// das funktioniert auf einem ARM ?
Hans-Georg Lehnard schrieb:> void digitalToggle(uint8_t P)> {> *portInputRegister(digitalPinToPort(P)) = digitalPinToBitMask(P);> }
Hmm. Könnte ich mir jetzt was darunter vorstellen. Schlimm finde ich das
nicht.
Außer:
PINx beschreiben zum Umschalten, ist jetzt eben 'die allerneueste Mode'
;-)
Funktioniert aber nicht immer. (Ich lass das erstmal.)
Ralf G. schrieb:> Hans-Georg Lehnard schrieb:>> void digitalToggle(uint8_t P)>> {>> *portInputRegister(digitalPinToPort(P)) = digitalPinToBitMask(P);>> }>> Hmm. Könnte ich mir jetzt was darunter vorstellen. Schlimm finde ich das> nicht.> Außer:> PINx beschreiben zum Umschalten, ist jetzt eben 'die allerneueste Mode'> ;-)> Funktioniert aber nicht immer. (Ich lass das erstmal.)
Für dich ist das völlig normal, das man eine Funktion, die einen Apfel
zurückliefern soll, "GibMirEineBirne" tauft und das das alles so oder so
nur Modeerscheinungen sind ?
Für mich ist der Thread damit auch erledigt ...
Hans-Georg Lehnard schrieb:> Ralf G. schrieb:>> Hans-Georg Lehnard schrieb:>>> void digitalToggle(uint8_t P)>>> {>>> *portInputRegister(digitalPinToPort(P)) = digitalPinToBitMask(P);>>> }>>>> Hmm. Könnte ich mir jetzt was darunter vorstellen. Schlimm finde ich das>> nicht.>> Außer:>> PINx beschreiben zum Umschalten, ist jetzt eben 'die allerneueste Mode'>> ;-)>> Funktioniert aber nicht immer. (Ich lass das erstmal.)>> Für dich ist das völlig normal, das man eine Funktion, die einen Apfel> zurückliefern soll, "GibMirEineBirne" tauft und das das alles so oder so> nur Modeerscheinungen sind ?
Hier wird doch nichts geliefert!?
Hier wird nur was gemacht.
'_void_ digitalToggle(uint8_t P)' heißt für mich: 'Digitales Umschalten
von...'
Wenn jetzt in 'P' Port und Pin codiert ist (Hi/Low im Byte) dann holt
sich
'digitalPinToPort(P)' die Portadresse, 'digitalPinToBitMask(P)' die
Pinmaske, ... '*portInputRegister' ..., hmm, hätte ich jetzt mit so
einer Konstruktion verglichen: '_SFR_MEM8(sfr_regs::_pin)'
Und zum Schluss wird sowas wie 'PORTB = 1 << 6' draus.
> Ralf G. schrieb:> PINx beschreiben zum Umschalten, ist jetzt eben 'die allerneueste Mode'> ;-)> Funktioniert aber nicht immer. (Ich lass das erstmal.)
Das würde ich dann noch mal hervorheben wollen:
->> Funktioniert aber nicht immer. <<-
Gerade weil das "nicht immer funktioniert", ist es gut eine Template
Klasse zu haben, die weiß, wann es geht und die der Compiler zu den 1..3
AVR-Opcodes reduziert, die jeweils gebraucht werden.
Ralf G. schrieb:> Hier wird doch nichts geliefert!?> Hier wird nur was gemacht.> '_void_ digitalToggle(uint8_t P)' heißt für mich: 'Digitales Umschalten> von...'> Wenn jetzt in 'P' Port und Pin codiert ist (Hi/Low im Byte) dann holt> sich> 'digitalPinToPort(P)' die Portadresse, 'digitalPinToBitMask(P)'
Kann man sich ja noch vorstellen
die
> Pinmaske, ... '*portInputRegister' ..., hmm, hätte ich jetzt mit so> einer Konstruktion verglichen: '_SFR_MEM8(sfr_regs::_pin)'>
*portInputRegister(..) ist eine Funktion und die hat einen Pointer als
Rückgabe der dereferenziert wird ... Mir ist schon klar was da gemacht
wird aber warum packe ich da das wort input in den Funktionsnamen.
> Und zum Schluss wird sowas wie 'PORTB = 1 << 6' draus.>>> Funktioniert aber nicht immer. (Ich lass das erstmal.)> Das würde ich dann noch mal hervorheben wollen:> ->> Funktioniert aber nicht immer. <<-
PORTB = 1 << 6' toggelt niemals einen Pin auch nicht manchmal
Das setzt bit6 auf 1 und alle anderen Bits auf 0.
Hans-Georg Lehnard schrieb:> *portInputRegister(..) ist eine Funktion und die hat einen Pointer als> Rückgabe der dereferenziert wird ... Mir ist schon klar was da gemacht> wird aber warum packe ich da das wort input in den Funktionsnamen.
Weil das Ding die Adresse von PINx liefert, nicht von PORTx.
Hans-Georg Lehnard schrieb:> PORTB = 1 << 6' toggelt niemals einen Pin auch nicht manchmal
Hat ja auch keiner behauptet.
Aber PINB = 1 << 6 toggelt auf neueren AVRs einen Pin.
Jörg Wunsch schrieb:> Hans-Georg Lehnard schrieb:>> PORTB = 1 << 6' toggelt niemals einen Pin auch nicht manchmal>> Hat ja auch keiner behauptet.>> Aber PINB = 1 << 6 toggelt auf neueren AVRs einen Pin.
Ok, danke das ist mir neu und bisher noch bei keinen anderen MC
begegnet.
Und ich dachte auch immer Arduino kennt nicht nur neue Avrs ...
Hmm ich spare bei dem Toggle Befehl mit dem Input Pin Register 2Byte im
Flash und 2 Takte während der Laufzeit dafür handle ich mir etwas nicht
portables in einer "portablen" Lib ein.
Kann man das wenigstens beim AVR mit
#if _AVR_ARCH_ >= XXX abfangen ?
Moby AVR schrieb im Beitrag #3995376:
> Um die geht es aber hier nicht, sondern um einfache Mikrocontroller.
Als wir angefangen haben, waren die Computer in der 10000 DM Klasse so
groß wie heutigen Mikrocontroller. Jede Sprache hat ihre Berechtigung.
Nur hier geht es nicht um AS sondern um C++.
Konrad S. schrieb:> Weil das Ding die Adresse von PINx liefert, nicht von PORTx.
Ja, verdammt. :-( ich meinte ja auch PINx (kleiner Schreibfehler...)
Ralf G. schrieb:> PINx beschreiben zum Umschalten, ist jetzt eben 'die allerneueste Mode'> ;-)
Hans-Georg Lehnard schrieb:> Hmm ich spare bei dem Toggle Befehl mit dem Input Pin Register 2Byte im> Flash und 2 Takte während der Laufzeit dafür handle ich mir etwas nicht> portables in einer "portablen" Lib ein.>> Kann man das wenigstens beim AVR mit> #if AVR_ARCH >= XXX abfangen ?
Also ich ignoriere das erstmal :-)
Ralf G. schrieb:> Ja, verdammt. :-( ich meinte ja auch PINx (kleiner Schreibfehler...)
Und auch das solltest du zurücknehmen ..
> Und zum Schluss wird sowas wie 'PORTB = 1 << 6' draus.
wenn du 'PINB = 1 << 6' meinst ...
Tu das nie wieder ich könnte es dir übel nehmen ;)
Hallo Jörg,
Jörg Wunsch schrieb:> Hans-Georg Lehnard schrieb:>> PORTB = 1 << 6' toggelt niemals einen Pin auch nicht manchmal>> Hat ja auch keiner behauptet.>> Aber PINB = 1 << 6 toggelt auf neueren AVRs einen Pin.
Was genau heißt in diesem Zusammenhang "neuere AVRs"?
Liebe Grüße,
Karl
Hallo Karl,
wenn du noch dabei bist eine Lib zu bauen würde ich solche
"Spezialitäten" ehrlich gesagt nicht berücksichtigen und den portablen
Weg wählen. Sonst hast du wirklich ein Fass ohne Boden. Ähnliches gilt
auch für den Pull up der hat mal ein eigenes Register (PUE) dann wird er
wieder über das PORTx Register eingeschaltet.
Das Problem dabei ist du musst nach jeweiligen Chip unterscheiden und
auch innerhalb eines Chips funtioniert das nur wenn der PIN als
digitaler Ausgang benutzt wird. Wenn der Pin z.B. als Timer Ausgang
konfiguriert ist funktioniert das nicht mehr.
Falls ich die Datenblätter falsch verstanden habe bitte korrigieren.
Die Logik von Atmel ist:
Ein "ReadOnlyRegister" ist ein Register, wenn man da was reinschreibt
ändert sich zwar nichts in diesem Register aber in einem anderen kann
das manchmal der Fall sein.
Wenn du eine Anwendung wie den Transistortester schreibts, der bei der
Kapazitätsmessung Ladungsimpulse definierter Länge auf den Kondensator
gibt, kann das Vorteile bringen weil es die Auflösung erhöht, aber für
eine LED blinken zu lassen ist das völlig egal ob die 2 Takte länger
braucht oder nicht.
Und LED blinken lassen werden die Hauptanwender deiner Lib sein.
Hans-Georg Lehnard schrieb:> Das Problem dabei ist du musst nach jeweiligen Chip unterscheiden und> auch innerhalb eines Chips funtioniert das nur wenn der PIN als> digitaler Ausgang benutzt wird. Wenn der Pin z.B. als Timer Ausgang> konfiguriert ist funktioniert das nicht mehr.
Man kann ja eine toogle Methode zur verfügung stellen mit einer
weitgehend allgemeinen Lösung. Und dann die Speziallösungen mit dem
Präprozessor zugänglich machen. Das ist ja das schöne an einer
Abstraktionsschicht im Vergleich mit dem direkten Registerhandling.
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> Hmm ich spare bei dem Toggle Befehl mit dem Input Pin Register 2Byte im> Flash und 2 Takte während der Laufzeit dafür handle ich mir etwas nicht> portables in einer "portablen" Lib ein.
Was ist eine Abstraktionsschicht?
Liebe Grüße,
Karl
Hallo Konrad,
Konrad S. schrieb:> Hans-Georg Lehnard schrieb:>> Kann man das wenigstens beim AVR mit>> #if AVR_ARCH >= XXX abfangen ?>> Kaum, denn es kann immer wieder mal was Neues dazukommen.
Bitte korrigiere mich, wenn ich etwas Falsches sage, aber wenn mich
meine Erinnerung nicht trügt, macht man etwas mit solchen Bibliotheken,
das man "Pflege" nennt. ;-)
> Übrigens ... bei den ATxmega-Controllern sieht's nochmal anders aus.
Ja... äh, und?
Liebe Grüße,
Karl
@Karl Käfer
Mit der Abfrage von AVR_ARCH erfährt man etwas über den Befehlssatz,
nicht über Peripherie-Eigenschaften. Abfragen (speziell
Vergleichsoperatoren wie >= usw.) lassen sich zuverlässig nur für
diejenigen Eigenschaften auf ein Makro anwenden, für die das Makro
designt wurde. Mag sein, dass einige Peripherie-Eigenschaften mit
AVR_ARCH korrespondieren, aber ich halte das nicht für eine zugesicherte
Eigenschaft. Die Bibliotheks-Pflege müsste für jeden neu hinzukommenden
Controller untersuchen, ob die bisherigen Annahmen bezüglich AVR_ARCH
und Peripherie-Eigenschaften noch zutreffen. Apropos ... noch sehe ich
so eine Bibliothek in weiter Ferne - wenn dieses verschwommene Pünktchen
am Horizont überhaupt eine Bibliothek ist, kein Fatamorgähnchen. ;-)
Karl Käfer schrieb:> Ja... äh, und?
Nun, die ATxmega-Ports weisen erhebliche Unterschiede zu den
ATtinys/ATmegas auf, sowohl hinsichtlich Konfiguration als auch
bezüglich Benutzung. Die Ports lassen sich ausgehend von ihrer
jeweiligen Basisadresse über eine Struktur ansprechen, eine zugesicherte
Eigenschaft.
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> wenn du noch dabei bist eine Lib zu bauen würde ich solche> "Spezialitäten" ehrlich gesagt nicht berücksichtigen und den portablen> Weg wählen. Sonst hast du wirklich ein Fass ohne Boden. Ähnliches gilt> auch für den Pull up der hat mal ein eigenes Register (PUE) dann wird er> wieder über das PORTx Register eingeschaltet.
In meinen Partdescription-XML-Dateien, 194 Stück an der Zahl, gibt es
139 ATtiny und ATmega, welche ich für mich zunächst einmal als
Zielplattform festgelegt habe. Von diesen 139 Tinys und Megas haben
insgesamt 7 ein PUE[ABC]-Register und insgesamt 29 können einen Pin
mittels Beschreibens des PIN-Registers umschalten. Dafür arbeite ich
derzeit an einem kleinen Codegenerator, der aus diesem XML dann
Präprozessor-Code generiert.
> Das Problem dabei ist du musst nach jeweiligen Chip unterscheiden und> auch innerhalb eines Chips funtioniert das nur wenn der PIN als> digitaler Ausgang benutzt wird. Wenn der Pin z.B. als Timer Ausgang> konfiguriert ist funktioniert das nicht mehr.
Wenn ich vor jedem "toggle()" testen will, daß der Pin nicht für Timer
konfiguriert ist, lande ich wieder bei Arduino. Die machen das IIRC so,
und für deren anvisierte Zielgruppe ist es auch absolut sinnvoll.
Wie oben schon einmal erwähnt, ist das aber eine philosophische Frage.
Nach meiner Auffassung ist es die wichtigste Aufgabe einer Bibliothek,
Dinge zu ermöglichen, und nicht, sie zu verhindern. Deswegen will ich
mögliche Fehler höchstens dann abfangen, wenn es nichts kostet. Damit
möchte ich maximale Effizienz und Flexibilität erreichen.
Wer es anders haben möchte, ist nicht meine Zielgruppe und hat mit
Arduino eine funktionierende, aber weniger flexible und effiziente
Alternative.
Liebe Grüße,
Karl
Karl Käfer schrieb:> Hallo Hans-Georg,> Was ist eine Abstraktionsschicht?
Das ist genau das, was dieser Diskussion fehlt ;)
Ich zittiere mal Wikipedia ...
Eine Abstraktion bezeichnet meist den induktiven Denkprozess des
Weglassens von Einzelheiten und des Überführens auf etwas Allgemeineres
oder Einfacheres.
Wir verzetteln uns hier immer wieder in Kleinigkeiten und in die Tiefen
der Avr Bitpopelei.
Dem Hobbyanwender ist diese Diskussion völlig wurscht, der bestellt sich
den passenden Chip, programmiert ihn in seine Lieblingssprache, und
damit ist das Problem erledigt.
Der gewerbliche Anwender, der mindestens 10 Jahre Ersatzteile liefern
muss kann das leider nicht so einfach tun ..
Karl Käfer schrieb:> Hallo Hans-Georg,
...
> Nach meiner Auffassung ist es die wichtigste Aufgabe einer Bibliothek,> Dinge zu ermöglichen, und nicht, sie zu verhindern.
Kann man so sehen ...
> Deswegen will ich mögliche Fehler höchstens dann abfangen, wenn es nichts >
kostet. Damit möchte ich maximale Effizienz und Flexibilität erreichen.
>> Wer es anders haben möchte, ist nicht meine Zielgruppe>
Genau so ist es du definierst dein Sytem (Lib) und entscheidest was es
kann und was nicht.
Über Geschmack lässt sich nicht streiten.
Hans-Georg Lehnard schrieb:> du definierst dein Sytem (Lib) und entscheidest was es> kann und was nicht.
… wobei ich Karl Käfers 'Geschmack' teile. Ich bin - wie gesagt - gerade
am Timer und komme auch nur viel langsamer voran als Karl (viele
Baustellen, pro Tag nur mal 1-2h Zeit, …).
Eine 'mikrocontroller.net-Lib' darf man das sicher nicht nennen, wenn es
keinen Konsens gibt. Ich versuche möglichst nach der gleichen
Philosophie wie Karl weiter zu machen. Wenn ich seine GPIOs nehme,
passt's wenigstens.
Karl Käfer schrieb:> Dafür arbeite ich> derzeit an einem kleinen Codegenerator, der aus diesem XML dann> Präprozessor-Code generiert.
Cool. :-)
Hallo Hans-Georg,
Hans-Georg Lehnard schrieb:> Karl Käfer schrieb:>> Hallo Hans-Georg,>> Was ist eine Abstraktionsschicht?>> Das ist genau das, was dieser Diskussion fehlt ;)
Was ich sagen wollte, ist: für spezifische Details wie PUE-Register oder
die Frage, wie genau die Controller-Hardware einen Pin toggeln kann, hat
der liebe Gott (oder jemand anderes) die Abstraktionsschicht erfunden.
Wenn der GPIO-Pin die Methoden "toggle()" und "setPullup()" anbietet,
ist es für den Benutzer der Bibliothek gleichgültig, wie sie intern
implementiert sind.
> Wir verzetteln uns hier immer wieder in Kleinigkeiten und in die Tiefen> der Avr Bitpopelei.
Gerade diese "Avr Bitpopelei" halte ich aber für extrem wichtig, da sie
die Grundlage für etliche andere Dinge ist. Wer diese nicht sauber
hinbekommt, wird sich schwer tun, damit einen performanten, effizienten
Treiber für zum Beispiel ein Display hinzubekommen.
Liebe Grüße,
Karl
Hallo Torsten,
Torsten C. schrieb:> Über Geschmack lässt sich nicht streiten.
Doch, natürlich kann man das. Es bringt nur nichts, weil man nie zu
einem Ergebnis kommt.
> Hans-Georg Lehnard schrieb:>> du definierst dein Sytem (Lib) und entscheidest was es>> kann und was nicht.>> … wobei ich Karl Käfers 'Geschmack' teile. Ich bin - wie gesagt - gerade> am Timer und komme auch nur viel langsamer voran als Karl (viele> Baustellen, pro Tag nur mal 1-2h Zeit, …).
Ehrlich gesagt habe ich nicht die geringste Idee, wie man die Timer der
AVRs in C++ abbilden könnte.
> Karl Käfer schrieb:>> Dafür arbeite ich>> derzeit an einem kleinen Codegenerator, der aus diesem XML dann>> Präprozessor-Code generiert.>> Cool. :-)
Die ersten beiden Generatoren habe ich hier einmal angehängt, aber der
Code für die USARTS gefällt mir noch nicht so richtig. Für Vorschläge
und Ideen werden bin ich gerne offen.
Liebe Grüße,
Karl
Hans-Georg Lehnard schrieb:> Hmm ich spare bei dem Toggle Befehl mit dem Input Pin Register 2Byte im> Flash und 2 Takte während der Laufzeit dafür handle ich mir etwas nicht> portables in einer "portablen" Lib ein.
Ganz so einfach ist das nicht, denn read-modify-write ist nicht atomar.
Hat man eine ISR, die einen anderen Pin an demselben Port ändert, geht
das irgendwann schief. Es sei denn, man denkt dran, das ganze mit
ATOMICBLOCK zu umschließen.
Zum eigentlichen Thema:
Ich programmiere seit einiger Zeit meine XMegas objektorientiert. Das
hat mehrere Gründe:
Erst einmal ist das für mich persönlich die Art, wie ich am besten
Programmieraufgaben löse. Das ist rein subjektiv und gilt natürlich
nicht allgemein.
Bei mehrfach vorhandener, identischer Hardware (z.B. bis zu 8 USARTs
beim XMega) stösst die herkömmliche Programmierung mit einer festen
Kodierung der Hardware definitiv an ihre Grenzen. Eine dynamische
Bindung der Hardware an jeweils eine Instanz einer Klasse zur Laufzeit
ist da deutlich einfacher und flexibler.
Ich programmiere auch häufiger Benutzerschnittstellen auf grafischen
LCDs. Mit Hilfe der Polymorphie und Objekten, die sich selbst zeichnen,
kann man da meiner Meinung nach viel übersichtlicher und flexibler seine
Schnittstelle aufbauen.
Insgesamt ist meine Erfahrung, dass einfache, aber dafür zeitkritische
Steueraufgaben meist besser klassisch funktional/prozedural gelöst
werden sollten. Bei zeitunkritischen Steueraufgaben mit
Benutzerschnittstelle hat dafür der objektorientierte Ansatz Vorteile.
Detlev T. schrieb:> Hans-Georg Lehnard schrieb:>> Hmm ich spare bei dem Toggle Befehl mit dem Input Pin Register 2Byte im>> Flash und 2 Takte während der Laufzeit dafür handle ich mir etwas nicht>> portables in einer "portablen" Lib ein.>> Ganz so einfach ist das nicht, denn read-modify-write ist nicht atomar.> Hat man eine ISR, die einen anderen Pin an demselben Port ändert, geht> das irgendwann schief. Es sei denn, man denkt dran, das ganze mit> ATOMICBLOCK zu umschließen.
Das ist natürlich richtig. Aber wie oft kommt es vor, daß ich auf ein
und denselben Pin in der Main-Loop und einer ISR zugreife?
> Zum eigentlichen Thema:>> Ich programmiere seit einiger Zeit meine XMegas objektorientiert. Das> hat mehrere Gründe:>> Erst einmal ist das für mich persönlich die Art, wie ich am besten> Programmieraufgaben löse. Das ist rein subjektiv und gilt natürlich> nicht allgemein.
Doch, eigentlich schon. Wer einmal den Komfort von objektorientierter
Programmierung kennengelernt hat, will ihn nicht mehr missen... ;-)
> Bei mehrfach vorhandener, identischer Hardware (z.B. bis zu 8 USARTs> beim XMega) stösst die herkömmliche Programmierung mit einer festen> Kodierung der Hardware definitiv an ihre Grenzen. Eine dynamische> Bindung der Hardware an jeweils eine Instanz einer Klasse zur Laufzeit> ist da deutlich einfacher und flexibler.>> Ich programmiere auch häufiger Benutzerschnittstellen auf grafischen> LCDs. Mit Hilfe der Polymorphie und Objekten, die sich selbst zeichnen,> kann man da meiner Meinung nach viel übersichtlicher und flexibler seine> Schnittstelle aufbauen.>> Insgesamt ist meine Erfahrung, dass einfache, aber dafür zeitkritische> Steueraufgaben meist besser klassisch funktional/prozedural gelöst> werden sollten. Bei zeitunkritischen Steueraufgaben mit> Benutzerschnittstelle hat dafür der objektorientierte Ansatz Vorteile.
Nunja... man kann auch in C sehr viele OO-Konzepte umsetzen, aber das
ist dann eben nicht so übersichtlich. Elecia White hat da in "Making
Embedded Systems" viele gute Anregungen und Tips. Auf Mikrocontrollern
kann man es ja machen. ;-)
Andererseits bin ich jedoch überzeugt, daß man mit einer guten
C++-Library ein Werkzeug haben und viele komfortable Konzepte nutzen
kann, ohne dabei an Effizienz gegenüber C einzubüßen. Natürlich kann man
kritische Dinge in Assembler machen -- aber da sind C und C++ doch kein
Hindernis?!
Liebe Grüße,
Karl
Karl Käfer schrieb:> Das ist natürlich richtig. Aber wie oft kommt es vor, daß ich auf ein> und denselben Pin in der Main-Loop und einer ISR zugreife?
Nee, nee, nicht den selben Pin. Es reich(t|en) schon ein/ mehrere
andere(r) Pin(s) am selben Port!
Detlev T. schrieb:> Bei mehrfach vorhandener, identischer Hardware (z.B. bis zu 8 USARTs> beim XMega) stösst die herkömmliche Programmierung mit einer festen> Kodierung der Hardware definitiv an ihre Grenzen.
Warum?
Detlev T. schrieb:> Bei mehrfach vorhandener, identischer Hardware (z.B. bis zu 8 USARTs> beim XMega) stösst die herkömmliche Programmierung mit einer festen> Kodierung der Hardware definitiv an ihre Grenzen. Eine dynamische> Bindung der Hardware an jeweils eine Instanz einer Klasse zur Laufzeit> ist da deutlich einfacher und flexibler.
Richtig ist sicherlich, dass die Bindung der Hardware an jeweils eine
Instanz einer Klasse besser lesbar ist:
1
serial1.print(constchar*str);
Aber bevor nun wieder 'mit ANSI C geht das auch' kommt:
Die herkömmliche Programmierung ist weniger 'schön' und Fehler des
Programmierers rutschen schneller mal durch.
chris_ schrieb:> Der Vorteil: bekannte Funktionsnamen, viele Codebeispiele
Man sollte sich m.E. daran orientieren, wenn man was effizientes neu
aufbauen möchte. Immer wird das sicher nicht gehen.
Torsten C. schrieb:> Richtig ist sicherlich, dass die Bindung der Hardware an jeweils eine> Instanz einer Klasse besser lesbar ist
Mit
ldi ZL,low(Var) ;Pointer auf Variable im SRAM
ldi ZH,high(Var)
rcall serial1out
ist auch alles ganz verständlich gesagt.
Ganz ohne zusätzlichen Bedarf an Klassen und deren Instanzen.
> Die herkömmliche Programmierung ist weniger 'schön' und Fehler des> Programmierers rutschen schneller mal durch
Asm formuliert klar, einfach, eindeutig.
Die schweren Fehler sind sprachunabhängig sowieso eher strategischer
bzw. systemarchitektonischer Natur oder durch Nichttesten aller
möglichen realen Inputs bedingt.
Weniger schön finde ich wiederum
> serial1.print(const char * str);
und man weiß ja, das Instruktionen und Ausdrücke in C noch ganz andere,
viel extremere Formen annehmen können.
Torsten C. schrieb:> Die herkömmliche Programmierung ist weniger 'schön' und Fehler des> Programmierers rutschen schneller mal durch.
Um das zu verfeinern:
Herkömmliche Programmierung (gilt für ANSI-C genau so wie für Assembler)
erlaubt es nicht, dass unerlaubte Parameter-Typen als Parameter
übergeben werden. Enums sind z.B. wie Integer.
Es geht nicht nur um 'Fehler des Programmierers'^^, sondern auch um
Bequemlichkeit: Die IDE schlägt nur Parameter, Methoden usw. vor, die
auch compilierbar sind, also z.B. möglichst keine 'private' Member
außerhalb der Klasse. Bei C# ist das z.B. so.
Um nochmal den 'Guru' zu zitieren:
"fundamental design goal: design violations should not compile".
Die Steigerung wäre:
"the IDE should warn at design violations".
Das gehört eher zum Thema:
"C++ auf einem MC, welche Vorteile hätte das?"
Ich hoffe, es passt trotzdem!
chris_ schrieb:> Das Serial.print ist polymorph
Du meinst wahrscheinlich das richtige.
Es geht Dir hier vermutlich nicht um Polymorphie mit der Bestimmung der
Klasse zur Laufzeit. Das würde VTables und Performance-Nachteile
bringen.
Funktionen und Methoden mit gleichen Bezeichnern im gleichen Namensraum
und unterschiedlichen Signaturen sind aber auch was, was die Lesbarkeit
des Codes m.E. deutlich verbessert. Das sehe ich auch so, Chris.
Der Vorteile von Namensräumen wurde ja auch schon genannt, wobei einige
IDEs diesen Aspekt m.E. noch nicht perfekt im Griff haben.
>Es geht Dir hier vermutlich nicht um Polymorphie mit der Bestimmung der>Klasse zur Laufzeit. Das würde VTables und Performance-Nachteile>bringen.
Leider weiß ich nicht, ob Polymorphismus in C++ immer VTables nach sich
zieht.
Poylmorphie wird im englischen Wikpedia-Artikel an einem Python Beispiel
gezeigt, indem es so etwas wie VTables vermutlich nicht gibt. Ich denke
das ist eine Implementierungsdetail von C++ was aber genauso gut zur vom
Compiler zur Compile-Time ohne VTables zu lösen wäre.
http://en.wikipedia.org/wiki/Polymorphism_%28computer_science%29
Moby schrieb:> Asm formuliert klar, einfach, eindeutig.
Bitte eröffne deinen eigenen Assembler-Evangelismus-Thread.
Deine diesbezüglichen Auslassungen sind in diesem Thread unerwünscht.
> Serial.print(78) gives "78"> Serial.print(1.23456) gives "1.23"> Serial.print('N') gives "N"> Serial.print("Hello world.") gives "Hello world.">Es geht Dir hier vermutlich nicht um Polymorphie mit der Bestimmung der>Klasse zur Laufzeit.
Nachdem ich mir den Wikipedia Artikel durchgelesen habe
http://de.wikipedia.org/wiki/Tabelle_virtueller_Methoden
bin ich der Meinung, dass für den obigen Code keine VTables gebraucht
werden, da alle Argumente zur Laufzeit bekannt sind. Der Compiler kann
z.B für
Serial.print(1.23456) gives "1.23"
gleich die passende Funktion
Serial.print(float f)
kompilieren.
Ich nehme mal an und hoffe, dass es der gcc auch so macht.
Hallo chris_,
chris_ schrieb:> bin ich der Meinung, dass für den obigen Code keine VTables gebraucht> werden, da alle Argumente zur Laufzeit bekannt sind. Der Compiler kann> z.B für>> Serial.print(1.23456) gives "1.23">> gleich die passende Funktion>> Serial.print(float f)>> kompilieren.> Ich nehme mal an und hoffe, dass es der gcc auch so macht.
Das ist korrekt. "Serial.print(float f)" hat eine andere
Funktionssignatur als "Serial.print(char c)", und darum weiß der
Compiler auch ohne VTables, was er aufrufen muß. Der Compiler ist
übrigens die C++-Version des gcc.
Liebe Grüße,
Karl
>Das ist korrekt.
Hallo Karl,
das freut mich zu hören. Ich habe mich bis gerade eben noch nie so
richtig mit dem Thema VTables befasst. Der Begriff wird aber immer gerne
mal wieder fallen gelassen wie eine Art "Mysterium". Das richtige
Verständnis dafür scheint für viele schwierig.
Das scheint mir auch für den Wikipedia-Artikel zu gelten. Da steht:
"Die Tabelle virtueller Methoden (engl.: virtual method table oder
virtual function table, kurz VMT, VFT, vtbl oder vtable) ist ein Ansatz
von Compilern objektorientierter Programmiersprachen um dynamisches
Binden umzusetzen. Das ist unter anderem Grundvoraussetzung für
Vererbung und Polymorphie."
Eigentlich müsste es präzisser heisen
Das ist unter anderem Grundvoraussetzung für Vererbung wenn die Objekte
zur Compile-Zeit noch nicht bekannt sind.
Karl Käfer schrieb:> "Serial.print(float f)" hat eine andere Funktionssignatur> als "Serial.print(char c)"
Genau. Das ist alles Richtig, danke. Ich wollte nur sagen, dass
unterschiedliche Funktionssignaturen auch ohne Klassen, Polymorphie und
VTables in C++ gehen, aber nicht in K&R-C, ASM, …
Dann sind wir uns mal wieder einig. :-)
Sorry, falls ich für Verwirrung gesorgt haben sollte.
chris_ schrieb:> bin ich der Meinung, dass für den obigen Code keine VTables gebraucht> werden, da alle Argumente zur Laufzeit bekannt sind.chris_ schrieb:> Leider weiß ich nicht, ob Polymorphismus in C++ immer VTables nach sich> zieht.
Ich habe mir in C ein Interface / Vtable selber geschrieben.
Da sieht man gut, wie der Compiler optimier kann und wann du Overhead
hast.
Was passiert:
a) Compiler inlined processDataId100
mehr Code, dafür ist der Funktionszeiger auf doStart bekannt und die
Vtable wird wegoptimiert. Der Code von doStart, etc. kann geinlined
werden.
b) Compiler lässt processDataId100 stehen
die Funktion kennt die Funktionszeiger nicht. Der Zeiter wird aus der
Vtable geladen. Statt einer Konstanten wird der Inhalt der Vtable als
Sprungadresse gelesen. Der Code von doStart, etc. ist unbekannt und kann
nicht optimiert werden.
Wie man sieht liegt der Nachteil von Polymorphismus zur Laufzeit in der
mangelnden Optimierungsmöglichkeit, wenn er nicht aufgelöst wird.
Trivialen Funktionsaufrufe wie doEnd(), können nicht
wegoptimiert/geinlined werden. Im Vergleich hat die Zeigerarithmetik
meist weniger Aufwand. Dafür entsteht weniger Code, falls doEnd(), etc
sowieso nicht geinlined werden (große Funktionen).
in C++ müsste das irgendwie so aussehen:
1
struct Base{
2
virtual void doStart(int ID)=0;
3
virtual void doData(const void * Data, int length)=0;
4
virtual void doEnd()=0;
5
}
6
7
struct sendData_t: public Base {/*Implementierung*/};
8
sendData_t sendData;
9
struct receiveData_t: public Base{/*Implementierung*/};
PS: Dieselbe Funktion nehmen zu können, um Datenpakete zu
zusammenzustellen und wieder aufzudröseln, habe ich von BOOST (ich
glaube serialize) geklaut.
PSS: Man sollte im C++ Beispiel auch nicht mehr C-Arrays nehmen, sondern
std::array
http://de.cppreference.com/w/cpp/container/array
Grüße Felix
FelixW schrieb:> Man sollte im C++ Beispiel auch nicht mehr C-Arrays nehmen, sondern> std::array
Guter Hinweis, danke. :-)
FelixW schrieb:> Wie man sieht liegt der Nachteil von Polymorphismus zur Laufzeit in der> mangelnden Optimierungsmöglichkeit, wenn er nicht aufgelöst wird.
Ich habe keine zig verschiedenen Toolketten im Betrieb, um die
Assembler-Dateien zu analysieren. Macht es Sinn, in einer C++-Lib
Polymorphismus zu verwenden, der sich zur Compiler-Zeit auflösen lässt,
oder ist das zu 'unsicher'?
Beim Begriff "Polymorphismus" scheint es auch ein unterschiedliches
Verständnis zu geben.
Bei den oben genannten Funktionenen
> Serial.print(78) gives "78"> Serial.print(1.23456) gives "1.23"> Serial.print('N') gives "N"> Serial.print("Hello world.") gives "Hello world."
geht es um den in der Wikipedia als "Ad hoc polymorphism" bezeichneten
Typ:
http://en.wikipedia.org/wiki/Polymorphism_%28computer_science%29
Felix, wenn ich Dein Beispiel richtig verstehe, geht es Dir um den als
"Subtyping" bezeichneten Typ.
Torsten C. schrieb:> FelixW schrieb:>> Man sollte im C++ Beispiel auch nicht mehr C-Arrays nehmen, sondern>> std::array> Guter Hinweis, danke. :-)
Das ist nur ein wrapper für c arrays, Der die Initialisierung, etc.
unnötig erschwert.
> Polymorphismus zu verwenden, der sich zur Compiler-Zeit auflösen lässt,> oder ist das zu 'unsicher'?
Einfach die Basisklasse ausschlieslich verwenden, um davon klassen
abzuleiten, und kein virtual verwenden. Dann kann nichts passieren.
Daniel A. schrieb:> Einfach die Basisklasse ausschlieslich verwenden, um davon klassen> abzuleiten, und kein virtual verwenden. Dann kann nichts passieren.
Häää? Und dann werden die Member der abgeleiteten Klassen verwendet?
Falls das geht, würde ich sehr gern lernen, wie das geht.
Torsten C. schrieb:> Daniel A. schrieb:>> Einfach die Basisklasse ausschlieslich verwenden, um davon klassen>> abzuleiten, und kein virtual verwenden. Dann kann nichts passieren.>> Häää? Und dann werden die Member der abgeleiteten Klassen verwendet?>> Falls das geht, würde ich sehr gern lernen, wie das geht.
Du gibst dem compiler einfach keinen grund für ne vtable
1
//ungetestet
2
classbase{
3
public:
4
inlineinttest(){
5
return0;
6
}
7
};
8
classderived:publicbase{
9
inlineinttest(){
10
return1;
11
}
12
};
13
14
voidprintDerived(derived*x){
15
std::cout<<x.test()<<std::endl;// muss derived::test sein, weil nicht virtual
16
}
17
18
// dashier vermeiden:
19
// die Basisklasse ausschlieslich verwenden, um davon klassen abzuleiten
20
voidprintBase(base*x){
21
std::cout<<x.test()<<std::endl;// muss base::test sein, weil nicht virtual
22
}
23
24
intmain(){
25
derivedx;
26
printDerived(x);// gibt 1 aus
27
printBase(x);// gibt 0 aus, weil nicht virtual
28
}
Die Polymorphie ist so leicht eingeschränkt, aber zur compiletime stehen
alle Funktionsaufrufe fest, der compiler kann nichts in eine vtable
packen. Die programmierung wird so aber anspruchsfoller.
chris_ schrieb:> Felix, wenn ich Dein Beispiel richtig verstehe, geht es Dir um den als> "Subtyping" bezeichneten Typ.
ja
Daniel A. schrieb:> Das ist nur ein wrapper für c arrays, Der die Initialisierung, etc.> unnötig erschwert.
warum? Was geht nicht/nur umständlicher (bei welcher C++ Version).
Daniel A. schrieb:>> Polymorphismus zu verwenden, der sich zur Compiler-Zeit auflösen lässt,>> oder ist das zu 'unsicher'?> Einfach die Basisklasse ausschlieslich verwenden, um davon klassen> abzuleiten, und kein virtual verwenden. Dann kann nichts passieren.
Wenn der Compiler den bekannten Typ optimieren kann macht es keinen
Unterschied. Wenn nicht, dass hast du nicht funktionierenden code.
Ich rate davon ab!
Irgendwo weiter vorne gab es ein PDF. Es gibt (subtyping) Polymorphismus
zu
a) Kompilierzeit (mit templates)
b) Linkerzeit (mit templates und Instanzen)
c) Laufzeit (virtual)
chris_ schrieb:> Beim Begriff "Polymorphismus" scheint es auch ein unterschiedliches> Verständnis zu geben.>> Bei den oben genannten Funktionenen>>> Serial.print(78) gives "78">> Serial.print(1.23456) gives "1.23">> Serial.print('N') gives "N">> Serial.print("Hello world.") gives "Hello world.">> geht es um den in der Wikipedia als "Ad hoc polymorphism" bezeichneten> Typ:>> http://en.wikipedia.org/wiki/Polymorphism_%28computer_science%29
Danke für die Klarstellung
Vielleicht hilft dem besseren Verständnis:
Ad-hoc Polymorphismus heißt nichts anderes, als das der Kompiler die
Typen in den Funktionsnamen aufnimmt und damit keinen extra code
generiert. (genauso wie namespace) Die genaue Namensgebung ist vom
compiler abhängig. Ist Serial.print() nicht als "static" deklariert
sieht das dann so aus:
_Serial_print_int( &Serial, 78)
_Serial_print_float( &Serial, 1.23456)
_Serial_print_char( &Serial, 'N')
_Serial_print_pointer_to_char( &Serial, "Hello world.")
Damit kann man dann auch C++ Memberfunktionen von C-Code aus aufrufen
(allerdings dann Kompilerabhängig).
Hallo FelixW,
FelixW schrieb:> Ad-hoc Polymorphismus heißt nichts anderes, als das der Kompiler die> Typen in den Funktionsnamen aufnimmt und damit keinen extra code> generiert.
Der Compiler übernimmt nur die Argument-Typen in die Signatur, nicht die
Rückgabe-Typen. Daher ist:
FelixW schrieb:> Wenn der Compiler den bekannten Typ optimieren kann macht es keinen> Unterschied.
Genau da liegt dass Problem: Wenn mann bei meinem Beispiel derived::test
virtual machen würde, kann der Compiler zur Compiletime nicht wissen, ob
in einer anderen Compilationsunit eine classe von derived abgeleitet
wird, die die methode test überschreibt. Dass ist erst zur linkerzeit
bekannt, wenn keine pointer auf die Classen bei dynamisch nachgeladenen
Library Funktionen als Parameter oder Rückgabewerte existieren, wobei
dass der Compiler nicht wissen kann. Derartige Compileroptimierungen
halte ich für unwarscheinlich.
> Wenn nicht, dass hast du nicht funktionierenden code.
Diese aussage ist falsch. Es verhält sich nicht so wie bei gewönlicher
polymorphie, aber es funktioniert, so wie es im standard definiert
wurde.
> Ich rate davon ab!
Wenn alle beteiligten die Funktionsweise von Code ohne virtual
verstehen, und wenn sie wissen, was sie tuhen, sehe ich keine Probleme.
Ich habe den Thread interessiert mitgelesen und im Netz eine Library von
KonstantinChizhov gefunden, die für meine Kenntnisse noch einigermassen
nachvollziehbar ist.
Alexandra
Mcucpp - is a C++ hardware abstraction library for microcontrollers.
************************************************************************
The aim of the project is to make a high level but extremly efficient
C++ hardware abstraction framework for different microcontroller
families.
For latest version visit https://github.com/KonstantinChizhov/Mcucpp
************************************************************************