Forum: Mikrocontroller und Digitale Elektronik C++ auf einem MC, wie geht das?


von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

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
volatile uint8_t*const ddr;
Sind auch referenzen möglich:
1
volatile uint8_t& ddr;

PS: In dem Bespiel, welches ich ganz weit oben Postete, habe ich dafür 
Referenzen verwendet.

: Bearbeitet durch User
von kopfkratzer (Gast)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

Hallo Torsten,

Torsten C. schrieb:
>
1
volatile uint8_t* ddr;
2
> volatile uint8_t* port;
3
> volatile uint8_t* pin;
>
> 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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ?

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

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!

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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? ;-)

: Bearbeitet durch User
von Moby (Gast)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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 !.

von Bastler (Gast)


Lesenswert?

Noch hab ich keine ASM-Lösung schimmern sehen. Soviel zur Kapitulation!

von Klaus W. (mfgkw)


Lesenswert?

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!

von Bastler (Gast)


Lesenswert?

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++?

von Karl Käfer (Gast)


Lesenswert?

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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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
    volatile uint8_t * const ddr;
2
    volatile uint8_t * const port;
3
    volatile uint8_t * const pin;

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.

: Bearbeitet durch User
von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

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 ...

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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! :-)

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

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 ;)

von Hans-Georg L. (h-g-l)


Lesenswert?

Ich habe hier noch ein Entwicklungsbord mit AT91sam9260

und der hat 22 *32Bit Register für ein PORT ! ...

Soweit bin ich aber noch nicht .. ;)

von Hans-Georg L. (h-g-l)


Lesenswert?

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 !

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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
    volatile uint8_t * const ddr;
2
>     volatile uint8_t * const port;
3
>     volatile uint8_t * const pin;

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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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".

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

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 ;)

von Ralf G. (ralg)


Lesenswert?

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>

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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".

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

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 ... ,)

von Ralf G. (ralg)


Lesenswert?

Torsten C. schrieb:
> Hast Du die Beiträge ^^ gelesen?

Das hat mit 'const' nichts zu tuen. Ich hab's ausprobiert.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

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!

: Bearbeitet durch User
von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

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:
1
#include <avr/io.h>
2
#include <stdarg.h>
3
4
5
// ********************************************************************************
6
// templates (für 'Spezial'-Enums)
7
//
8
template<class T>
9
inline T operator|(const T a, const T b)
10
{
11
  return static_cast<T>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
12
};
13
// --------------------------------------------------------------------------------
14
template<class T>
15
inline T operator&(const T a, const T b)
16
{
17
  return static_cast<T>(static_cast<uint16_t>(a) & static_cast<uint16_t>(b));
18
};
19
// ********************************************************************************
20
extern "C"
21
{
22
  void __cxa_pure_virtual()
23
  {
24
      // erst mal nichts weiter
25
  }
26
}
27
// ********************************************************************************
28
enum e_pinInit
29
{
30
  pin0   = 0x01,
31
  pin1  = 0x02,
32
  pin2  = 0x04,
33
  pin3  = 0x08,
34
  pin4  = 0x10,
35
  pin5  = 0x20,
36
  pin6  = 0x40,
37
  pin7  = 0x80,
38
  pin03  = 0x0F,
39
  pin47  = 0xF0,
40
  pin07  = 0xFF,
41
  pinPullUp  = 0x100
42
};
43
// --------------------------------------------------------------------------------
44
// AVR Port
45
template< uint16_t sfr_pin, uint16_t sfr_ddr, uint16_t sfr_port>
46
struct s_AvrPort
47
{
48
  static const uint16_t pin  = sfr_pin;
49
  static const uint16_t dir  = sfr_ddr;
50
  static const uint16_t port = sfr_port;  
51
};
52
// --------------------------------------------------------------------------------
53
#define PORT(x)  typedef s_AvrPort<(uint16_t)&PIN##x,(uint16_t)&DDR##x,(uint16_t)&PORT##x> port##x;
54
55
#ifdef PORTA
56
PORT(A)
57
#endif
58
#ifdef PORTB
59
PORT(B)
60
#endif
61
#ifdef PORTC
62
PORT(C)
63
#endif
64
#ifdef PORTD
65
PORT(D)
66
#endif
67
#ifdef PORTE
68
PORT(E)
69
#endif
70
#ifdef PORTF
71
PORT(F)
72
#endif
73
#ifdef PORTG
74
PORT(G)
75
#endif
76
// --------------------------------------------------------------------------------
77
// Zugriff auf SFR Register
78
class SfrRegAccess
79
{
80
public:
81
  static uint8_t Get(const uint16_t reg, uint8_t bits)
82
  {
83
      return _SFR_MEM8(reg) & bits;
84
  };
85
  static void  Set(const uint16_t reg, uint8_t bits, uint8_t mask)
86
  {
87
    if (mask > 1)
88
    {
89
      _SFR_MEM8(reg) = (_SFR_MEM8(reg) & ~mask) | (bits & mask);
90
      return;
91
    }
92
    else if ( mask)
93
      _SFR_MEM8(reg) |=  bits;
94
    else
95
      _SFR_MEM8(reg) &= ~bits;
96
  };
97
  static void Toggle(const uint16_t reg, uint8_t bits)
98
  {
99
      _SFR_MEM8(reg) ^=  bits;
100
  };
101
};
102
// --------------------------------------------------------------------------------
103
class c_OutputPinsBase
104
{
105
private:
106
public:
107
  virtual void On(e_pinInit mask) = 0;
108
  virtual void On() = 0;
109
  virtual void Off(e_pinInit mask) = 0;
110
  virtual void Off() = 0;
111
  virtual void Toggle(e_pinInit mask) = 0;
112
  virtual void Toggle() = 0;
113
  virtual uint8_t GetPins() = 0;
114
  virtual void Out(uint8_t value) = 0;
115
};
116
// --------------------------------------------------------------------------------
117
template <typename sfr_regs>
118
class c_OutputPins : c_OutputPinsBase
119
{
120
private:
121
  const uint8_t _pins;
122
public:
123
  c_OutputPins(e_pinInit pins) : c_OutputPinsBase(), _pins(pins)
124
  {
125
    SfrRegAccess::Set(sfr_regs::dir,  pins & pin07, 1);
126
  };
127
  void On(e_pinInit mask)       { SfrRegAccess::Set(sfr_regs::port, mask & (_pins & pin07), 1); };
128
  void On()               { SfrRegAccess::Set(sfr_regs::port, _pins & pin07, 1); };
129
  void Off(e_pinInit mask)    { SfrRegAccess::Set(sfr_regs::port, mask & (_pins & pin07), 0); };
130
  void Off()            { SfrRegAccess::Set(sfr_regs::port, _pins & pin07, 0); };
131
  void Toggle(e_pinInit mask)   { SfrRegAccess::Toggle(sfr_regs::port, mask & (_pins & pin07)); };
132
  void Toggle()           { SfrRegAccess::Toggle(sfr_regs::port, _pins & pin07); };
133
  uint8_t GetPins()        { return _pins; };
134
  void Out(uint8_t value)      { SfrRegAccess::Set(sfr_regs::port, value, _pins & pin07); };
135
};
136
// --------------------------------------------------------------------------------
137
class c_InputPinsBase
138
{
139
private:
140
public:
141
  virtual uint8_t In(uint8_t mask) = 0;
142
  virtual uint8_t In() = 0;
143
};
144
// --------------------------------------------------------------------------------
145
template <typename sfr_regs>
146
class c_InputPins : c_InputPinsBase
147
{
148
private:
149
  const uint8_t _pins;
150
public:
151
  c_InputPins(e_pinInit pins) : c_InputPinsBase(), _pins(pins)
152
  {
153
    if (pins & pinPullUp)
154
      SfrRegAccess::Set(sfr_regs::port, pins & pin07, 1);
155
  };
156
  uint8_t In(uint8_t mask)  { return SfrRegAccess::Get(sfr_regs::pin, mask & (_pins & pin07)); };
157
  uint8_t In()        { return SfrRegAccess::Get(sfr_regs::pin, _pins & pin07); };
158
};
159
// --------------------------------------------------------------------------------
160
class c_LCD
161
// nur zum Test, für ein paar mehr oder weniger sinnvolle Ausgaben
162
{
163
private:
164
  c_OutputPinsBase *_data;
165
  c_OutputPinsBase *_E;
166
  c_OutputPinsBase *_RS;
167
  c_OutputPinsBase *_RW;
168
public:
169
  c_LCD(c_OutputPinsBase *d,c_OutputPinsBase *E,c_OutputPinsBase *RS,c_OutputPinsBase *RW) : _data(d), _E(E), _RS(RS), _RW(RW) {};
170
  void DataOut(uint8_t value)
171
  {
172
    _data->Out(value);
173
  }
174
  void Enable(uint8_t value)
175
  {
176
    if (value)
177
      _E->On();
178
    else
179
      _E->Off();
180
  }
181
};
182
// --------------------------------------------------------------------------------
183
184
185
int main(void)
186
{
187
  c_OutputPins<portA>  data(pin5|pin4|pin3|pin2);
188
  c_OutputPins<portB>  e(pin1);
189
  c_OutputPins<portB>  rs(pin2);
190
  c_OutputPins<portB>  rw(pin0);
191
192
  c_LCD LCD((c_OutputPinsBase*)&data,(c_OutputPinsBase*)&e,(c_OutputPinsBase*)&rs,(c_OutputPinsBase*)&rw);
193
194
  uint8_t start = 0xA0;
195
  while(1)
196
  {
197
    LCD.DataOut(start++);
198
    if ((start & 0x07) == 0x07)
199
      LCD.Enable(1);
200
    else
201
      LCD.Enable(0);
202
  }
203
}
Vor allen Dingen: Das ist nicht das, was ich mir unter effizient 
vorgestellt habe. Vielleicht hat ein Profi 'ne bessere Idee.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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".

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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!

: Bearbeitet durch User
von noreply@noreply.com (Gast)


Lesenswert?

@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.

von Hans-Georg L. (h-g-l)


Lesenswert?

Die xml Files findest du unter windows

 .... \Atmel\Atmel Studio 6.2\devices

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

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?
1
// Timer
2
TC0->TC_CHANNEL[0].HierRegisterName // Timer 0
3
TC0->TC_CHANNEL[1].HierRegisterName // Timer 1
4
TC0->TC_CHANNEL[2].HierRegisterName // Timer 2
5
TC1->TC_CHANNEL[0].HierRegisterName // Timer 3
6
TC1->TC_CHANNEL[1].HierRegisterName // Timer 4
7
TC1->TC_CHANNEL[2].HierRegisterName // Timer 5
8
TC2->TC_CHANNEL[0].HierRegisterName // Timer 6
9
TC2->TC_CHANNEL[1].HierRegisterName // Timer 7
10
TC2->TC_CHANNEL[2].HierRegisterName // Timer 8

von Daniel A. (daniel-a)


Lesenswert?

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???

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Daniel A. (daniel-a)


Lesenswert?

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

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Hans-Georg L. (h-g-l)


Lesenswert?

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" ;)

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

Torsten C. schrieb:
> 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.

Ach Torsten,

diese while Schleife ist doch dein "Hauptpramm" in einer embedded 
Anwendung.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

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 ?

von Scelumbro (Gast)


Lesenswert?

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.

von Reiner D. (dollreiner)


Lesenswert?

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.

von Karl Käfer (Gast)


Angehängte Dateien:

Lesenswert?

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
typedef  volatile uint8_t* const  _Register;
16
17
18
class Pin { /** base class for a pin */
19
protected:
20
    _Register ddr;
21
    _Register port;
22
    _Register pin;
23
    uint8_t  num;
24
25
public:
26
    /** constructor 
27
     * @param  ddr   DDRn register address of pin (eg. &DDRB)
28
     * @param  port  PORTn register ''            (eg. &PORTB)
29
     * @param  pin   PINn register                (eg. &PINB)
30
     * @param  num   pin number                   (eg. PB2)
31
     */
32
    Pin(_Register ddr, _Register port, _Register pin, uint8_t num):
33
        ddr(ddr), port(port), pin(pin), num(num)
34
    {}
35
};
36
37
38
class InputPin : public Pin { /** an input pin */
39
public:
40
    /** constructor
41
     * @params  see Pin::Pin()
42
     */
43
    InputPin(_Register ddr, _Register port, 
44
             _Register pin, uint8_t num, bool pullup=false):
45
        Pin(ddr, port, pin, num) {
46
        *this->ddr &= ~(1 << this->num);
47
        if(pullup) { *this->port |= (1 << this->num); }
48
    }
49
50
    /** return true if pin is high, else false */
51
    bool isHigh(void) { return (*this->pin & (1 << this->num)); };
52
53
    /** return true if pin is low, else false */
54
    bool isLow(void) { return !((*this->pin & (1 << this->num))); };
55
56
    /** return true if pullup is set, else false */
57
    bool getPullup(void) { return (*this->port & (1 << this->num)); }
58
59
    /** set pullup resistor 
60
     * @params  pullup  activate pullup if true
61
     */
62
    bool setPullup(bool pullup) { 
63
        if(pullup) { *this->port |= (1 << this->num);} 
64
        else { *this->port &= ~(1 << this->num); }
65
        return this->getPullup();
66
    }
67
};
68
69
70
class OutputPin : public Pin { /** an output pin */
71
public:
72
    OutputPin(_Register ddr, _Register port, 
73
              _Register pin, uint8_t num):
74
        /** constructor @params  see Pin::Pin() */
75
        Pin(ddr, port, pin, num) {
76
        *this->ddr |= (1 << this->num);
77
    }
78
79
    /** set output high */
80
    OutputPin& setHigh(void) { /** set pin high */
81
        *this->port |= (1 << this->num); return *this; };
82
83
    /** set output low */
84
    OutputPin& setLow(void)  { /** set pin low */
85
        *this->port &= ~(1 << this->num); return *this; };
86
87
    /** toggle output */
88
    OutputPin& toggle(void)  { /** toggle pin */
89
        *this->pin |= (1 << this->num); return *this; };
90
91
    /** assignment operator 
92
     * @param  parm  if true: set pin high, else set pin low
93
     */
94
    void operator=(bool const &parm) {
95
        if(parm) {
96
            this->setHigh();
97
        } else {
98
            this->setLow();
99
        }
100
    }
101
};
102
103
104
/**
105
 * deriving this from InputPin and OutputPin by virtually
106
 * inheriting from Pin bloats code so we better duplicate
107
 */
108
class InoutPin: public Pin {
109
public:
110
    /** constructor
111
     * @params  see Pin::Pin()
112
     */
113
    InoutPin(_Register ddr, _Register port, _Register pin, uint8_t num):
114
        Pin(ddr, port, pin, num)
115
    {}
116
117
    /** set pin as input pin */
118
    InoutPin& setInput(void) { *this->ddr &= ~(1 << this->num); return *this; }
119
120
    /** set pin as output pin */
121
    InoutPin& setOutput(void) { *this->ddr |= (1 << this->num); return *this; }
122
123
    // input functions (see InputPin)
124
    int isHigh(void) { return (*this->pin & (1 << this->num)); };
125
    int isLow(void) { return !((*this->pin & (1 << this->num))); };
126
127
    // output functions (see OutputPin)
128
    InoutPin setHigh(void) { *this->port |= (1 << this->num); return *this; };
129
    InoutPin setLow(void)  { *this->port &= ~(1 << this->num); return *this; };
130
    void operator=(bool const &parm) {
131
        if(parm) {
132
            this->setHigh();
133
        } else {
134
            this->setLow();
135
        }
136
    }
137
};
138
139
#endif // _PIN_HPP

Liebe Grüße,
Karl

von Karl Käfer (Gast)


Lesenswert?

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

von chris_ (Gast)


Lesenswert?

>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.

von 900ss (900ss)


Angehängte Dateien:

Lesenswert?

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

: Bearbeitet durch User
von DualZähler (Gast)


Lesenswert?

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?":
1
class LCD
2
{
3
private:
4
  Pin *data;
5
  Pin *E;
6
  Pin *RS;
7
  Pin *RW;
8
public:
9
  LCD(Pin *d,Pin *E,Pin *RS,Pin *RW) : data(d), E(E), RS(RS), RW(RW) {};
10
  void DataOut(uint8_t value)
11
  {
12
      data.???
13
  }
14
  void Enable(uint8_t value)
15
  {
16
    if (value)
17
      E.???
18
    else
19
      E.???
20
  }
21
};
22
23
OutputPin data ( PINDEF(B, 0) );
24
OutputPin e ( PINDEF(B, 1) );
25
OutputPin rs ( PINDEF(B, 2) );
26
OutputPin rw ( PINDEF(B, 3) );
27
28
LCD LCD((Pin*)&data,(Pin*)&e,(Pin*)&rs,(Pin*)&rw);

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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
class MeinPinMitMethoden {
2
    InputPin pin;
3
    OutputPin led;
4
    Timer timer;
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
class MeinPinMitMethoden {
2
    InputPin * const pin;   // festverdrahtet
3
    OutputPin * const led;  // festverdrahtet
4
    Timer * const timer;    // festverdrahtet
5
    // Weitere Member (Methoden, Zustandsautomaten, ...)
6
};
dynamisch:
1
class MeinPinMitMethoden {
2
    InputPin * pin;   // dynamisch
3
    OutputPin * led;  // dynamisch
4
    Timer * timer;    // dynamisch
5
    // Weitere Member (Methoden, Zustandsautomaten, ...)
6
};

: Bearbeitet durch User
von Scelumbro (Gast)


Lesenswert?

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:
1
#include <avr/io.h>
2
3
#define GPIOB_BASE  0x23
4
#define GPIOC_BASE  0x26
5
#define GPIOD_BASE  0x2B
6
#define GPIOE_BASE  0x2C
7
#define  GPIOF_BASE  0x2F
8
9
typedef struct
10
{
11
  uint8_t pin;
12
  uint8_t ddr;
13
  uint8_t port;
14
  
15
} GPIO_t;
16
17
18
19
template <uint8_t baseaddr> struct AVRgpioBank
20
{
21
   GPIO_t * reg;
22
  
23
   AVRgpioBank()
24
   {
25
     reg = reinterpret_cast<GPIO_t *>(baseaddr);
26
  
27
   }
28
};
29
30
31
struct gpioPin
32
{
33
  void setDir(const bool value) {}
34
        void getValue() const {}
35
  void setValue(const bool value) {}
36
        bool operator =(const bool &value) {}
37
        bool operator == (const bool &value) {}
38
};
39
40
//Template class for one single GPIO pin
41
template<uint8_t baseaddr, uint8_t num> struct AVRgpioPin : public gpioPin
42
{
43
  AVRgpioBank<baseaddr> port;
44
  
45
  void setDir(const bool value)
46
  {
47
    if(value)
48
    {
49
      port.reg ->ddr |= (1<<num);
50
    }
51
    else
52
    {
53
      port.reg ->ddr &= ~(1<<num);
54
    }
55
  }
56
  bool get() const
57
  {
58
    return ((port.reg ->pin) && (1<<num));
59
  }
60
  void set(const bool value)
61
  {
62
    if(value)
63
    {
64
      port.reg ->port |= (1<<num);
65
    }
66
    else
67
    {
68
      port.reg ->port &= ~(1<<num);
69
    }
70
  }
71
  
72
  bool operator =(const bool &value)
73
  {
74
    set(value);
75
    return value;
76
  }
77
  bool operator == (const bool &value)
78
  {
79
    return (get() == value);
80
    
81
  }
82
83
84
int main(void)
85
{
86
  AVRgpioPin<GPIOC_BASE, 0> led;
87
  led = true;
88
  while(1)
89
  {
90
    //TODO:: Please write your application code
91
  }
92
}
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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ;)

von Daniel A. (daniel-a)


Lesenswert?

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
namespace hardware {
4
  namespace LED {
5
    static const ucpp::Port<0>::Pin<0> status; // declariere status led
6
    static const ucpp::Port<0>::Pin<1> error; // declariere error led
7
  }
8
  inline void init(){
9
    LED::status.setOutput();
10
    LED::error.setOutput();
11
  }
12
}
13
14
// Main.cpp
15
#include CONFIG_FILE // includire config abhängig von macro 
16
17
int main(){
18
  Hardware::init();
19
  while( true ){
20
    Hardware::LED::status.toggle();
21
    delay_ms(500);
22
  }
23
}
24
25
// compilieren
26
avr-gcc -mcu=xyz -DCONFIG_FILE="Platine1Conf.hpp" ...

von Philip S. (phs)


Lesenswert?

Scelumbro schrieb:
> Überhaupt die zusammenarbeit von ISR mit C++ bereitet mir noch
> Kopfzerbrechen.

Auf einem STM32F4 Discovery Board habe ich das so gelöst:
1
(...)
2
static dma::DmaChannel                  ada_dmaChannel(adc_dmaStream, 0);
3
4
static devices::AdcViaSTM32F4_Adc1      adc(rcc, ada_dmaChannel);
5
6
(...)
7
8
#if defined(__cplusplus)
9
extern "C" {
10
#endif /* defined (__cplusplus) */
11
12
void
13
ADC_IRQHandler(void) {
14
    adc.handleIrq();
15
}
16
17
void
18
DMA2_Stream0_IRQHandler(void) {
19
    adc_dmaStream.handleIrq();
20
}
21
22
#if defined(__cplusplus)
23
} /* extern "C" */
24
#endif /* defined (__cplusplus) */

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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;
  }
}

: Bearbeitet durch User
von Achim (Gast)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Karl Käfer (Gast)


Angehängte Dateien:

Lesenswert?

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

von Ralf G. (ralg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl Käfer (Gast)


Angehängte Dateien:

Lesenswert?

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

von Ralf G. (ralg)


Lesenswert?

@Karl Käfer
1
template <typename T, typename U> bool _get(T obj, U bit) { return *obj & (1 << bit); }
kann das sein, dass hier noch das return rein muss?

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Ralf G. (ralg)


Angehängte Dateien:

Lesenswert?

@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!

von Hans-Georg L. (h-g-l)


Lesenswert?

Ralf G. schrieb:
> @Karl Käfer
>
1
> template <typename T, typename U> bool _get(T obj, U bit) { return *obj 
2
> & (1 << bit); }
3
>
> 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 ...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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:
1
TCCR0B = (0 << FOC0A) | (0 << FOC0B) | (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00);

Wie würdest du das gerne "schöner" codieren? (in C, ohne ++)

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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
static const int FOC0A = (1<<0);
2
static const int FOC0B = (1<<1);
3
static const int WGM00 = (1<<2);
4
static const int WGM01 = (1<<3);
5
static const int WGM02 = (1<<4);
6
static const int CS00 =  (1<<5);
7
static const int CS01 =  (1<<6);
8
static const int CS02 =  (1<<7);

... oder fassen mehrere Bits zusammen. Z.B. in den MSP430-Bibliotheken 
ist das so.

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

Torsten C. schrieb:
> oder fassen mehrere Bits zusammen. Z.B. in den MSP430-Bibliotheken
Ralf G. schrieb:
> Ich find's sogar schön, Bits zusammenzufassen...

Hier ein Beispiel:

https://github.com/noccy80/mspdev/blob/master/reference/MSP430ware/deprecated/CCS/msp430x552x.h

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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?

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

Hans-Georg Lehnard schrieb:
> (1<<5) ist für den Compiler signed int,  0x20u nicht.

(1U << 5) aber auch nicht.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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?

von Karl Käfer (Gast)


Lesenswert?

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

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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? ;-)

von Karl Käfer (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Hans-Georg,

Hans-Georg Lehnard schrieb:
> Ralf G. schrieb:
>> @Karl Käfer
>>
1
>> template <typename T, typename U> bool _get(T obj, U bit) { return *obj
2
>> & (1 << bit); }
3
>>
>> 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!

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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^^:
1
#define ADC12DIV_0 (0*0x20u) /* ADC12 Clock Divider Select: 0 */
2
#define ADC12DIV_1 (1*0x20u) /* ADC12 Clock Divider Select: 1 */
3
#define ADC12DIV_2 (2*0x20u) /* ADC12 Clock Divider Select: 2 */
4
#define ADC12DIV_3 (3*0x20u) /* ADC12 Clock Divider Select: 3 */
5
#define ADC12DIV_4 (4*0x20u) /* ADC12 Clock Divider Select: 4 */
6
#define ADC12DIV_5 (5*0x20u) /* ADC12 Clock Divider Select: 5 */
7
#define ADC12DIV_6 (6*0x20u) /* ADC12 Clock Divider Select: 6 */
8
#define ADC12DIV_7 (7*0x20u) /* ADC12 Clock Divider Select: 7 */

PS: Man könnte auch noch ein "ADC12DIV_MASK" definieren, für

xxx = (xxx & ~ADC12DIV_MASK) + ADC12DIV_5;

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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

von Bastler (Gast)


Lesenswert?

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.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Bastler schrieb:
> Ob er es seit 5 Jahren kann, bezweifle ich.

https://gcc.gnu.org/ml/gcc/2009-10/msg00060.html

Nachdem das 2009 in den trunk gewandert ist...

von F. F. (foldi)


Lesenswert?

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.

von F. F. (foldi)


Lesenswert?

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

von Bastler (Gast)


Lesenswert?

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!

von Ralf G. (ralg)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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'^^.

: Bearbeitet durch User
von TriHexagon (Gast)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

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?

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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!)

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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!'

: Bearbeitet durch User
von Theoretiker (Gast)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

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. ;)

von Torsten C. (torsten_c) Benutzerseite


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von F. F. (foldi)


Lesenswert?

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.

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Angehängte Dateien:

Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Karl Käfer (Gast)


Lesenswert?

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

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von noreply@noreply.com (Gast)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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. :-(

von Daniel A. (daniel-a)


Lesenswert?

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?

von Bastler (Gast)


Lesenswert?

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.

von chris_ (Gast)


Lesenswert?

>Led7.toggle();

Hmm ... das hier vielleicht:

http://playground.arduino.cc/Code/DigitalToggle

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ?

von Ralf G. (ralg)


Lesenswert?

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.)

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ...

von Ralf G. (ralg)


Lesenswert?

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. <<-

von Bastler (Gast)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Konrad S. (maybee)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Hans-Georg L. (h-g-l)


Lesenswert?

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 ...

von Hans-Georg L. (h-g-l)


Lesenswert?

1
#include <avr/io.h>
2
3
int main(void)
4
{
5
  DDRA  |= (1<<5);
6
  fa:  0d 9a         sbi  0x01, 5  ; 1
7
  PORTA ^= (1<<5);
8
  fc:  92 b1         in  r25, 0x02  ; 2
9
  fe:  80 e2         ldi  r24, 0x20  ; 32
10
 100:  89 27         eor  r24, r25
11
 102:  82 b9         out  0x02, r24  ; 2
12
  
13
  DDRB |= (1<<6);
14
 104:  26 9a         sbi  0x04, 6  ; 4
15
  PINB =  (1<<6);  
16
 106:  80 e4         ldi  r24, 0x40  ; 64
17
 108:  83 b9         out  0x03, r24  ; 3
18
 10a:  ff cf         rjmp  .-2        ; 0x10a <main+0x10>
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 ?

: Bearbeitet durch Moderator
von Konrad S. (maybee)


Lesenswert?

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.

Übrigens ... bei den ATxmega-Controllern sieht's nochmal anders aus.
http://www.atmel.com/Images/doc8077.pdf
http://www.atmel.com/Images/Atmel-8331-8-and-16-bit-AVR-Microcontroller-XMEGA-AU_Manual.pdf

von noreply@noreply.com (Gast)


Lesenswert?

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++.

von Ralf G. (ralg)


Lesenswert?

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'
> ;-)

von Ralf G. (ralg)


Lesenswert?

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 :-)

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ;)

von Karl Käfer (Gast)


Lesenswert?

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

von Moby (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Aber PINB = 1 << 6 toggelt auf neueren AVRs einen Pin.

Das konnten schon ja schon die uralten 2313er Tinys...

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

: Bearbeitet durch User
von Scelumbro (Gast)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Lesenswert?

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

von Konrad S. (maybee)


Lesenswert?

@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.

von Karl Käfer (Gast)


Lesenswert?

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

von Hans-Georg L. (h-g-l)


Lesenswert?

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 ..

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

Ü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. :-)

von Karl Käfer (Gast)


Lesenswert?

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

von Karl Käfer (Gast)


Angehängte Dateien:

Lesenswert?

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

von Detlev T. (detlevt)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

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

von Ralf G. (ralg)


Lesenswert?

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!

von chris_ (Gast)


Lesenswert?

Wieder ein Beispiel für C++ auf dem MC:

Beitrag "Nucleo STM32-F103RB nun wirklich "Arduino kompatibel""

Die Arduino Lib mit all ihren Unzulänglichkeiten hat sich ordentlich 
verbreitet.
Der Vorteil: bekannte Funktionsnamen, viele Codebeispiele

von Moby (Gast)


Lesenswert?

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?

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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(const char * str);

Aber bevor nun wieder 'mit ANSI C geht das auch' kommt:
1
serialprint(const SerialHnd * serial1, const char * str);

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.

: Bearbeitet durch User
von Moby (Gast)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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!

: Bearbeitet durch User
von chris_ (Gast)


Lesenswert?

>Weniger schön finde ich wiederum

>> serial1.print(const char * str);

Das Serial.print ist polymorph:

    Serial.print(78) gives "78"
    Serial.print(1.23456) gives "1.23"
    Serial.print('N') gives "N"
    Serial.print("Hello world.") gives "Hello world."
    Serial.print(78, BIN) gives "1001110"
    Serial.print(78, OCT) gives "116"
    Serial.print(78, DEC) gives "78"
    Serial.print(78, HEX) gives "4E"
    Serial.println(1.23456, 0) gives "1"
    Serial.println(1.23456, 2) gives "1.23"
    Serial.println(1.23456, 4) gives "1.2346"

Aus http://arduino.cc/en/serial/print

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von chris_ (Gast)


Lesenswert?

>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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von chris_ (Gast)


Lesenswert?

>    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.

von Karl Käfer (Gast)


Lesenswert?

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

von chris_ (Gast)


Lesenswert?

>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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von FelixW (Gast)


Lesenswert?

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.
1
typedef struct {
2
    void (*doStart)(int ID);
3
    void (*doData)(const void* Data, int Length);
4
    void (*doEnd)(void);
5
} Base_vtable_t;
6
7
int Param[200]; //Daten die das Gerät hat
8
9
/* Implementierung von UartSendStart */
10
/* Implementierung von UartSendData */
11
/* Implementierung von UartSendEnd */
12
/* Implementierung von UartReceiveStart */
13
/* Implementierung von UartReceiveData */
14
/* Implementierung von UartReceiveEnd */
15
16
const Base_vtable_t sendData ={ UartSendStart, UartSendData, UartSendEnd};
17
const Base_vtable_t receiveData = { UartReceiveStart, UartReceiveData, UartReceiveEnd};
18
19
void processPaket1(const Base_vtable_t  *ProcType){
20
    ProcType->doStart( 100);
21
    ProcType->doData(&(Param[100]), sizeof(Param[100]));
22
    ProcType->doData(&(Param[110]), sizeof(Param[110]));
23
    ProcType->doEnd();
24
}
25
26
void processPaket2(const Base_vtable_t  *ProcType){
27
    ProcType->doStart( 100);
28
    ProcType->doData(&(Param[2]), sizeof(Param[2]));
29
    ProcType->doEnd();
30
}
31
32
int main (void){
33
   processPaket1( &sendData ); //Datenpaket senden
34
   processPaket2( &receiveData ); //Datenpaket auslesen
35
}

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*/};
10
receiveData_t receiveData;
11
12
void processPaket1(const Base * ProcType){
13
    ProcType->doStart( 100);
14
    ProcType->doData(&(Param[100]), sizeof(Param[100]));
15
    ProcType->doData(&(Param[110]), sizeof(Param[110]));
16
    ProcType->doEnd();
17
}
18
19
int main (void){
20
   processPaket1( &sendData ); //Datenpaket senden
21
   processPaket2( &receiveData ); //Datenpaket auslesen
22
}


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

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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'?

von chris_ (Gast)


Lesenswert?

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.

von Daniel A. (daniel-a)


Lesenswert?

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.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

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
class base {
3
  public:
4
    inline int test(){
5
      return 0;
6
    }
7
};
8
class derived : public base {
9
    inline int test(){
10
      return 1;
11
    }
12
};
13
14
void printDerived(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
void printBase(base* x){
21
  std::cout << x.test() << std::endl; // muss base::test sein, weil nicht virtual
22
}
23
24
int main(){
25
  derived x;
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.

von FelixW (Gast)


Lesenswert?

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).

von Karl Käfer (Gast)


Lesenswert?

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:
1
bool print(int);
2
int  print(int);

ein Fehler.

Liebe Grüße,
Karl

von Daniel A. (daniel-a)


Lesenswert?

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.

von Karl Käfer (Gast)


Lesenswert?

Hallo Daniel,

Daniel A. schrieb:
> Dass ist erst zur linkerzeit bekannt,

"-flto" ist unser Freund. ;-)

SCNR,
Karl

von Mitleserin (Gast)


Lesenswert?

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
************************************************************************

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.