Forum: Mikrocontroller und Digitale Elektronik AVR und C++ - ein Versuch


von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

Inspiriert von dieser Diskussion: 
Beitrag "C++ auf einem MC, wie geht das?" habe ich mich dran 
gemacht und ein paar Klassen mit Beispielanwendung geschrieben. (Oups - 
Dateiname wurde beim Upload verändert)
Bin soweit mit meinem Fortschritt ganz zufrieden.

Nur aktuell klemmt es etwas an der ADC-Geschichte und ich weiß nicht, ob 
es ein Verständnisproblem, ein HW-Problem oder was es ist.

Die Testplatine ist eine selbstgemachte für einen mega16 in DIL Format 
mit Stecksockeln und Pinbrücken. Über ein OPV-Modul habe ich einen 
Kraftsensor angebunden, den ich per ADC auslesen kann. Das funktioniert 
soweit auch.
Über Steckbrücken kann ich zusätzlich noch einen Taster ins Spiel 
bringen.
Der Taster ist ein Pullup, d.h. active-high.

Wie man im Testprogramm sehen kann, habe ich 4 ADC-Eingänge aktiviert 
und lese die zyklisch aus. Ohne Steckbrücke zum Taster habe ich auf den 
4 Kanälen Werte zwischen 180 und 190. Stecke ich den Taster auf Kanal 3 
oder 4 dann geht der "Dreckeffekt" auf 0 zurück. Seltsamerweise nicht 
nur für den Kanal, wo der Taster dran hängt.
Ähnlich sieht es im aktiven Zustand aus. Der Sensor wird nur auf seinem 
Kanal angezeigt (wie erwartet). Hänge ich den Taster dran und drücke den 
Taster, so werden mehrere Kanäle mit 1024 angezeigt.

Kann das ein Übersprechen von der Platine sein, oder habe ich einen 
gravierenden Fehler in der Software?

Die Klassen sind bislang nur für den m16 ausformuliert.
Ich würde da gerne noch weiter machen, aber erst, wenn ich die Sache 
auch stabil bekomme.

von Stefan F. (Gast)


Lesenswert?

>Seltsamerweise nicht nur für den Kanal, wo der Taster dran hängt.

Das ist nicht seltsam. Alle ADC Eingänge führen über einen Multiplexer 
auf einen einzigen S+H Kondensator. Wenn der durch einen Eingang aktiv 
entladen wird, wirkt sich dein "Dreck" von den anderen Eingängen weniger 
aus.

Der ADC liefert nur dann sinnvolle Werte, wenn deine Signalquellen 
maximal 10k Ohm Ausgangswiderstand haben. Bei weniger ist Übersprechen 
zwischen den Kanälen genau so normal, wie falsche Messergebnisse.

Wenn du mit deiner Library-Sammlung so weiter machst, wirst du sehr bald 
an die Grenzen des Flash Speichers kommen. Vor allem bei den kleineren 
ATtinys. Da kannst du gleich Arduino nehmen.

Als Lernübung ist es allerdings Ok.

von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

> Der ADC liefert nur dann sinnvolle Werte, wenn deine Signalquellen
> maximal 10k Ohm Ausgangswiderstand haben. Bei weniger ist Übersprechen
> zwischen den Kanälen genau so normal, wie falsche Messergebnisse.

OK, dann leuchtet mir zumindest ein, warum die "Dreck"-Messungen auf 0 
gehen, wenn ich den Taster (T2) unbetätigt anschließe.
Wenn ich den allerdings drücke, dann habe ich den vollen Pegel auch auf 
mehreren Eingängen.
Den Zusammenhang verstehe ich nicht.

> Wenn du mit deiner Library-Sammlung so weiter machst, wirst du sehr bald
> an die Grenzen des Flash Speichers kommen. Vor allem bei den kleineren
> ATtinys. Da kannst du gleich Arduino nehmen.

Naja - für Tinys ist das auch nicht gedacht. Bei denen habe ich auch in 
Plain-C schon zu kämpfen, alles unter zu bringen. Nee, das ist eher für 
die größeren AVRs gedacht. ZUdem auch als Übung, wie weit ich mit der 
Abstraktion komme und wie weit die Geschichte für mich Sinn ergibt.
Ich gehe ähnlich vor, die Peter Dannegger, der erst überlegt, wie er den 
Code verwenden will, bevor er ihn produziert ;)

Derzeit bin ich bei knapp 30% beim m16 - also (für mich) noch im grünen 
Bereich :)

Ein paar Nachteile des Konzeptes habe ich schon identifiziert. Denen 
will ich in einem Parallelprojekt mal auf den Grund gehen. Vor dem 
Optimieren sollte es erstmal zuverlässig laufen.

von rmu (Gast)


Lesenswert?

Mit den virtuellen Methoden etc.. würd ich aufpassen, das braucht viel 
Platz und hat auch Laufzeit-Overhead. Virtuelle Methoden bieten sich an, 
wenn bestimmte Instanzen zur Laufzeit austauschbar sein müssen.

Wenn zur Compile-Zeit eh alles feststeht wirds mit templates kompakter 
und schneller. Der Compiler hat zwar mehr zu tun, aber bei heutigen 
Rechnern und durchschnittlichen µC-Projekten ist das kein Problem.

von Stefan F. (Gast)


Lesenswert?

> dann habe ich den vollen Pegel auch auf mehreren Eingängen.

Weil du damit den S+H Kondensator auflädst und an den anderen offenen 
Eingängen hängt nichts, was ihn wieder entlädt.

Schau Dir das mal an: 
https://www.gammon.com.au/images/Arduino/ADC_internals.png

von Holzer (Gast)


Lesenswert?

Das:
Reinhard M. schrieb:
> Vor dem
> Optimieren sollte es erstmal zuverlässig laufen.

stimmt mehr als das

rmu schrieb:
> Mit den virtuellen Methoden etc.. würd ich aufpassen, das braucht viel
> Platz und hat auch Laufzeit-Overhead.

also erst weitermachen und wenn du dann Platz oder Laufzeit Probleme 
bekommst, kannst du immer noch schauen.

von Thomas W. (diddl)


Lesenswert?

Eiegntlich bin ich ein großer Freund von C und habe C++ für Controller 
immer vermieden.

Aber spätestens wenn man eine LIB für ein Modul hat, und mehrere dieser 
Module an einem Controller anschliessen möchte, wünscht man sich 
Klassen.

Klar kann man jeder Funktion eine Structur mitgeben. Aber allein die 
Namenskonvention für Funktionen vereinfacht sich rapide. Oder wenn man 
Module austauscht gegen ähnliche Module, dann hat C++ deutlich die Nase 
vorn.


Deswegen meine Frage: Wie ist denn das Verhalten von C++ zu C in Bezug 
auf Programmgröße und Ausführgeschwindigkeit?

von Holzer (Gast)


Lesenswert?

Man bekommt nichts geschenkt. Jeder Vorteil kostet auch was. Welchen 
Controller willst du denn einsetzten?

von Reinhard M. (reinhardm)


Lesenswert?

>> dann habe ich den vollen Pegel auch auf mehreren Eingängen.

> Weil du damit den S+H Kondensator auflädst und an den anderen offenen
> Eingängen hängt nichts, was ihn wieder entlädt.

> Schau Dir das mal an:
> https://www.gammon.com.au/images/Arduino/ADC_internals.png

Ok, also das Bild bzw. den Aufbau kannte ich. Allerdings bin ich kein 
Elektroniker und weiß nix über die Seiteneffekte, die Ihr auf den ersten 
Blick seht.
Ich kann ein Datenblatt lesen und eine Firmware dazu schreiben. Das 
Optimieren der Hardware muss ich anderen überlassen :(

Aber danke für den Hinweis! Dann werde ich noch etwas mehr mit den 
Eingängen experimentieren.

> Wenn zur Compile-Zeit eh alles feststeht wirds mit templates kompakter
> und schneller.

Hm, also wenn ich Funktionsaufrufe vermeide gewinne ich auf jeden Fall 
Laufzeit. Den Bedarf an templates habe ich bislang noch nicht gesehen. 
Vielleicht sehe ich den in der zweiten Runde?
Abgesehen davon gefällt mir auch die Syntax der templates bei der 
Verwendung nicht. Ich finde es nicht lesbarer.
Die Kapselung der Daten kostet auf jeden Fall Laufzeit und 
klassenübergreifend können Speicherbereiche nicht gepackt werden, d.h. 
ich verliere Speicher durch Lücken zwischen den Klassen.
Beides Probleme, die durch templates nicht gelöst werden.

> Eiegntlich bin ich ein großer Freund von C und habe C++ für Controller

Ich liebe das Wort "eigentlich" :D

> Deswegen meine Frage: Wie ist denn das Verhalten von C++ zu C in Bezug
> auf Programmgröße und Ausführgeschwindigkeit?

Nun, es dürfte klar sein, dass man sich in beiden Aspekten Nachteile 
erkauft. Je nachdem, wie gut man in C programmiert hat.
Ich konnte bei mir feststellen, dass C++ ungefär 30% mehr Platz braucht 
und in der Ausführungsgeschwindigkeit gibt es auch deutliche Nachteile. 
Zumindest wenn man effizient in C programmiert hat.

Was man gewinnt ist Lesbarkeit, Flexibilität und Übersichtlichkeit. 
Wobei der Gewinn an Flexibilität sicher am größten ist. Ich finde es 
auch wesentlich angenehmer und weniger fehleranfällig, für 
Pin-Zuordnungen nur noch eine Zeile zu haben (gegenüber 3 in C).
Ob das die Nachteile aufwiegt, muss sicher von Fall zu Fall abgewägt 
werden.

Pauschale Aussagen sind selten richtig.

von Thomas W. (diddl)


Lesenswert?

Mit 30% mehr Code Größe kann ich gut leben, besonders bei den fetten 
AVR.

Aber bei den kleinen Tiny stellt man sich ja schon manchmal die Frage ob 
Assembler notwendig wird. Da wird es mit C++ schon mal gar nicht gehen.



Das Beste ist sicher, wenn man jede Lib erst mal in C implementiert und 
nur bei Bedarf auch in C++ Klassen kapselt.

Auf der anderen Seite wird man immer bei C bleiben, wenn alles benötigte 
in C vorhanden ist und in C++ erst implementiert werden müsste.



Andere Frage: Wenn ich nur die Dateiendung auf .cpp ändere und weiterhin 
C Code schreibe wie bisher, dann müsste doch die selbe Größe und 
Laufzeit raus kommen?

Wenn man dann auf String und das übrige Gefrickel verzichtet und nur ab 
und zu eine Klasse verwendet um Zusatz Hardware zu abstrahieren, dann 
sollte das eigentlich ein guter Kompromiss sein.

Klassen sind eigentlich das einzige was mir in C WIRKLICH fehlt. Und die 
auch nur für ganz bestimmte Dinge wie Hardware Module.

von Holzer (Gast)


Lesenswert?

Thomas W. schrieb:
> Andere Frage: Wenn ich nur die Dateiendung auf .cpp ändere und weiterhin
> C Code schreibe wie bisher, dann müsste doch die selbe Größe und
> Laufzeit raus kommen?

Versuch macht klug,

das sollte so stimmen. Aber der C++ Compiler übersetzt manche "C" - 
Konstrukte nicht. Und auch beim mischen von C und C++ kann es passieren 
das der Linker durch name-mangling 
https://en.wikipedia.org/wiki/Name_mangling
nicht mehr alles zusammen passt.

C++ Compiler bringen aber auch ein wenig was bei "reinem" C.
So sind Funktionen uberladungen möglich und default parameter gehen 
auch.

von Reinhard M. (reinhardm)


Lesenswert?

> Aber bei den kleinen Tiny stellt man sich ja schon manchmal die Frage ob
> Assembler notwendig wird. Da wird es mit C++ schon mal gar nicht gehen.

Wie gesagt - ich will keine eierlegende Wollmilchsau.
... und für die kleinen Tinys finde ich C++ sowieso fehl am Platz.

> Wenn man dann auf String und das übrige Gefrickel verzichtet und nur ab
> und zu eine Klasse verwendet um Zusatz Hardware zu abstrahieren

Nun, dann schau Dir mal die Klassen an. Da gibt es keine String-Klasse!
Wozu auch!

Arduino ist für mich ein Beispiel, wie man es nicht machen sollte. Die 
Abstraktion nimmt jeden Bezug zum Datenblatt und man erkauft sich jede 
Menge Nachteile, ohne wirklich Vorteile zu erhalten.
Nee, dafür ist mir meine Zeit zu schade!

Von der ganzen eingangs zitierten Diskussion hat mich im Grunde nur der 
Beitrag von Karl Heinz Buchegg überzeugt. Die Pinklasse ist so, wie ich 
eine Abstraktion auch anwenden möchte.
An die Vorlage habe ich meine C-Module angepasst. Erstmal nur um die 
Funktionalität zu erreichen. Jetzt kommt die Arbeit, zu schauen, wo sich 
was optimieren lässt.

Um den Sinn von C++ nachvollziehen zu können, muss man sich nur mal 
anschauen, wieviel Aufwand ist es, einen weiteren Taster oder noch 
besser, einen weiteren Drehencoder, vielleicht sogar mit anderem 
Tastverhalten einzubinden.
In beiden Fällen seien die erprobten Dateien für einen Taster bzw. 
Encoder vorhanden.
Ach ja - Taster und Encoder setzen beide auf Interrupt-Verarbeitung.

Bei diesem Vergleich ist C++ (für mich) der ganz klare Sieger. Auch wenn 
ich den Encoder an einen anderen Port hängen muss, weil ich die 
verwendeten Pins anderweitig nutzen will - auch hier (für mich) C++ ganz 
klar im Vorteil.

Deshalb ist das auch meine Stoßrichtung. Kleine Klassen, die man einfach 
verwenden kann und bei deren Verwendung man weniger Fehler machen kann, 
als in der C-Variante.

Kritisch wird es erst, wenn der Platzverbrauch die 90% überschreitet 
oder die Ausführungsschritte nicht mehr in der benötigten Zeit 
abgearbeitet werden können.

von rmu (Gast)


Lesenswert?

Holzer schrieb:
> Das:
> Reinhard M. schrieb:
>> Vor dem
>> Optimieren sollte es erstmal zuverlässig laufen.
>
> stimmt mehr als das
>
> rmu schrieb:
>> Mit den virtuellen Methoden etc.. würd ich aufpassen, das braucht viel
>> Platz und hat auch Laufzeit-Overhead.
>
> also erst weitermachen und wenn du dann Platz oder Laufzeit Probleme
> bekommst, kannst du immer noch schauen.

Die Frage ob man eine Bindung zur Laufzeit oder Compilezeit vornimmt ist 
eine Frage des Designs, nicht der Optimierung. Polymorphismus als 
Selbstzweck sieht zwar vielleicht nett aus, ist aber schädlich für 
Codesize, Laufzeit und Wartbarkeit.

von Stefan F. (Gast)


Lesenswert?

> Wie ist denn das Verhalten von C++ zu C in Bezug
> auf Programmgröße und Ausführgeschwindigkeit?

Es kann auf ungefähr 0% Overhead hinaus laufen. Nämlich wenn du auf 
virtuelle Methoden, Exceptions und dynamische Speicherverwaltung 
komplett verzichtest.

von rmu (Gast)


Lesenswert?

Stefan U. schrieb:
>> Wie ist denn das Verhalten von C++ zu C in Bezug
>> auf Programmgröße und Ausführgeschwindigkeit?
>
> Es kann auf ungefähr 0% Overhead hinaus laufen. Nämlich wenn du auf
> virtuelle Methoden, Exceptions und dynamische Speicherverwaltung
> komplett verzichtest.

Es kann durchaus auch kleiner/schneller werden, sogar mit Exceptions, 
das kommt aber ganz auf die Anwendung drauf an.

Mit Exceptions kann man die Behandlung von Fehlern "zentralisieren". 
Fehlermeldungen über Rückgabewerte zu machen zwingt zu Fehlerabfrage 
nach jedem Funktionsaufruf. Disclaimer: ich hab keine Ahnung, ob der GCC 
auf AVR exceptions unterstützt und wie die dort implementiert sind, auf 
ARMs (M0 aufwärts) funktionert das aber ausgezeichnet und ohne 
Laufzeit-Overhead im nicht-exception-Fall.

Mit templates kann man Effekte erzielen die in C nur mit Makros möglich 
sind. Es gibt sicher einiges was von der Sprache und von den 
Implementierungen bei den templates nicht optimal gelöst ist, lesbarer 
und wartbarer als Makros sind sie aber alle mal.

von avr-g++ Fanboy (Gast)


Lesenswert?

Das Hauptproblem ist meiner Meinung nach, dass viele Leute C++ nur
als ein "C mit Klassen" ansehen und es dementsprechend auch nur wie
ein "aufgebohrtes C" verwenden.

Wer C++ aber als eigenständige Sprache betrachtet und die gebotenen
Möglichkeiten vernünftig einsetzt, wird feststellen, dass ein C++-
Compiler durchaus deutlich effizienteren Maschinen-Code erzeugen kann 
als ein C-Compiler. Ja, das ist wirklich so. Wer es nicht glaubt
(und das dürften wohl die wenigsten der C-Verfechter), kann sich
für den Anfang ja mal das hier ansehen und wirken lassen:
https://www.youtube.com/watch?v=zBkNBP00wJE

von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

> Polymorphismus als Selbstzweck sieht zwar vielleicht nett aus, ist aber
> schädlich für Codesize, Laufzeit und Wartbarkeit.

Einverstanden. Wie gut, dass ich nix zum Selbstzweck mache :D

Ich habe bislang wenig template-Beispiele gesehen, die mich animiert 
hätten, es nach zu machen. Bei den meisten template-Geschichten kam es 
mir so vor, als wenn Blinde über Farben philosophieren. Wenn es dann 
wirklich mal ans Eingemachte geht, wird es schnell sehr ruhig in der 
template-Fangemeinde.

> Es kann durchaus auch kleiner/schneller werden, sogar mit Exceptions,
> das kommt aber ganz auf die Anwendung drauf an.

Na dann liefere doch mal ein Beispiel.

Ich habe hier ein ganz triviales Beispiel zusammen gestrickt - ohne 
Selbstzweck, ohne templates, ohne virtuelle Funktionen ...
Einfach nur zwei Pins die regelmäßig umgeschaltet werden.

Über das Auskommentieren von defines lassen sich unterschiedliche 
Varianten übersetzen. Jeweils mit den gleichen Compiler-Schaltern.

Die Plain-C Variante kommt auf 164 byte program und 0 byte data
Die C++ Variante mit der Pin-Klasse von Meister buchegg kommt bei mir 
auf 380 bytes program und 24 bytes data
Der Versuch einer Optimierung kommt auf 308 byte program und 0 byte 
data.

Leider weiß ich nicht, wie Meister Buchegg zu seinem knackigen Code kam. 
Bei mir will das nicht so richtig.

Leider folgt der compiler (avr-g++ 4.8.1) nicht den inline Vorgaben und 
macht eigenmächtige Funktionen draus, sodass die C++-Variante auch 
Laufzeit-Einbußen verzeichnen muss.

Wenn mir also jemand erklären könnte, wie ich dem Compiler abgewöhnen 
kann, einen Zeiger erst in 2 Register zu laden und dann den Wert zu 
interpretieren, dem wäre ich sehr verbunden.

Auch sonstige weiterführende Tips werden gerne angenommen. Ich lerne 
gerne dazu :)
Auf theoretisches Vielosofieren habe ich keinen Bock.

von Thomas W. (diddl)


Lesenswert?

Deine source files sind .c nicht .cpp

Was verwendest du für einen Compiler, dass der nicht meckert?


Hat es einen besonderen Grund warum .c?

von avr-g++ Fanboy (Gast)


Lesenswert?

Reinhard M. schrieb:
> Ich habe hier ein ganz triviales Beispiel zusammen gestrickt

Entschuldige bitte, aber mit so einem Spaghetti-Code mit zig #ifdefs, 
die nicht mal vernünftig eingerückt sind, möchte ich mich ehrlich gesagt 
nicht großartig auseinander setzen. Und dein Makefile ist wohl auch eher 
als Beitrag für den Code Obfuscation Contest gedacht. Es wäre vielleicht 
zunächst sinnvoll, wenn du mit einem Minimalbeispiel anfängst und dich 
dann schrittweise weiter vorarbeitest.

> Leider folgt der compiler (avr-g++ 4.8.1) nicht den inline Vorgaben

Wenn ich aus deinem Makefile schlau werde, benutzt du sowohl -Os als 
auch den Debug Mode. Von daher wundert mich das jetzt nicht wirklich. 
Also nochmal der Tip, lieber mit einem Minimalbeispiel anzufangen und 
vor allem auch die Compiler Flags zunächst sparsam einzusetzen, bis du 
wirklich durchsteigst, welches Flag welche Auswirkungen auf den 
erzeugten Maschinencode hat.

Thomas W. schrieb:
> Deine source files sind .c nicht .cpp

Seine Source Files sind nicht .c, sondern .C - das ist ein Unterschied!
Natürlich ist .cpp verbreiteter, aber .C ist ebenso erlaubt.

von Stefan F. (Gast)


Lesenswert?

> ich hab keine Ahnung, ob der GCC auf AVR exceptions unterstützt

"Exceptions are not supported"

http://www.nongnu.org/avr-libc/user-manual/FAQ.html

von Stefan F. (Gast)


Lesenswert?

> Leider folgt der compiler (avr-g++ 4.8.1) nicht den inline Vorgaben

Doch, tut er. Sieh Dir das folgende Beispiel an, alles mit -O1 
cmpiliert:

In C:
1
#include <avr/io.h>
2
#include <stdint.h>
3
4
void gehe_an(uint8_t pin)
5
{
6
    PORTB |= (1<<pin);
7
}
8
9
void gehe_aus(uint8_t pin)
10
{
11
    PORTB |= (1<<pin);
12
}
13
14
int main(void) 
15
{    
16
    while(1)
17
    {
18
        gehe_an(1);
19
        gehe_aus(1);
20
    }
21
}

-> 110 Bytes

In C mit inline:
1
#include <avr/io.h>
2
#include <stdint.h>
3
4
inline void gehe_an(uint8_t pin)
5
{
6
    PORTB |= (1<<pin);
7
}
8
9
inline void gehe_aus(uint8_t pin)
10
{
11
    PORTB |= (1<<pin);
12
}
13
14
int main(void) 
15
{    
16
    while(1)
17
    {
18
        gehe_an(1);
19
        gehe_aus(1);
20
    }
21
}

-> 62 Bytes

In C++:
1
#include <avr/io.h>
2
#include <stdint.h>
3
4
5
class MeinPin 
6
{
7
    public:
8
        MeinPin(uint8_t nummer);
9
        void gehe_an();
10
        void gehe_aus();
11
    private:
12
        uint8_t nummer;
13
};
14
15
MeinPin::MeinPin(uint8_t nummer)
16
{
17
    this->nummer=nummer;
18
}
19
20
void MeinPin::gehe_an()
21
{
22
    PORTB |= (1<<nummer);
23
}
24
25
void MeinPin::gehe_aus()
26
{
27
    PORTB &= ~(1<<nummer);
28
}
29
30
31
int main(void) 
32
{    
33
    MeinPin pin(1);
34
    while(1)
35
    {
36
        pin.gehe_an();
37
        pin.gehe_aus();
38
    }
39
}

-> 146 Bytes, also ca 30% Overhead.

Und jetzt in C++ mit inline:
1
#include <avr/io.h>
2
#include <stdint.h>
3
4
5
class MeinPin 
6
{
7
    public:
8
        MeinPin(uint8_t nummer);
9
        inline void gehe_an();
10
        inline void gehe_aus();
11
    private:
12
        uint8_t nummer;
13
};
14
15
MeinPin::MeinPin(uint8_t nummer)
16
{
17
    this->nummer=nummer;
18
}
19
20
void MeinPin::gehe_an()
21
{
22
    PORTB |= (1<<nummer);
23
}
24
25
void MeinPin::gehe_aus()
26
{
27
    PORTB &= ~(1<<nummer);
28
}
29
30
31
int main(void) 
32
{    
33
    MeinPin pin(1);
34
    while(1)
35
    {
36
        pin.gehe_an();
37
        pin.gehe_aus();
38
    }
39
}

-> 62 Bytes,  Überraschung!!! 0% Overhead

von Reinhard M. (reinhardm)


Lesenswert?

@avr-g++ Fanboy
Geiles video! Danke für den Link! Das muss ich mir in Ruhe nochmal 
reinziehen.

> Entschuldige bitte, aber mit so einem Spaghetti-Code mit zig #ifdefs,
> die nicht mal vernünftig eingerückt sind, möchte ich mich ehrlich gesagt
> nicht großartig auseinander setzen.

Ja genau - war mir irgendwie klar. Keine Ahnung, aber wie soll man 
sequentielle Zeilen einrücken?

> Und dein Makefile ist wohl auch eher als Beitrag für den Code Obfuscation
> Contest gedacht.

LOL - das Makefile ist schon über 10 Jahre alt. Seinerzeit habe ich mir 
die Entwicklungsumgebung für AVR-Projekte aufgesetzt. Ich weiß nimmer, 
welches Hilfsmittel es war, aber die Makedatei wurde generiert.
Ich habe sie solange angepasst bis es nach meiner Vorstellung 
funktionierte und seither wird die Datei von Projekt zu Projekt weiter 
kopiert. Warum sollte ich was in Frage stellen, was funktioniert und ich 
sowieso nicht besser machen kann?

Aktuell habe ich nur einige Stellen für die C++-Unterstützung angepasst.
Aber hey - man kann alles schlecht reden.

> Wenn ich aus deinem Makefile schlau werde, benutzt du sowohl -Os als
> auch den Debug Mode.

Das sind sicher zwei paar Stiefel. Wenn ich mir die 
Befehlszeilenausgaben anschaue, dann sehe ich da keine Debug-Optionen

> mit -O1 cmpiliert:

Hm, irgendwo las ich mal, dass man beim AVR die -O1 nicht verwenden 
sollte, sondern nur -Os
Wenn sich daran was geändert hat, dann habe ich das nicht mitbekommen.

> Sieh Dir das folgende Beispiel an

Ganz klasse! Das ist jetzt nicht Dein Ernst, oder?
Was soll da der Vorteil Deiner Klassen sein?
Du verwendest das PORT-Makro in den Klassen-Funktionen und wenn Du jetzt 
nen Pin von PortC verwenden willst, dann brauchst Du ne neue Klasse.
Ja, so macht der Einsatz von C++ wirklich Sinn =:O

von Lutz (Gast)


Lesenswert?

Bitte korrigiere in C und C inline die Definition in gehe_aus() und 
compiliere neu.

von avr-g++ Fanboy (Gast)


Lesenswert?

Reinhard M. schrieb:
> @avr-g++ Fanboy
>> Entschuldige bitte, aber mit so einem Spaghetti-Code mit zig #ifdefs,
>> die nicht mal vernünftig eingerückt sind, möchte ich mich ehrlich gesagt
>> nicht großartig auseinander setzen.
>
> Ja genau - war mir irgendwie klar. Keine Ahnung, aber wie soll man
> sequentielle Zeilen einrücken?

Mit Tabs oder Leerzeichen.

>> Sieh Dir das folgende Beispiel an
>
> Ganz klasse! Das ist jetzt nicht Dein Ernst, oder?
> Was soll da der Vorteil Deiner Klassen sein?
> Du verwendest das PORT-Makro in den Klassen-Funktionen und wenn Du jetzt
> nen Pin von PortC verwenden willst, dann brauchst Du ne neue Klasse.
> Ja, so macht der Einsatz von C++ wirklich Sinn =:O

Ehrlich gesagt frage ich mich schon, ob du nur trollen willst oder 
wirklich nicht zu dieser einfachen Transferleistung im Stande bist!?
Falls du kein Troll sein solltest, möchte ich dir nahelegen,
deine generelle Herangehensweise an solche Themen zu hinterfragen.
In der Form ist das nämlich sicherlich nicht zielführend.

1
#include <avr/io.h>
2
#include <stdint.h>
3
4
class MeinPin 
5
{
6
    public:
7
        MeinPin(uint8_t io_register, uint8_t nummer);
8
        inline void gehe_an();
9
        inline void gehe_aus();
10
    private:
11
        volatile uint8_t io_register;
12
        const uint8_t nummer;
13
};
14
15
MeinPin::MeinPin(uint8_t io_register, uint8_t nummer)
16
    : io_register(io_register), nummer(nummer)
17
{
18
}
19
20
void MeinPin::gehe_an()
21
{
22
    io_register |= (1<<nummer);
23
}
24
25
void MeinPin::gehe_aus()
26
{
27
    io_register &= ~(1<<nummer);
28
}
29
30
int main(void) 
31
{    
32
    MeinPin pin(PORTB, 1);
33
    while(1)
34
    {
35
        pin.gehe_an();
36
        pin.gehe_aus();
37
    }
38
}

Die Code-Größe ändert sich dadurch übrigens um kein einziges Byte.
Und natürlich ist das Beispiel immer noch trivial. Es bringt aber
doch nichts, hier seitenlangen "Produktiv-Code" runterzuleiern,
den du am Ende erst recht nicht nachvollziehen können wirst.

von Stefan F. (Gast)


Lesenswert?

> irgendwo las ich mal, dass man beim AVR die -O1 nicht
> verwenden sollte, sondern nur -Os

Das kommt ganz darauf an, wie du den Code optimiert haben möchtest. -Os 
minimiert den Flash bedarf. Der Schuss KANN allerdings nach hinten los 
gehen und mit schlechter Performance und übermäßigem Stack Bedarf enden. 
Muss nicht, aber kann.

-O1 optimiert den Code auf Performance. Auch dies KANN negativ enden, 
nämlich mit übermäßigem Flash Bedarf.

Häufig liefern beide Varianten sehr ähnlichen Code. Meistens liefern 
beide Varianten ein brauchbares Ergebnis.


>> Sieh Dir das folgende Beispiel an
> Ganz klasse! Das ist jetzt nicht Dein Ernst, oder?
> Was soll da der Vorteil Deiner Klassen sein?

Die Klasse soll gar nicht Vorteilhaft sein. Mein Beispiel sollte nur 
zeigen, wie viel Overhead C++ kostet, wenn man es sparsam einsetzt.

von David U. (dsu)


Angehängte Dateien:

Lesenswert?

Hi,

Der Thread hat mich jetzt dazu gebracht mich hier anzumelden...
Ich bin auch seit ein paar Wochen dabei mich einzuarbeiten in Elektronik 
und Mikrocontroller und hab nen ziemlich starken C++ Hintergrund - also 
musste ich das natuerlich als erstes ausprobieren ;)

Ich leg meine Version hier einfach mal mit hin als Beispiel fuer nen 
anderen Ansatz mit C++.
Die Version setzt stark auf templates und inlining - keine exceptions, 
laufzeitpolymorphie oder degleichen.
Ausserdem ist das alles header only, also wird nix sinnlos dazugelinkt.

Ziel ist geringe Codegroesse und Typsicherheit - aber ich bin natuerlich 
noch am Anfang und noch net so ganz drin in der Materie...

test.cpp hoert auf interrupts, kommuniziert ueber uart, steuert ein 
display an ueber spi, liest den adc aus, macht pwm und benutzt einen 
timer.

Will jetzt deswegen keinen neuen Thread aufmachen, aber vielleicht 
passts ja hierhin, eben als beispiel fuer c++ (mit template magie), 
nicht "c mit klassen"

von Thomas W. (diddl)


Lesenswert?

Okay, ihr habt mich vollkommen überzeugt von C++.
Meine Vorurteile muss ich über Board werfen.

Nach 4 Stunden probieren und testen kann ich sagen, dass man absolut 
verlustfrei in C++ coden kann für AVR µC, egal welche Sorte.


Vielen Dank für die Inspiration :-)

: Bearbeitet durch User
von Exaktomator (Gast)


Lesenswert?

Thomas W. schrieb:
> dass man absolut verlustfrei in C++ coden kann für AVR µC

Mit einer Teilmenge von C++, um etwas genauer zu sein.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Den folgenden Talk von der CppCon fand ich sehr spannend: 
https://www.youtube.com/watch?v=zBkNBP00wJE&list=PLHTh1InhhwT7J5jl4vAhO1WvGHUUFgUQH&index=4

Dabei zeigt der Kollege, welche Abstraktionen alles möglich sind, die 
vom Compiler dann z.T. komplett weg optimiert werden.

Heute hatte ich auch wieder so ein Erlebnis: uController Hersteller 
erstellt für seine Controller eine C-Abstraktions-Library. Die hat eine 
Funktion, um die 3 Parameter einer PLL zu ermitteln. Die Funktion wird 
beim Start immer mit dem selben Parameter aufgerufen und liefert auch 
immer das selbe Ergebnis. Die Implementierung iteriert mit drei 
verschachtelte Schleifen über alle Möglichen Werte.

In C++ könnte man das als constexpr functions implementieren und der 
compiler würde dann das Ergebnis zur compiler-Zeit berechnen.

mfg Torsten

von Reinhard M. (reinhardm)


Lesenswert?

@David Uebler
Ich habe mir Deine Magie runtergeladen und angeschaut.
Die Klassen sind ja ganz ordentlich geschrieben. Hast Dir viel Arbeit 
gemacht.
Wenn ich mir allerdings die "Projekt"-Dateien anschaue, dann frag ich 
mich schon: und wo liegt jetzt genau der Vorteil?

Die test.cpp sieht grausig aus und ich kann keinen Vorteil gegenüber 
einer C-Variante entdecken. Es ist weder lesbarer, noch 
fehlertoleranter, noch kürzer. avr-g++ Fanboy müsste das eigentlich auch 
wieder als Spaghetti-Code bezeichnen.

Ähnlich sieht es mit dem Makefile aus. Scheinbar lesbarer, aber nicht 
für andere Projekt verwendbar. Das Makefile dagegen, das ich verwende, 
ist jederzeit für jedes Projekt verwendbar. Minimalste Anpassungen und 
es ist wieder verwendbar. Da hat sich mal ein erfahrener Entwickler 
(nicht ich) viel Gedanken gemacht und es ausgetüftelt. In meiner 
Variante habe ich zudem eine Trennung von Quellcode und Binärdateien - 
etwas, was mir auch sehr wichtig ist.

Last not least habe ich mir die lss-Datei angeschaut. Und die reißt mich 
auch nicht wirklich vom Hocker. Da sind jede Menge Funktionsaufrufe und 
die Interrupt-Routinen beinhalten auch nur Funktionsaufrufe - also 
eigentlich etwas, was man vermeiden möchte. Durch den Funktionsaufruf 
aus der Interrupt-Routine heraus kann der Compiler die Register nicht 
mehr überwachen und muss alle sichern und wieder herstellen. Auch wieder 
suboptimal.

Somit hat Dein Upload wieder mein Vorurteil bestätigt: template-Magie 
sind Geschichten, an denen sich Theoretiker aufgeilen können, aber für 
den Praktiker wenig hilfreich/überzeugend

von Planlos (Gast)


Lesenswert?

Stefan U. schrieb:
> Und jetzt in C++ mit inline:
> -> 62 Bytes,  Überraschung!!! 0% Overhead

(Übergabe von PORTB als uint8_t geht übrigens so nicht.)

Aber: mach zum Vergleich mal eine Version mit template, vor allem die 
Pin-Nummer als Template-Parameter.

Die reduziert sich dann auf wenige Bytes, effektiv wird gehe_an und 
gehe_aus dann jeweils zu einem einzigen ASM-Befehl.

Weil "1<<variable" auf dem AVR als Schleife implementiert ist, spart 
allein die zur Kompilezeit bekannte Pin-Nummer viel Rechenzeit und 
Flash, das sollte aber auch bei der inline/const-member Version schon 
klappen.

von Reinhard M. (reinhardm)


Lesenswert?

So, ich habe noch einen Vergleich gemacht und zwar mit 
reinterpret_cast
Hier zuerst die alte Klasse:
1
class Pin
2
{
3
public:
4
  Pin(uint8_t port, uint8_t pin)
5
   : port((volatile uint8_t*)port)
6
   , pin(pin) {
7
    };
8
9
  inline void toOutput() { *(port - 1) |=   1 << pin;  };
10
  inline void toInput()  { *(port - 1) &= ~(1 << pin); };
11
12
  inline void setLow()   { cli(); *port &= ~(1 << pin); sei(); };
13
  inline void setHigh()  { cli(); *port |=   1 << pin;  sei(); };
14
  inline void toggle()   { cli(); *port ^=   1 << pin;  sei(); };
15
16
  inline int8_t isHigh() const  { return  *(port - 2) & (1 << pin);  };
17
  inline int8_t isLow() const   { return !*(port - 2) & (1 << pin);  };
18
19
  inline uint8_t getPin() const { return pin; };
20
21
private:
22
  volatile uint8_t* port;
23
  const uint8_t  pin;
24
  };

und hier jetzt die neue:
1
class Pin
2
{
3
public:
4
  Pin(uint8_t port, uint8_t pin)
5
   : port(port)
6
   , pin(pin) {
7
    };
8
9
  inline void toOutput() { *reinterpret_cast<uint8_t*>(port-1) |=   1 << pin;  };
10
  inline void toInput()  { *reinterpret_cast<uint8_t*>(port-1) &= ~(1 << pin); };
11
12
  inline void setLow()   { cli(); *reinterpret_cast<uint8_t*>(port) &= ~(1 << pin); sei(); };
13
  inline void setHigh()  { cli(); *reinterpret_cast<uint8_t*>(port) |=   1 << pin;  sei(); };
14
  inline void toggle()   { cli(); *reinterpret_cast<uint8_t*>(port) ^=   1 << pin;  sei(); };
15
16
  inline int8_t isHigh() const  { return  *reinterpret_cast<uint8_t*>(port-2) & (1 << pin); };
17
  inline int8_t isLow() const   { return !*reinterpret_cast<uint8_t*>(port-2) & (1 << pin); };
18
19
  inline uint8_t getPin() const { return pin; };
20
21
private:
22
  volatile uint8_t port;
23
  const uint8_t pin;
24
  };

Wie sich heraus stellte, kann der avr-gcc in Version 4.8.1 die 
Optimierungen der PC-Variante gleicher Version noch nicht. Es wird also 
noch eine Weile dauern, bis man die Tips aus dem Video auch umsetzen 
kann.

Die Klasse mit dem Zeiger braucht 334 bytes und die Klasse mit dem cast 
326 bytes. Der Unterschied reißt es jetzt nicht wirklich raus und 
wirklich leserlicher finde ich die Klasse mit dem cast auch nicht.

Die Leute, die meine Beiträge negativ bewerten dürften selbst mal die 
Assemblerausgaben vergleichen und beurteilen. Ist meine Einschätzung 
wirklich so daneben?
... und der nicht funktionierende Schwachsinn von hier ist dann besser? 
Beitrag "Re: AVR und C++ - ein Versuch"

von Kaj (Gast)


Lesenswert?

Planlos schrieb:
> allein die zur Kompilezeit bekannte Pin-Nummer
Die muss ja aber nicht immer zur Compiletime bekannt sein. Damit duerfte 
sich dieser "Vorteil" dann doch wieder erledigen, oder nicht? :o

von Wilhelm M. (wimalopaan)


Lesenswert?

Also das Makefile ist doch ok: mehr braucht es doch für ein Projekt mit 
einer header-only-template-lib nicht!

Ein C++-Code, der auf Heap, RTTI und runtime-polymorphism und exceptions 
verzichtet und stattdessen parametric-polymorphism zusammen mit ein paar 
patterns wie monostate verwendet, dabei mindestens C++14 ist, und auch
neue features wie closures und variadic templates benutzt, ist m.E. sehr
gut für bare metal geeignet. Und produziert keinesfalls größe 
executables als C und der Verbrauch an stack ist auch nicht größer 
(eigentlich aheb ich eher die Erfahrung gemacht, dass die Größen 
schrumpfen).

Leider gibt es keine wirklich ganz brauchbaren Bibliotheken (jedenfalls 
für mich) und die Hersteller wie etwa Atmel mit den Tinys/Megas machen 
einem das Leben wirklich nicht einfach den Code generisch für eine ganze 
Familie von MCUs zu schreiben (ok, bei den XMegas sieht es etwas besser 
aus).

VG
 Wilhelm

von Sheeva P. (sheevaplug)


Lesenswert?

Kaj schrieb:
> Planlos schrieb:
>> allein die zur Kompilezeit bekannte Pin-Nummer
> Die muss ja aber nicht immer zur Compiletime bekannt sein. Damit duerfte
> sich dieser "Vorteil" dann doch wieder erledigen, oder nicht? :o

Du weißt zur Compiletime nicht, wie Deine Hardware aussieht?

von MCUCPP (Gast)


Angehängte Dateien:

Lesenswert?

@Wilhelm M.

>Leider gibt es keine wirklich ganz brauchbaren Bibliotheken (jedenfalls
>für mich) und die Hersteller wie etwa Atmel mit den Tinys/Megas machen
>einem das Leben wirklich nicht einfach den Code generisch für eine ganze
>Familie von MCUs zu schreiben

Sicher gibt es brauchbare Bibilotheken in C++

https://github.com/KonstantinChizhov/Mcucpp

Dauerte ein Weilchen bis ich <Template> & Co. verstanden habe.

Mcucpp ist ein riesiger Fundus und funktioniert prima. Die Beispiele 
zeigen den Weg. Ohne vertieftes C++ Wissen muss man sich zuerst 
einarbeiten und allenfalls Vorurteile beiseite legen. Aber vom Konzept 
her bin ich überzeugt, dass modular aufgebauter C++ Code besser lesbar 
und universeller verwendbar ist als Plain_C.

Beispiel:
********
MAX7219 / MAX7221 8-Digit LED Display Driver

- Übergabe der uC_Pins und div. Parameter als <template>
- Erstellen eines CTRL-Bus mittels eine Pinlist
-.h(pp) File enthält die Klasse und meist auch alle Methoden.

Die Initialisierung in Projekt.cpp sieht dann so aus

//display driver
// *******************************************
  typedef cMax7219 <
    Pb1,                  // DIN
    Pb3,                  // LOAD
    Pb2,                  // CLK
    8,                    // digits
    1                     // intensity
 // default               // decode
// *******************************************
>_Display;

von Wilhelm M. (wimalopaan)


Lesenswert?

Ja, die (Deine?) Bibliothek kenne ich. Die finde ich auch im Ansatz ganz 
gut! Was ich meinte war etwas wie die avr-libc, die als größeres 
OSS-Projekt (dauerhaft) Bestand hat.

: Bearbeitet durch User
von MCUCPP (Gast)


Lesenswert?

MCUCPP ist nicht von mir,
so tief werde ich C++ nie verstehen,
Link und Author stehen ja im Beispiel.

Wenn ich es richtig verstehe, ist AVR-Lib eine vorkompilierte Bibliothek 
mit Header-Dateien .h und vorkompilierten Dateien .S.

Da ist nichts C++ und wird vermutlich auch noch länger nicht sein.

Ob man so eine Bibliothek ohne C_Sourcen irgendwie in ein C++_Frame 
einbinden kann weiss ich nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

MCUCPP schrieb:
> MCUCPP ist nicht von mir,

ah, das hatte ich auch nur anhand des nickname vermutet ;-)

> so tief werde ich C++ nie verstehen,
> Link und Author stehen ja im Beispiel.
>
> Wenn ich es richtig verstehe, ist AVR-Lib eine vorkompilierte Bibliothek
> mit Header-Dateien .h und vorkompilierten Dateien .S.

MCUCPP benutzt auch avr-libc wie jedes Programm, das mit avr-gcc 
erstellt wird.

>
> Da ist nichts C++ und wird vermutlich auch noch länger nicht sein.

Ist ja auch eine C-Bibliothek.

von Thomas W. (diddl)


Lesenswert?

Mit C++ Templates habe ich mich bisher kaum beschäftigt. Diese mächtige 
Tool ist super interessant, leider ist die Syntax für mich sehr schwer 
zu verstehen. Vom Prinzip her schon, aber was da genau im Hintergrund 
passiert ...

Habe ich das richtig verstanden? Diese enum{} Sachen dienen vorallem 
dazu, dass das "Objekt" keinen Patz im Speicher braucht. Sonst könnte 
man ja auch einfach Variable verwenden.

Könnte man denn damit das Port und die Bitnummer eines digitalen Ein- 
oder Ausgang "speichern"? Zusammen mit inline Funktionen würde dann 
niemals wirklich RAM belegt. Das Objekt wäre rein virtuell und doch 
könnte man den Ausgang auf high setzen, togglen oder auf low setzen, 
Eingänge abfragen etc. etc.


Bisher habe ich IO per #define definiert und über inline Funktionen 
bedient.

#define LED3  (PORTC, 3)

Schöner wäre natürlich:

dInput button1(PORTC,1);
dOutput led3(PORTC,3);


Ja diese MCUCPP ist sehr schön gemacht. Danke für den Link.

: Bearbeitet durch User
von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

So, ich denke, ich habe den Kasus Knaxus gefunden. Die Pin.h sieht jetzt 
zwar übel aus, aber da das Stoff ist, den man normalerweise von Projekt 
zu Projekt ohne Änderung kopiert, ist mir das egal.
Der Teil, der in jedem Projekt neu geschrieben werden muss, ist mir 
wichtiger und der soll sauber aussehen.

Der Vergleich:
Plain C ergibt 130 byte program
1
#include <avr/io.h>
2
3
int main(void) {
4
  DDRA |=   1 << PA1;
5
  DDRC &= ~(1 << PC5);
6
7
  // main loop
8
  for (;;) {
9
      if (PINC & (1 << PC5)) PORTA &= ~(1 << PA1);
10
      else                   PORTA |=   1 << PA1;
11
      }
12
  }

C++ hat lesbare Pin-Klassen, die auch weiter verwendet werden können und 
braucht genau 8 byte mehr.
1
#include "Pin.h"
2
3
using namespace srd;
4
5
int main(void) {
6
  PortA::Pin1 led;
7
  PortC::Pin5 check;
8
9
  led.toOutput();
10
  check.toInput();
11
12
  // main loop
13
  for (;;) {
14
      if (check.isHigh()) led.setLow();
15
      else                led.setHigh();
16
      }
17
  }
Die einzelnen Pin-Anweisungen sind jetzt in C und C++ identisch (auch in 
der lss-Datei.
Für mich eine Basis, den Rest daran anzupassen :)

von Thomas W. (diddl)


Lesenswert?

Wenn man mehrere Software I2C braucht verwende ich gern die Fleury .s
Die ist halt so klein und funktioniert sehr gut.

Man muss da halt das .s File duplizieren und die IO sowie die 
Funktionsaufrufe anpassen.

Super genial wäre eine C++ Kapselung der Assembler Routinen in eine 
Template Klasse, sodass der Assembler Code automatisch modifiziert wird. 
Die Aufrufe gehen dann elegant über das jeweilige I2C Objekt.

von Thomas W. (diddl)


Lesenswert?

Oh cool, die neue pin.h, einfach simpel und gut verwendbar!
Gefällt mir.

Das

"PortA::Pin1 led;"

ist auch ein cooler Ansatz.

von David U. (dsu)


Lesenswert?

@Reinhard M.

> Wenn ich mir allerdings die "Projekt"-Dateien anschaue, dann frag ich
> mich schon: und wo liegt jetzt genau der Vorteil?

siehe ganz unten in meiner Antwort


> Die test.cpp sieht grausig aus und ich kann keinen Vorteil gegenüber
> einer C-Variante entdecken. Es ist weder lesbarer, noch
> fehlertoleranter, noch kürzer. avr-g++ Fanboy müsste das eigentlich auch
> wieder als Spaghetti-Code bezeichnen.

Ja. Die sieht grausig aus. Aber ich bin auch noch net fertig ;)
Es fehlt noch ein Konzept aus Benutzer Sicht, eine art API.
Habs wie gesagt erst angefangen, also work in progress.
Im Moment ist das wirklich nur zum testen der ganzen Funktionen.


> Ähnlich sieht es mit dem Makefile aus. Scheinbar lesbarer, aber nicht
> für andere Projekt verwendbar. Das Makefile dagegen, das ich verwende,
> ist jederzeit für jedes Projekt verwendbar. Minimalste Anpassungen und
> es ist wieder verwendbar. Da hat sich mal ein erfahrener Entwickler
> (nicht ich) viel Gedanken gemacht und es ausgetüftelt.

Naja das Makefile solls ja auch nur bauen. Du kannst auch dein Makefile 
fuer meinen Code benutzen, ist ja header only.


> In meiner Variante habe ich zudem eine Trennung von Quellcode und Binärdateien -
> etwas, was mir auch sehr wichtig ist.

Trennung von Binaerdateien und Quellcode? versteh nicht was du meinst.


> Last not least habe ich mir die lss-Datei angeschaut. Und die reißt mich
> auch nicht wirklich vom Hocker. Da sind jede Menge Funktionsaufrufe [...]

Kommt auf die optimierungsstufe an. mit O[1/2/3] sind die allermeisten 
weg.
Os schaut vorallem auf die codegroesse, und da koennen sie Sinn machen.
Will man also nicht immer vermeiden denke ich.
Os: 69 calls
01: 15 calls
02: 11 calls
O3: 12 calls


> die Interrupt-Routinen beinhalten auch nur Funktionsaufrufe - also
> eigentlich etwas, was man vermeiden möchte. Durch den Funktionsaufruf
> aus der Interrupt-Routine heraus kann der Compiler die Register nicht
> mehr überwachen und muss alle sichern und wieder herstellen. Auch wieder
> suboptimal.
Bitte schau genauer hin:
Es gibt drei ISRs. Zwei davon sind explizit dynamisch. Das hab ich extra 
so eingebaut. Die gehen ueber eine Sprungtabelle (siehe icall) und 
koennen zur Laufzeit umgebogen werden (irq::attach<...>(..)) - ist also 
Absicht.
Einer, der Timer, ist nicht dynamisch und wird geinlined, also kein 
call.


> Somit hat Dein Upload wieder mein Vorurteil bestätigt: template-Magie
> sind Geschichten, an denen sich Theoretiker aufgeilen können, aber für
> den Praktiker wenig hilfreich/überzeugend

Auf den ersten blick, vielleicht.
Aber es gibt einfach viel mehr optimierungspotential fuer den Compiler 
auf die Tour. Meine ganzen "Klassen" zb. haben keinen zustand, und das 
ist echt was wert aus compiler sicht. Es gibt so nur dann branches und 
calls wenn es notwendig ist.

Ausserdem etwas das mir persoenlich ganz wichtig ist: Typsicherheit.
Es gibt in meinem Beispiel extrem viele aufzaehlungen (timer waveforms, 
pwm modi, twi master und slave, etc.) und alles ist in typen gegossen, 
die gleichzeitig einen wert haben. Sobald der benutzer etwas falsch 
macht krachts beim bauen.

Wenn du genau hinschaust wird dir auffallen dass ALLE dateien nichts 
ueber den konkreten mikrocontroller wissen. nur in der atmega328.hpp 
steht welche bits sich auf was beziehen. und eben diese masken werden in 
den rest eingesetzt. Damit gibt es eine zentrale stelle die den 
mikrocontroller beschreibt und dessen eigenheiten in den rest zur 
compilezeit einbaut und zwar ohne ifdefs.
Das mag jetzt was sein was mich "aufgeilt" aber ich find das machts 
sauberer.

Aber eins: Danke fuer den Hinweis, dass die cpp schlimm aussieht. das 
stimmt absolut und ich muss mir echt ne nutzerschicht ueberlegen die 
einheitlich ist. wird nochmal schwer. da sieht deine version schoener 
aus.

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

Sheeva P. schrieb:
> Kaj schrieb:
>> Planlos schrieb:
>>> allein die zur Kompilezeit bekannte Pin-Nummer
>> Die muss ja aber nicht immer zur Compiletime bekannt sein. Damit duerfte
>> sich dieser "Vorteil" dann doch wieder erledigen, oder nicht? :o
>
> Du weißt zur Compiletime nicht, wie Deine Hardware aussieht?
Doch, aber das heisst nicht, dass zur Compiletime auch die Pin-Nummer 
bekannt sein muss, da diese vielleicht erst ueber eine Nachricht (UART, 
Tastendruck, was auch immer) zur Laufzeit reinkommt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bei mir sieht sowas so aus:

1
using PortD = AVR::Port<DefaultMcuType::PortRegister, AVR::D>;
2
using led2 = AVR::Pin<PortD, 7>;
3
4
int main()
5
{
6
    Scoped<EnableInterrupt> interruptEnabler;
7
8
    led2::dir<Output>();
9
    led2::on();
10
11
    AVR::Timer8Bit<0>::prescale<256>();
12
    const uint16_t prescale = 256;
13
    const uint8_t timerTopValue = (Config::fMcu / prescale) / 1000_Hz;
14
15
    AVR::Timer8Bit<0>::ocra(timerTopValue);
16
    AVR::Timer8Bit<0>::mode(AVR::TimerMode::CTC);
17
    AVR::Timer8Bit<0>::start();
18
19
    EventHandlerParameter evp {
20
        Timer::create(1000_ms, TimerFlags::Periodic)
21
    };
22
23
    auto th = TimerHandler(evp);
24
25
    EventManager::run([&]() {}, th);
26
}

VG

von David U. (dsu)


Lesenswert?

Wilhelm M. schrieb:
> Bei mir sieht sowas so aus:
> [...]
>     EventHandlerParameter evp {
>         Timer::create(1000_ms, TimerFlags::Periodic)
>     };
>
>     auto th = TimerHandler(evp);
>
>     EventManager::run([&]() {}, th);
> [...]

Wie sieht denn bei dir der EventManager aus? hoert der auf alle 
interrupts?
oder hast du einen catch-all handler der das dann auseinanderpfrimelt?
Wuerd mich interessieren.

von Cyblord -. (cyblord)


Lesenswert?

Thomas W. schrieb:

> "PortA::Pin1 led;"
>
> ist auch ein cooler Ansatz.

Nur sollte man sich an die Bezeichnungen im Datenblatt halten. Wenn dann 
PA1 und nicht Pin1.

Und hier sieht man schon, dass OO-Abstraktion auf diesem Level absoluter 
Humbug ist.
OOP auf Ebene der Applikation bringt einen echten Mehrwehrt aber doch 
nicht die Abstraktion über das setzen eines Pins.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

David U. schrieb:
> Wilhelm M. schrieb:
>> Bei mir sieht sowas so aus:
>> [...]
>
> Wie sieht denn bei dir der EventManager aus? hoert der auf alle
> interrupts?

Nein, in dem Beispiel liefert der HW-Timer AVR::Timer<0> einen ms-Tick
an den Event-Manager. Der Event-Manager ist der Dispatcher: holt einen 
Event aus der Event-Queue und ruft den passenden Handler auf. Wobei das 
Closure in

EventManager::run([&]() {}, th);

permanent aufgerufen wird. Aber so ein leeres Closure wird natürlich vom 
Compiler wegoptimiert ...

> oder hast du einen catch-all handler der das dann auseinanderpfrimelt?
> Wuerd mich interessieren.

von David U. (dsu)


Lesenswert?

Wilhelm M. schrieb:
> David U. schrieb:
>> Wilhelm M. schrieb:
>>> Bei mir sieht sowas so aus:
>>> [...]
>>
> [...]
> Nein, in dem Beispiel liefert der HW-Timer AVR::Timer<0> einen ms-Tick
> an den Event-Manager. Der Event-Manager ist der Dispatcher: holt einen
> Event aus der Event-Queue und ruft den passenden Handler auf.

ah ok, verstehe. aber iwo musst du doch nen interrupt vektor aufsetzen.
wo passiert das?

von Wilhelm M. (wimalopaan)


Lesenswert?

Das schreibt man einfach in die (einzige) Implmentierungsdatei:
1
ISR(TIMER0_COMPA_vect)
2
{
3
    Timer::timerTick();
4
}

Alles andere sind ja Header.

(Hatte mich oben vertan: die HW-Timer liefert den ms-Tick an den 
SW-Timer. Läuft ein SW-Timer ab, so stellt er einen Event in die 
EventQueue ...)

von David U. (dsu)


Lesenswert?

Wilhelm M. schrieb:
> Das schreibt man einfach in die (einzige) Implmentierungsdatei:
>
>
1
> ISR(TIMER0_COMPA_vect)
2
> {
3
>     Timer::timerTick();
4
> }
5
>

alles klar. ja hm. das nervt mich n bischen am avr-gcc.
der baut die vektor tabelle selbst und schaut nach funktionen die 
__vector_N heissen.
laesst sich wohl auch kaum schoener machen ohne extra praeprozessor, 
oder haste ne idee?

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich denke, solange man avr-libc verwendet, wird man es so machen müssen. 
Denn der Start-Code crt1 überschreibt den InterruptVektor ...

von Thomas W. (diddl)


Lesenswert?

Ja klar, die Vektor Tabelle ist im Flash und muss in den SRAM kopiert 
werden.

Aber was spricht dagegen die Vektoren trotzdem selbst zu setzen? Die 
avr-libc kann man trotzdem verwenden.

von David U. (dsu)


Lesenswert?

Thomas W. schrieb:
> Ja klar, die Vektor Tabelle ist im Flash und muss in den SRAM kopiert
> werden.
>
> Aber was spricht dagegen die Vektoren trotzdem selbst zu setzen? Die
> avr-libc kann man trotzdem verwenden.

hm geht das? wird die echt in den sram kopiert? dachte eigtl die liegt 
nur im flash. wenns so waere koennte man die ja auch zur laufzeit 
umbiegen.
hast du das schonmal ausprobiert?

von Thomas W. (diddl)


Lesenswert?

David U. schrieb:
> hm geht das? wird die echt in den sram kopiert? dachte eigtl die liegt
> nur im flash. wenns so waere koennte man die ja auch zur laufzeit
> umbiegen.
> hast du das schonmal ausprobiert?

Nee, keine Ahnung. Ich habe mich darauf bezogen was Wilhelm M. schrieb:

Wilhelm M. schrieb:
> Ich denke, solange man avr-libc verwendet, wird man es so machen müssen.
> Denn der Start-Code crt1 überschreibt den InterruptVektor ...



Im datenblatt zum Mega1284P steht, die Interrupt tabelle steht an der 
niedrigsten Adresse vom "program memory space".

Also Flash und daher schwer änderbar.

von Wilhelm M. (wimalopaan)


Lesenswert?

Thomas W. schrieb:
> David U. schrieb:
>> hm geht das? wird die echt in den sram kopiert? dachte eigtl die liegt
>> nur im flash. wenns so waere koennte man die ja auch zur laufzeit
>> umbiegen.
>> hast du das schonmal ausprobiert?
>
> Nee, keine Ahnung. Ich habe mich darauf bezogen was Wilhelm M. schrieb:

Darüber habe ich nichts gesagt. Und ja, der Interruptvector steht im 
Flash ab 0x0000.

>
> Wilhelm M. schrieb:
>> Ich denke, solange man avr-libc verwendet, wird man es so machen müssen.
>> Denn der Start-Code crt1 überschreibt den InterruptVektor ...

Schlecht ausgedrückt: der Compiler schreibt in die nicht verwendeten 
Dummy-Sprünge hinein.

>
> Im datenblatt zum Mega1284P steht, die Interrupt tabelle steht an der
> niedrigsten Adresse vom "program memory space".
>
> Also Flash und daher schwer änderbar.

Man könnte aber mal probieren, ob man eine statische Elementfunktion mit 
dem _attribute_ signal austatten kann und ob die dann in den IVektor 
kommt ...

Ggf. kann man auch noch auf den StartCode crt1 mit einer Option an den 
Linker verzichten.

von Di P. (drpepper) Benutzerseite


Lesenswert?

Ich werfe hier einfach mal eine Buchempfehlung rein:

https://www.amazon.de/Real-Time-Efficient-Object-Oriented-Microcontroller-Programming/dp/3642346871

Das hat mir den Einstieg mit C++ auf µCs vereinfacht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Versucht man einer statischen Elementfunktion das __attribute__(signal)
zu verpassen, sagt der avr-g++ (!!!), dass die ISR den falschen Namen 
hat. Also, das wird man nicht so einfach ändern können.

Es sein denn, man schreibt gcrt.S um, so dass dort dann andere Namen als
__vector_1 etc. drin stehen.

Das lohnt m.E. den Aufwand nicht ...

von Sheeva P. (sheevaplug)


Angehängte Dateien:

Lesenswert?

Reinhard M. schrieb:
> So, ich denke, ich habe den Kasus Knaxus gefunden. Die Pin.h sieht jetzt
> zwar übel aus, aber da das Stoff ist, den man normalerweise von Projekt
> zu Projekt ohne Änderung kopiert, ist mir das egal.

Vor einiger Zeit habe ich mir mal Gedanken darüber gemacht, wie man 
Register und Pins etwas eleganter in C++ ausdrücken kann. Das Ergebnis 
findest Du im Anhang in den Dateien "includes/Reg.hpp" und 
"includes/Pin.hpp". Zudem habe ich in den Verzeichnissen "ex0" und "ex1" 
zwei Beispiele beigefügt, die sich mit
1
avr-g++ -mmcu=atmega8 -Os -std=c++11 -I ../includes/ -o main main.cpp
übersetzen lassen.

von c-hater (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Darüber habe ich nichts gesagt. Und ja, der Interruptvector steht im
> Flash ab 0x0000.

Nein, nicht notwendigerweise. Die Interruptvectortabelle kann bei einem 
ATMega üblicherweise an 5 verschiedenen Stellen im Flash liegen...

von Thomas W. (diddl)


Lesenswert?

Sheeva P. schrieb:
> Vor einiger Zeit habe ich mir mal Gedanken darüber gemacht, wie man
> Register und Pins etwas eleganter in C++ ausdrücken kann. Das Ergebnis
> findest Du im Anhang in den Dateien "includes/Reg.hpp" und
> "includes/Pin.hpp". Zudem habe ich in den Verzeichnissen "ex0" und "ex1"
> zwei Beispiele beigefügt, die sich mit
>
1
avr-g++ -mmcu=atmega8 -Os -std=c++11 -I ../includes/ -o main 
2
> main.cpp
> übersetzen lassen.

Danke für deine Code Inspiration.

Du speicherst 32 Bit pro Pin ab, etwas großzügig finde ich. Speziell da 
man DDR und PIN aus PORT errechnen kann, die Register Offsets sind -1 
und -2.

Mir gefällt es an sich, aber ich möchte auf null Byte Objektgröße 
kommen.
Mittels Templates und Inline Funktionen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Thomas W. schrieb:

> Mir gefällt es an sich, aber ich möchte auf null Byte Objektgröße
> kommen.
> Mittels Templates und Inline Funktionen.

Genau: hier ein Beispiel (unvollständig, da die MCU Templates fehlen):

1
template<typename MCUPort, typename Name>
2
struct Port {
3
    typedef MCUPort mcuport_type;
4
    typedef Name name_type;
5
    Port() = delete;
6
    static void set(uint8_t v) {
7
        getBaseAddr<MCUPort, Name>()->out = v;
8
    }
9
    static uint8_t get() {
10
        return getBaseAddr<MCUPort, Name>()->out;
11
    }
12
    static void dir(uint8_t v) {
13
        getBaseAddr<MCUPort, Name>()->ddr = v;
14
    }
15
    static volatile uint8_t& dir() {
16
        return getBaseAddr<MCUPort, Name>()->ddr;
17
    }
18
    static uint8_t read() {
19
        return getBaseAddr<MCUPort, Name>()->in;
20
    }
21
};
22
23
template<typename Port, uint8_t PinNumber>
24
struct Pin {
25
    static_assert(PinNumber < 8, "wrong pin number");
26
    Pin() = delete;
27
    typedef Port port;
28
    static constexpr uint8_t number = PinNumber;
29
    static constexpr uint8_t pinMask = (1 << PinNumber);
30
    static void on() {
31
        Port::set(Port::get() | pinMask);
32
    }
33
    static void off() {
34
        Port::set(Port::get() & ~pinMask);
35
    }
36
    static void toggle() {
37
        Port::set(Port::get() ^ pinMask);
38
    }
39
    template<typename Dir>
40
    static void dir() {
41
        if (Dir::value) {
42
            Port::dir() |= pinMask;
43
        }
44
        else {
45
            Port::dir() &= ~pinMask;
46
        }
47
    }
48
};

Für die Abstraktion von HW-REssourcen macht man keine Objekte, sondern 
nicht instanziierbare class-templates.

von Oke (Gast)


Lesenswert?

Sieht interessant aus. Wie wendet man sowas nun an?

von Oke (Gast)


Lesenswert?

Gerade das template mit dem DIR:
1
 template<typename Dir>
2
    static void dir() {
3
        if (Dir::value) {
4
            Port::dir() |= pinMask;
5
        }
6
        else {
7
            Port::dir() &= ~pinMask;
8
        }
9
    }

von Wilhelm M. (wimalopaan)


Lesenswert?

Bspw. wenn an Port D, Pin 7 eine LED (Ausgang) ist, schreibt man
folgendes:
1
using PortD = AVR::Port<DefaultMcuType::PortRegister, AVR::D>;
2
using led = AVR::Pin<PortD, 7>;
3
4
led::dir<Output>();
5
6
led::on();
7
led::off();

Und das ist zero-overhead gegenüber reinem C-Code.

von Oke (Gast)


Lesenswert?

Und wie ist Output definiert?

von Wilhelm M. (wimalopaan)


Lesenswert?

Folgendermaßen:
1
struct Output {
2
    static constexpr bool value = true;
3
};
4
struct Input {
5
    static constexpr bool value = false;
6
};



Man könnte durch std::enable_if<> und entsprechende traits auch das
Laufzeit-if-statement entfernen. Doch da der Wert ja eh constexpr ist, 
wird das vom Compiler weg optimiert!

von Sheeva P. (sheevaplug)


Lesenswert?

Thomas W. schrieb:
> Du speicherst 32 Bit pro Pin ab, etwas großzügig finde ich.

Das sieht nur so aus; tatsächlich optimiert der avr-g++ das restlos weg.

> Speziell da
> man DDR und PIN aus PORT errechnen kann, die Register Offsets sind -1
> und -2.

Das mag für heutige AVRs der Fall sein, muß aber nicht notwendigerweise 
auch für zukünftige gelten.

> Mir gefällt es an sich, aber ich möchte auf null Byte Objektgröße
> kommen.

Wie gesagt: laß' Dich nicht vom Quellcode täuschen.

> Mittels Templates und Inline Funktionen.

Ich bin gespannt, ob Dir das damit gelingen wird.

von Reinhard M. (reinhardm)


Lesenswert?

Moin moin,

> Es fehlt noch ein Konzept aus Benutzer Sicht, eine art API.

Ok, dann nehme ich alles zurück und behaupte das Gegenteil :D
Ein sehr geschätzter Ausbilder pflegte zu sagen: man darf einem Bauer 
keine halbfertige Arbeit zeigen :O

>> In meiner Variante habe ich zudem eine Trennung von Quellcode und
>> Binärdateien - etwas, was mir auch sehr wichtig ist.
>
> Trennung von Binaerdateien und Quellcode? versteh nicht was du meinst.

Kennst Du cmake? Dort ist es auch Konzept, dass man zum Bauen ein 
build-Verzeichnis anlegt, wo dann die Binär-Dateien erzeugt werden. Ich 
habe es einfacher gehalten und (weil ich auf Linux mit Befehlszeilen 
arbeite, habe ich) ein Verzeichnis "nix" angelegt, in dem das Makefile 
liegt. Dort rufe ich auch make auf. Die Quelldateien liegen im 
Verzeichnis darüber.
Somit wird das Verzeichnis nicht durch Binär- oder Zwischen-Dateien 
verunreinigt :)
Anm: Makefile mit nix-Verzeichnis ist im Paket aus erstem Beitrag 
enthalten.

> Es fehlt noch ein Konzept aus Benutzer Sicht, eine art API.

Hm, da gehe ich einfach anders an die Geschichte heran. Ich komme aus 
der Ecke Benutzer-Ergonomie und habe mir angewöhnt, als erstes die 
Benutzerschnittstelle festzuklopfen. Genauso mache ich es auch beim 
Entwurf von Objekten/Klassen - als erstes überlege ich mir das public 
interface. Erst danach überlege ich mir, wie ich das umsetzen kann.
Ist vielleicht nicht der leichteste Weg, aber die Akzeptanz ist deutlich 
höher.

> Kommt auf die optimierungsstufe an. mit O[1/2/3] sind die allermeisten
> weg.

Ok, als ich mit AVR und Co anfing, hieß es noch, dass O[123] 
falschen/fehlerhaften Code produzieren würde und dass man die 
Optimierungsstufen möglichst vermeiden solle. Wenn die Compiler 
inzwischen besser geworden sind, kann ich ja mal andere 
Optimierungsstufen ausprobieren. So ein compiler, wie in dem Video der 
cppcon wäre schon schick - wird aber sicher noch ein paar Jahre dauern 
:(

> Bitte schau genauer hin:
> Es gibt drei ISRs. Zwei davon sind explizit dynamisch. Das hab ich extra
> so eingebaut.

Lach - das habe ich wohl gesehen, denn es war auch mein erster Ansatz. 
Als ich dann aber sah, was der Compiler draus machte, dachte ich mir, 
das ist bullshit, das muss anders gehen.
Dann habe ich mir angeschaut, was ich denn normalerweise in den 
Interrupt packen würde: Tastenentprellung, Encoder-Auswertung, 
Buzzer-Handling ...
Yo - und dann wurde mir klar, dass nix davon wirklich im ISR ablaufen 
muss.
Deshalb habe ich z.B. die Clock-Klasse gestrichen und die Sekunden in 
den SystemTicker mit rein gepackt. Jetzt zählt der SystemTicker Sekunden 
und Millisekunden.
Letzteres ist als Auflösung fein genug für alle Arten von ISR-clients.
Ist in dem Paket aus dem ersten Post zu sehen.
Jetzt werden die Tasten in der Hauptschleife entprellt. Funktioniert 
gut.
Ich betreibe einen mega16 mit 16MHz, was bedeutet, zwischen 2 
Systemtick-Events können 16.000 Anweisungen ausgeführt werden.
In meinem Fall ist das üppig genug, um nix mehr in die ISR zu packen.
Heißt aber nicht, dass ich jetzt jede Menge überflüssige 
Funktionsaufrufe akzeptieren würde.

> Meine ganzen "Klassen" zb. haben keinen zustand, und das
> ist echt was wert aus compiler sicht. Es gibt so nur dann branches und
> calls wenn es notwendig ist.

Hm, den Vorteil gibt es aber nur auf den ersten, flüchtigen Blick. Die 
Anwendung braucht Zustände und die müssen irgendwo hingepackt werden.
Ich halte es nicht für benutzerfreundlich, wenn man sich als Anwender 
der Klassen noch Gedanken darüber machen muss, welche Zustände die 
Objekte der Klassen brauchen, um funktionieren zu können.
So ein Framework ist ein richtiges Stück Arbeit. Wenn ich mir das antue, 
dann will ich aus Anwendersicht auch einen Vorteil von haben. Wenn 
dagegen sowas rauskommt, wie die Arduino-Programmierschicht, dann lasse 
ich lieber von vorn herein die Finger davon.

Ob die Klassen jetzt schick aussehen (Deine tun das, ganz ohne Frage!) 
ist mir völlig schnuppe. Der Teil der Projekte soll doch möglichst ohne 
Anpassung von Projekt zu Projekt kopiert werden. Also ist dort doch der 
richtige Platz für schmutzige Hacks, um nachher sauberen Anwendungscode 
zu erreichen. Gut, so ist zumindest meine Denke ...
... und wenn ich zurück denke, an die Fremd-Bibliotheken, die mir im 
Laufe der Zeit ans Herz gewachsen sind, dann haben die genau die gleiche 
Philosophie beherzigt :)

> Wenn du genau hinschaust wird dir auffallen dass ALLE dateien nichts
> ueber den konkreten mikrocontroller wissen.

Ich habe es gesehen, frage mich aber: ist das für den Anwender eines 
Frameworks wirklich wichtig?
Also mich interessiert es herzlich wenig, ich will mich eigentlich™ nur 
mit der öffentlichen Schnittstelle des Frameworks auseinander setzen.
Außerdem - egal wie groß der Quellcode für einen AVR auch werden sollte, 
er ist selbst auf einem älteren Rechner in einem Wimpernschlag 
übersetzt. Warum also sollte ich mir da über irgendeine Abhängigkeit 
einen Kopf machen?

Auf dem PC sieht es anders aus. Dort gibt es schon Übersetzungszeiten im 
Stundenbereich. Da werden die Abhängigkeiten wichtiger. Zumindest aus 
meiner Sicht ;)

> Nur sollte man sich an die Bezeichnungen im Datenblatt halten. Wenn dann
> PA1 und nicht Pin1.
>
> Und hier sieht man schon, dass OO-Abstraktion auf diesem Level absoluter
> Humbug ist.

Hach - das sehe ich natürlich ganz anders - so rein subjektiv.
Ich halte es für natürlich, dass ein Port Pins hat. PA1 ist doppelt 
gemoppelt. PortA sagt doch schon, dass es sich um den Port A handelt. 
Wieso sollte ich dann beim Pin nochmal die Abhängigkeit A in den Namen 
packen?
In C mit den #defines muss das sein. In C++ kann man drauf verzichten.
Mir gefällt mein Ansatz besser (logisch, sonst hätte ich es nicht so 
gemacht) :D
... und ich werde auch ganz sicher nicht irgendwelche Registernamen in 
die Benutzerschnittstelle hochziehen. Die haben da imho nix mehr 
verloren.

> alles klar. ja hm. das nervt mich n bischen am avr-gcc.
> der baut die vektor tabelle selbst und schaut nach funktionen die
> __vector_N heissen.
> laesst sich wohl auch kaum schoener machen ohne extra praeprozessor,
> oder haste ne idee?

Schau mal im Paket aus dem ersten Beitrag die Datei SystemTicker.h - 
dort ist der ISR-handler "versteckt"/codiert. Der notwendige Hack für 
avr-gcc taucht sonst nirgendwo mehr auf.
Ich kann damit leben ;)

von Wilhelm M. (wimalopaan)


Lesenswert?

Reinhard M. schrieb:
>> alles klar. ja hm. das nervt mich n bischen am avr-gcc.
>> der baut die vektor tabelle selbst und schaut nach funktionen die
>> __vector_N heissen.
>> laesst sich wohl auch kaum schoener machen ohne extra praeprozessor,
>> oder haste ne idee?
>
> Schau mal im Paket aus dem ersten Beitrag die Datei SystemTicker.h -
> dort ist der ISR-handler "versteckt"/codiert. Der notwendige Hack für
> avr-gcc taucht sonst nirgendwo mehr auf.
> Ich kann damit leben ;)

Das mit der gefakten ISR ist ja eine gute Idee!

BTW:
Dann habe ich mal die Klasse SystemTicker angesehen. Du verwendest da im 
Prinzip ein monostate-pattern. Allerdings funktioniert Deine Klasse nur, 
wenn die erste Instanz weiterhin auf dem Stack liegt - wie in Deiner 
main(), die ja nie verlassen wird. Denn Du speicherst einen this-Zeiger 
in dem statischen Datenelement ticker. Du solltest stattdessen eine 
Instanz in ticker erzeugen. Würdest Du den ctor vonm (ersten) 
SystemTicker verschachtelt in einer Funktion aufrufen, so wird das darin 
enthaltene SystemTicker-Objekt am Ende der Funktion ja zerstört und 
somit ist this dangling. Dies ist konzeptionell falsch - ich vermute 
aber, dass es trotzdem funktionieren würde, weil Du ja keine non-static 
Datenelemente hast.

Im übrigen ist Deine Lösung auch nicht reentrant/thread-safe. Aber das 
spielt hier wohl kein Rolle.

Wenn Du auf den Zeiger verzichtest, brauchst Du auch die Prüfung für den 
Zeiger nicht mehr.

Die fragwürdige Stelle:
1
SystemTicker*     SystemTicker::ticker;
2
         uint16_t SystemTicker::maxTicks;
3
volatile uint16_t SystemTicker::ticks;
4
volatile uint32_t SystemTicker::seconds;
5
6
7
SystemTicker::SystemTicker(uint16_t interruptsPerSecond) {
8
  if (!ticker) {
9
     ticker = this;
10
     // only the first call shall setup the 16bit timer!
11
     setupTimer(interruptsPerSecond);
12
     }
13
  };

von Reinhard M. (reinhardm)


Lesenswert?

@Wilhelm M.
Du hast völlig recht! Mit allem :)
... aber wie Du schon selbst angemerkt hast: main wird ja nie verlassen.
Und wenn, ist das sowieso ein fataler Fehler. Deshalb sehe ich an der 
Stelle keinen Handlungsbedarf.
Außerdem sollte ein Systemticker imho vor der Schleife des 
Hauptprogrammes erzeugt werden.

Die Singleton-Reste könnte ich dagegen rausschmeißen. Sind noch aus der 
Zeit, da ich Funktionaliät anderer Klassen wie bei einem Eventhandler 
einbrachte. Da ich das aber nicht mehr machen will ...
...ok, hast mich überzeugt. Ich schmeiß es raus :)

//Edith:
der letzte Absatz war Kwatsch mit Source :(
Da waren die Finger schneller, als das Hirn. Ich schmeiß das deshalb 
nicht raus, weil ich die SystemTicker-Klasse auch als Handle verwenden 
wollte, um auf Daten zugreifen zu können. Wenn jemand ne Handle-Instanz 
anlegt, soll er aber nicht den Timer initialisieren. Deshalb die 
Singleton-Instanz. Weiß noch nicht, ob ich das so lasse ...

Ich habe jetzt den Rest an die neue Pinklasse angepasst und wie es 
aussieht, ist die neue Pinklasse doch nicht so dolle :(
Während das Beispiel aus dem ersten Beitrag auf 4614 bytes (mit -Os) 
kommt, liegt die Variante mit der neuen Pinklasse bei 5514 bytes und 
wird hässlicher in der Anwendung :(

Bei -O3 sieht das Bild ganz anders aus. Das Paket aus dem ersten Post 
kommt dann auf 8402 bytes, während es mit der neuen Pinklasse 6814 bytes 
sind.
Ich muss die lss-Dateien noch genauer untersuchen, aber derzeit tendiere 
ich dazu, mit der Variante aus dem ersten Beitrag und -Os weiter zu 
machen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Reinhard M. schrieb:
> //Edith:
> der letzte Absatz war Kwatsch mit Source :(
> Da waren die Finger schneller, als das Hirn. Ich schmeiß das deshalb
> nicht raus, weil ich die SystemTicker-Klasse auch als Handle verwenden
> wollte, um auf Daten zugreifen zu können. Wenn jemand ne Handle-Instanz
> anlegt, soll er aber nicht den Timer initialisieren. Deshalb die
> Singleton-Instanz. Weiß noch nicht, ob ich das so lasse ...

Es ist kein Singleton, es ist ein Monostate.

von Thomas W. (diddl)


Lesenswert?

Di P. schrieb:
> Ich werfe hier einfach mal eine Buchempfehlung rein:
>
> 
https://www.amazon.de/Real-Time-Efficient-Object-Oriented-Microcontroller-Programming/dp/3642346871
>
> Das hat mir den Einstieg mit C++ auf µCs vereinfacht.

Hab mir das Buch jetzt gekauft, das Probe Exemplar hat mich überzeugt.

Danke für den Tip!

von David U. (dsu)


Lesenswert?

Morgen auch =)

Reinhard M. schrieb:
> [...]
> Kennst Du cmake?
Jo. mehr als mir lieb ist... schlimmes teil aber leider gut.

> Die Quelldateien liegen im
> Verzeichnis darüber.
> [...]
achso. du meinst dateien - ja ist sauberer. hatte halt bisher nur ein 
elf und ein hex, da wars mir wurscht ;)
make konzept ist out of scope bei mir.


>> Es fehlt noch ein Konzept aus Benutzer Sicht, eine art API.
>
> Hm, da gehe ich einfach anders an die Geschichte heran. Ich komme aus
> der Ecke Benutzer-Ergonomie und habe mir angewöhnt, als erstes die
> Benutzerschnittstelle festzuklopfen. Genauso mache ich es auch beim
> Entwurf von Objekten/Klassen - als erstes überlege ich mir das public
> interface. Erst danach überlege ich mir, wie ich das umsetzen kann.
> Ist vielleicht nicht der leichteste Weg, aber die Akzeptanz ist deutlich
> höher.
Hehe ok lustig. Ich komm aus genau der anderen Ecke, hab bisher meistens 
libs geschrieben und versuch immer zuerst einen minimalen aber 
vollstaendigen unterbau hinzukriegen.

>> Bitte schau genauer hin:
>> Es gibt drei ISRs. Zwei davon sind explizit dynamisch. Das hab ich extra
>> so eingebaut.
>
> [...]
> Heißt aber nicht, dass ich jetzt jede Menge überflüssige
> Funktionsaufrufe akzeptieren würde.
Ja... hm. In produktivcode wuerde ich das wohl auch nicht.
Deckt vielleicht eher meine art ab code zu schreiben:
prototyp -> sauber machen -> optimieren.
da wuerde ich wohl am schluss die sprungtabelle durch ein switch 
ersetzen, wenn ich weiss welche pfade es gibt.

>> Meine ganzen "Klassen" zb. haben keinen zustand, und das
>> ist echt was wert aus compiler sicht. Es gibt so nur dann branches und
>> calls wenn es notwendig ist.
>
> Hm, den Vorteil gibt es aber nur auf den ersten, flüchtigen Blick. Die
> Anwendung braucht Zustände und die müssen irgendwo hingepackt werden.
> Ich halte es nicht für benutzerfreundlich, wenn man sich als Anwender
> der Klassen noch Gedanken darüber machen muss, welche Zustände die
> Objekte der Klassen brauchen, um funktionieren zu können.
Ja ich glaub da haben wir wieder ne andere perspektive.
Ich will einfach nix ueber die anwendung annehmen die der benutzer 
schreiben will, seine zustaende muss er sich schon selber ueberlegen.
ich glaub ich will gar nicht moeglichst freundlich sein zum benutzer, 
sondern ihm moeglichst maechtige und minimale werkzeuge geben. (siehe 
boost)

> So ein Framework ist ein richtiges Stück Arbeit. Wenn ich mir das antue,
> dann will ich aus Anwendersicht auch einen Vorteil von haben. Wenn
> dagegen sowas rauskommt, wie die Arduino-Programmierschicht, dann lasse
> ich lieber von vorn herein die Finger davon.
ich glaub der groesste vorteil ist, dass ich jetzt versteh wie ein avr 
funktioniert, da wars mir der aufwand wert =P
und ich hab halt ne lib die fuer mich passt.
(und ja... motivation die zu schreiben war das arduino zeug. hab ich 
gesehen und mir gedacht "oh gott das kann man doch nicht so machen")


> Ob die Klassen jetzt schick aussehen (Deine tun das, ganz ohne Frage!)
> ist mir völlig schnuppe. Der Teil der Projekte soll doch möglichst ohne
> Anpassung von Projekt zu Projekt kopiert werden. Also ist dort doch der
> richtige Platz für schmutzige Hacks, um nachher sauberen Anwendungscode
> zu erreichen. Gut, so ist zumindest meine Denke ...
> ... und wenn ich zurück denke, an die Fremd-Bibliotheken, die mir im
> Laufe der Zeit ans Herz gewachsen sind, dann haben die genau die gleiche
> Philosophie beherzigt :)
hehe, wieder unsere philosophien. wenn ich dreckige libs schreib krieg 
ich aerger und muss sie nochmal schreiben ;)


>> Wenn du genau hinschaust wird dir auffallen dass ALLE dateien nichts
>> ueber den konkreten mikrocontroller wissen.
>
> Ich habe es gesehen, frage mich aber: ist das für den Anwender eines
> Frameworks wirklich wichtig?
naja noe, solangs funktioniert. aber der anwender bin ja ich, und wenn 
ich mir nen anderen avr kauf will ich gerne die lib schnell auf den 
erweitern koennen ohne ueberall rumsuchen zu muessen wo denn jetzt 
device spezifisches zeugs steht.
so weiss ich halt "ok. neue datei anderer_mcu.hpp, bitmasken rein, wenns 
baut, dann gehts auch."


ich glaub ich schau mir mal die ganzen libs die hier hochgeladen wurden 
aus benutzersicht an und schau mir was ab, und mach meinen unterbau 
weiter im c++14/boost stil ;)

lg und danke fuer die freundliche antwort!

PS:
Das thema mit den interrupts beschaeftigt mich jetzt.
ich schau mal ob ich einen (teilweise compile-zeit) zustandsautomaten 
hinkrieg, der sich bedienen laesst wie eine sprungtabelle, aber alle 
pfade kennt und am schluss nicht call()en muss, sondern inlinen kann. 
sowas wuestes in die richtung hatte ich schonmal irgendwo, muss also 
gehn :D

: Bearbeitet durch User
von Reinhard M. (reinhardm)


Lesenswert?

Moin moin,

> make konzept ist out of scope bei mir.

Genauso sehe ich das auch. Habe auch keine Lust, meine Zeit mit make und 
Co zu vergeuden. Deshalb war ich total glücklich, als ich das Tuhl fand, 
mit dem man die Makedatei erzeugen konnte.
Wenn ich mich recht entsinne, war das Teil von Jörg Wunsch
Naja - ich habe das nach meinen Vorstellungen angepasst und seither wird 
es ohne groß Nachzudenken von Projekt zu Projekt kopiert.

> Hehe ok lustig. Ich komm aus genau der anderen Ecke, hab bisher meistens
> libs geschrieben und versuch immer zuerst einen minimalen aber
> vollstaendigen unterbau hinzukriegen.

Das funktioniert, solange man Einzelkämpfer ist. Wenn ein paar Dutzend 
Entwickler zusammen arbeiten müssen, dann ist es notwendig, zuerst die 
Schnittstellen fest zu klopfen ;)

>>> Meine ganzen "Klassen" zb. haben keinen zustand, und das
>>> ist echt was wert aus compiler sicht. Es gibt so nur dann branches und
>>> calls wenn es notwendig ist.
>>
>> Hm, den Vorteil gibt es aber nur auf den ersten, flüchtigen Blick. Die
>> Anwendung braucht Zustände und die müssen irgendwo hingepackt werden.
>> Ich halte es nicht für benutzerfreundlich, wenn man sich als Anwender
>> der Klassen noch Gedanken darüber machen muss, welche Zustände die
>> Objekte der Klassen brauchen, um funktionieren zu können.
> Ja ich glaub da haben wir wieder ne andere perspektive.
> Ich will einfach nix ueber die anwendung annehmen die der benutzer
> schreiben will, seine zustaende muss er sich schon selber ueberlegen.

Lach - das ist der Unterschied zwischen weißen und schwarzen Werkzeugen 
(neudeutsch: whitebox <> blackbox). Ich bin ein Freund von Blackbox :)
... und so verstehe ich auch die Datenkapselung in OO
Die meisten template-Bibliotheken, die ich bislang gesehen habe, sind 
dagegen whiteboxes, d.h. der Anwender kann erst dann eigenen Code 
schreiben, wenn er die ganze Bibliothek kennt und verstanden hat.
Das will ich als Anwender aber garnicht.
Mir reicht es, die öffentliche Schnittstelle zu lernen und anzuwenden.

Es ist ein großer Unterschied, ob man das Framework selbst entwickelt 
hat (und damit auswendig kennt), oder ob man als Frischling an ein 
Framework kommt und sich erst mit den Gedankengängen eines anderen 
auseinander setzen muss. Im letzteren Falle kommt man mit blackboxes 
schneller zum Ziel.
Wenn das Framework aber nicht ausreicht, die Anforderungen umzusetzen, 
dann ist es besser, wenn das Framework eine whitebox ist und man weiß, 
wo man Hand anlegen muss ...

> hehe, wieder unsere philosophien. wenn ich dreckige libs schreib krieg
> ich aerger und muss sie nochmal schreiben ;)

Schon klar. Ich denke, Du hast mich verstanden. Manchmal muss man 
Optimierungen vornehmen, die nicht schön aussehen (inline asm oder auch 
die ISR-Funktionen in C++). Die habe ich eben lieber in der Das thema 
mit den interrupts beschaeftigt mich jetzt.
ich schau mal ob ich einen (teilweise compile-zeit) zustandsautomaten
hinkriegBibliothek, als im Anwendungscode.

>>> Wenn du genau hinschaust wird dir auffallen dass ALLE dateien nichts
>>> ueber den konkreten mikrocontroller wissen.
>>
>> Ich habe es gesehen, frage mich aber: ist das für den Anwender eines
>> Frameworks wirklich wichtig?
>
>naja noe, solangs funktioniert. aber der anwender bin ja ich

Klar - mit der Einstellung ist es natürlich völlig wurscht, was und wie 
Du programmierst. Wenn Du allerdings ne Bibliothek schreibst, die auch 
andere anwenden können sollen, dann wird es schon wichtig, wieviel man 
lernen muss, um loslegen zu können und wie stark man sich mit der 
Bibliothek auseinander setzen muss.

> ich glaub ich schau mir mal die ganzen libs die hier hochgeladen wurden
> aus benutzersicht an und schau mir was ab, und mach meinen unterbau
> weiter im c++14/boost stil ;)

Hm, wenn wir unsere beiden Philosophien und Codeteile vereinigen 
könnten, hätte das bestimmt Potential :)

> Das thema mit den interrupts beschaeftigt mich jetzt.
> ich schau mal ob ich einen (teilweise compile-zeit) zustandsautomaten
> hinkrieg

Hm, ein Zustandsautomat lässt sich locker ohne Interrupts erstellen ;)

von Reinhard M. (reinhardm)


Lesenswert?

@Sheeva Plug

> Vor einiger Zeit habe ich mir mal Gedanken darüber gemacht, wie man
> Register und Pins etwas eleganter in C++ ausdrücken kann.

Gerade hatte ich einen Moment Zeit um mir Deine Beispiele anzuschauen 
...
Sehr beeindruckend!
Mir gefällt auch, dass Du den Preprozessor verwendest, um die Anzahl der 
Argumente zu reduzieren!

Was mir noch nicht ganz einleuchtet:
In der Registerklasse verwendest Du die gleichen Zeigeranweisungen wie 
ich, aber bei Dir wird kein Doppelregister mit dem Zeigerwert geladen.
Was habe ich übergesehen?

und wieso verbrauchen die Members der Pin-Klasse keinen Speicher?

von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

Ich schmeiß mich wech :D

jetzt habe ich eine Variante mit der Pin/Register-Variante von Sheeva 
Plug erstellt und übersetzt ...
Dateigröße ändert sich von 4550 bytes program + 189 bytes data
auf 4584 bytes program + 165 bytes data
also 10 bytes mehr :D

LOL - also ich würde sagen, Meister Buchegg hat eine Super Vorlage 
geliefert :)

Falls es jemand interessiert: ich habe die Module noch mit 
Include-Brokern versehen, sodass man Module über #defines (in Base.h) 
aktivieren, bzw. deaktivieren kann, ohne die Makedatei anpassen zu 
müssen.
Makedatei liegt im Verzeichnis "nix", also einfach dort reingehen und 
make aufrufen.

von neuer PIC Freund (Gast)


Lesenswert?

Cooler Test
1
[nix]$ make
2
make depend
3
make[1]: Verzeichnis „/tmp/AVR-Cpp_161021/nix“ wird betreten
4
if grep '^# DO NOT DELETE' Makefile >/dev/null; \
5
then \
6
        sed -e '/^# DO NOT DELETE/,$d' Makefile > \
7
                Makefile.$$ && \
8
        mv -f Makefile.$$ Makefile; \
9
fi
10
echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \
11
        >> Makefile; \
12
avr-g++ -MM -mmcu=atmega16 -DF_CPU=16000000UL  ../SystemTicker.C ../Runnable.C ../PushButton.C ../Encoder.C ../Buzzer.C ../LCD.C ../ADC.C ../UI.C ../main.C Makefile >> Makefile
13
avr-g++: warning: Makefile: linker input file unused because linking not done
14
make[1]: Verzeichnis „/tmp/AVR-Cpp_161021/nix“ wird verlassen
15
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../SystemTicker.C -o SystemTicker.o 
16
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../Runnable.C -o Runnable.o 
17
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../PushButton.C -o PushButton.o 
18
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../Encoder.C -o Encoder.o 
19
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../Buzzer.C -o Buzzer.o 
20
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../LCD.C -o LCD.o 
21
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../ADC.C -o ADC.o 
22
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../UI.C -o UI.o 
23
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL  -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../main.C -o main.o 
24
../main.C: In function 'int main()':
25
../main.C:200:3: internal compiler error: in subreg_get_info, at rtlanal.c:3673
26
   }
27
   ^
28
Please submit a full bug report,
29
with preprocessed source if appropriate.
30
See <http://gcc.gnu.org/bugs.html> for instructions.
31
make: *** [Makefile:211: main.o] Fehler 1
32
[nix]$ avr-g++ -v
33
Using built-in specs.
34
Reading specs from /usr/lib/gcc/avr/6.2.0/device-specs/specs-avr2
35
COLLECT_GCC=avr-g++
36
COLLECT_LTO_WRAPPER=/usr/lib/gcc/avr/6.2.0/lto-wrapper
37
Target: avr
38
Configured with: /build/avr-gcc/src/gcc-6-20160825/configure --disable-install-libiberty --disable-libssp --disable-libstdcxx-pch --disable-libunwind-exceptions --disable-linker-build-id --disable-nls --disable-werror --enable-__cxa_atexit --enable-checking=release --enable-clocale=gnu --enable-gnu-unique-object --enable-gold --enable-languages=c,c++ --enable-ld=default --enable-lto --enable-plugin --enable-shared --infodir=/usr/share/info --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --prefix=/usr --target=avr --with-as=/usr/bin/avr-as --with-gnu-as --with-gnu-ld --with-ld=/usr/bin/avr-ld --with-plugin-ld=ld.gold --with-system-zlib --with-isl --enable-gnu-indirect-function
39
Thread model: single
40
gcc version 6.2.0 (GCC)

von noob (Gast)


Lesenswert?

Whow gcc version 6.2.0 als avr-Variante?
Wo gibts die denn?

von Reinhard M. (reinhardm)


Lesenswert?

Ein Vergleich der Compiler-Ausgaben ergab:
1
avr-g++ (4.8.1)                           unknown  
2
Configured with: ../src/configure -v      Configured with: /build/avr-gcc/src/gcc-6-20160825/configure 
3
--enable-languages=c,c++                  --enable-languages=c,c++ 
4
--prefix=/usr/lib                         --prefix=/usr 
5
--infodir=/usr/share/info                 --infodir=/usr/share/info 
6
--mandir=/usr/share/man                   --mandir=/usr/share/man 
7
--bindir=/usr/bin 
8
--libexecdir=/usr/lib                     --libexecdir=/usr/lib 
9
--libdir=/usr/lib                         --libdir=/usr/lib 
10
--enable-shared                           --enable-shared 
11
--with-system-zlib                        --with-system-zlib 
12
--enable-nls                              --disable-nls 
13
--without-included-gettext 
14
--disable-libssp                          --disable-libssp 
15
--build=x86_64-linux-gnu 
16
--host=x86_64-linux-gnu 
17
--target=avr                              --target=avr 
18
                                          --disable-install-libiberty 
19
                                          --disable-libstdcxx-pch 
20
                                          --disable-libunwind-exceptions 
21
                                          --disable-linker-build-id 
22
                                          --disable-werror 
23
                                          --enable-__cxa_atexit 
24
                                          --enable-checking=release 
25
                                          --enable-clocale=gnu 
26
                                          --enable-gnu-unique-object 
27
                                          --enable-gold 
28
                                          --enable-ld=default 
29
                                          --enable-lto 
30
                                          --enable-plugin 
31
                                          --with-as=/usr/bin/avr-as 
32
                                          --with-gnu-as 
33
                                          --with-gnu-ld 
34
                                          --with-ld=/usr/bin/avr-ld 
35
                                          --with-plugin-ld=ld.gold 
36
                                          --with-isl 
37
                                          --enable-gnu-indirect-function

von Wilhelm M. (wimalopaan)


Lesenswert?

Auf jedem Linux-System ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Vielleicht habt Ihr ja mal Lust, die verschiedenen C++-Varianten Bib zu 
vergleichen. Jeder scheint ja so seine eigene kleine Variante zu haben. 
Dazu braucht es eine Anforderung, bspw.

1) ein LED alle 1s blinken lassen
2) 8 LED alle 1s blinken lassen
3) USART zum Host mit Echo des Zeichens
4) USART1 vom Host und USART2 zum Host mit Echo
...

Dann könnte man objektiv Code-Size und subjektiv Stil vergleichen ...

von Reinhard M. (reinhardm)


Lesenswert?

Moin Wilhelm,

hast Du Dir auch nen compiler selber geschnitzt?
Habe es gerade probiert, aber mein Linux scheint zu alt zu sein :O

> Jeder scheint ja so seine eigene kleine Variante zu haben.

Naja - es soll ja nicht nur übersetzen, sondern auch funktionieren. Das 
bedeutet aber, dass man auch die entsprechende Hardware haben muss. Weiß 
nicht, ob man sich da so adhoc auf einen gemeinsamen Nenner einigen 
kann.

von Carl D. (jcw2)


Lesenswert?

Reinhard M. schrieb:
> Moin Wilhelm,
>
> hast Du Dir auch nen compiler selber geschnitzt?
> Habe es gerade probiert, aber mein Linux scheint zu alt zu sein :O
>
>> Jeder scheint ja so seine eigene kleine Variante zu haben.
>
> Naja - es soll ja nicht nur übersetzen, sondern auch funktionieren. Das
> bedeutet aber, dass man auch die entsprechende Hardware haben muss. Weiß
> nicht, ob man sich da so adhoc auf einen gemeinsamen Nenner einigen
> kann.

GCC gibbet als Source und eine Schnitzanleitung hat google auch.
Ok, für Linux ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Als Unix-Mensch und jahrzehntelanger Linux-er verwende ich Arch-Linux. 
Da hat man alles immer taufrisch ... Eigentlich schade, dass es clang++ 
nicht mir nem AVR-Backend gibt.

Ein Testboard zum Aufstecken auf ein Breadboard mit nem 28-DIP oder 
40-DIP AVR hat doch bestimmt jeder rumliegen. Und ein paar LEDs und 
USB/Usart-Kabel werden wohl auch in irgendeiner Schublade liegen.

Schaun mer mal ...

von Reinhard M. (reinhardm)


Lesenswert?

Moin moin,

> GCC gibbet als Source und eine Schnitzanleitung hat google auch.

Ja, ok. Ich wusste nicht, dass der avr-gcc "nur" eine spezielle 
Übersetzung des gcc ist.
Bei debian sind die Versionsunterschiede zwischen native und avr so 
gigantisch, dass ich dachte, es sind unterschiedliche Saucen :O

Ok, inzwischen läuft der xte Übersetzungsversuch ...

Vermutlich müsste ich die AVR-libc auch neu bauen?

Mal schauen. Wochenende steht ja vor der Tür ;)

> Ein Testboard zum Aufstecken auf ein Breadboard mit nem 28-DIP oder
> 40-DIP AVR hat doch bestimmt jeder rumliegen. Und ein paar LEDs und
> USB/Usart-Kabel werden wohl auch in irgendeiner Schublade liegen.

Hm, was hättest Du noch gerne im Testprogram?
Meines hat ein LCD mit Software-SPI, 4 Taster, ADC und Buzzer ...
... dazu noch einen (kleinen) Zustandsautomaten, zwei Ausgabepins ...

Wenn Du was anderes testen möchtest, dann schreib doch mal konkret auf, 
was rein soll.

von Reinhard M. (reinhardm)


Lesenswert?

übersetzt und installiert ist er inzwischen.
Jetzt kommt jedoch gleich die Fehlermeldung:
1
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL -I/usr/lib/avr/include -Os -Wall -Wextra -Wwrite-strings -std=c++11 -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums -ffunction-sections ../SystemTicker.C -o SystemTicker.o 
2
as: Unbekannte Option »-mmcu=avr5«

Hm, ist der assembler separat? Muss ich den auch noch übersetzen?

von Wilhelm M. (wimalopaan)


Lesenswert?

Kann sein, dass die bin-utils (assembler / linker) nicht auf dem 
neuesten Stand sind und die Option nicht verstehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Reinhard M. schrieb:
>
1
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL 
2
> -I/usr/lib/avr/include -Os -Wall -Wextra -Wwrite-strings -std=c++11 
3
> -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums 
4
> -ffunction-sections ../SystemTicker.C -o SystemTicker.o
5
> as: Unbekannte Option »-mmcu=avr5«
>
> Hm, ist der assembler separat?

Ja.  Gehört zu den Binutils.  Das mindeste was brauchst is Binutils + 
GCC + AVR-LibC.

> -I/usr/lib/avr/include

Lass den Köse weg. Entweger es funktioniert ohne, oder deine Toolchain 
ist kaputt / falsch konfiguriert / unvollständig...

> as

Ist das der Host as?  gas für x86 oder ne andere Host-Plattform wirk 
kaum -mmcu=avr5 kennen ;-)

Der verwendete as sollte dem entsprechenden config.log zu entnehmen 
sein.

: Bearbeitet durch User
von David U. (dsu)


Lesenswert?

Nabend.

So ich hatte ja das Thema mit der fehlenden api ;)

hab jetzt mal einen ansatz angefangen und finds schoener.
das beispiel weiter oben mit dem knopf und der led die auf dem 
invertierten pegel laeuft:
1
#include "mc.hpp"                                 
2
                                                  
3
int main() {                                      
4
    using namespace mc;                           
5
                                                  
6
    auto led   = pin<mcu::pin::pc3>::init(output);
7
    auto check = pin<mcu::pin::pd2>::init(input); 
8
                                                  
9
    while(true) {                                 
10
        led = !check;                             
11
    }                                             
12
}

overhead: 0 byte, es faellt der gleiche assembler wie bei plain c raus.
(pin belegung ist anders, aber die ist auf meinem testboard halt so und 
ich steck das jetzt nicht um.)

haette auch mal spass dran ein paar gleiche faelle zu vergleichen und 
verschiedene ansaetze daran zu sehen :)
(Wie Wilhelm M. schreibt)

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Reinhard M. schrieb:
> übersetzt und installiert ist er inzwischen.
> Jetzt kommt jedoch gleich die Fehlermeldung:
>
1
avr-g++ -c -mmcu=atmega16 -I. -gstabs -DF_CPU=16000000UL 
2
> -I/usr/lib/avr/include -Os -Wall -Wextra -Wwrite-strings -std=c++11 
3
> -save-temps  -funsigned-char -funsigned-bitfields -fshort-enums 
4
> -ffunction-sections ../SystemTicker.C -o SystemTicker.o
5
> as: Unbekannte Option »-mmcu=avr5«
6
>
>
> Hm, ist der assembler separat? Muss ich den auch noch übersetzen?

Ich mach das immer danach:
http://www.nongnu.org/avr-libc/user-manual/install_tools.html
erst BinUtils, dann GCC, dann AvrLibc.
Danach spiele ich etwas mit Symlinks, da ich mehrere Avr-Gcc Versionen 
parallel installiert hab. Eine setze ich dann als Default.

Wobei ich das meist auf Arbeit mache. Da muß ich zwar Windows, aber 
nebenher läuft immer eine virtuelle Kiste mit Linux. Da ist das Warten 
nicht so öd.

von Reinhard M. (reinhardm)


Lesenswert?

Johann L. schrieb:
> Ja.  Gehört zu den Binutils.  Das mindeste was brauchst is Binutils +
> GCC + AVR-LibC.

Danke für den Hinweis. Wär ja auch zu einfach gewesen :O

David Uebler schrieb:
> hab jetzt mal einen ansatz angefangen und finds schoener.
> das beispiel weiter oben mit dem knopf und der led die auf dem
> invertierten pegel laeuft

Das sieht schon besser aus. Allerdings solltest Du für einen Taster auch 
eine Entprellung machen. Also Interrupt und main-Schleife.

Ich habe bei meinen Beispielen gesehen, dass so ein 2-Zeilen Beispiel 
ein völlig anderes Bild suggeriert, als nachher beim kompletten 
Testprogramm rauskommt.

Deshalb habe ich eben eine etwas komplexere Beispielanwendung 
beigepackt.

> (pin belegung ist anders, aber die ist auf meinem testboard halt so und
> ich steck das jetzt nicht um.)

Na, die sollte doch jeder an seine HW anpassen können, ohne dass sich 
großartig was an der Firmwaregröße ändert. Ist also kein Aufheben wert 
;)

Carl Drexler schrieb:
> Ich mach das immer danach:
> http://www.nongnu.org/avr-libc/user-manual/install...
> erst BinUtils, dann GCC, dann AvrLibc.

Danke für die Tips und Schande über mein Haupt. Das Handbuch ist das 
meistgenutzte bei mir, aber die Seite habe ich noch nie gesehen :(
Also denne: in die Hände spucken und los geht's ;)

von Reinhard M. (reinhardm)


Lesenswert?

neuer Pic-Freund schrieb:
> Cooler Test

Yepp - jetzt hatter mich auch :(
Habe probiert, Support für long-long wegzulassen, genauso wie die 
goldene Option ...
... macht keinen Unterschied. Es kracht immer :(

von Wilhelm M. (wimalopaan)


Lesenswert?

Kleiner Tipp grundsätzlich zum Entwickeln: mach die ne VM (bspw. mit 
VirtualBox) mit ArchLinux. Das ist eine rolling distribution und immer 
sehr aktuell, und auch v.a. stabil. Etwas Linux-KnowHow ist aber 
notwendig... Doch gibt es ganz hervorragende Anleitungen für Anfänger.

von Reinhard M. (reinhardm)


Lesenswert?

Wilhelm M. schrieb:
> Kleiner Tipp grundsätzlich zum Entwickeln

??? - das verstehe ich jetzt nicht. Meinst Du zum Compilerbau, oder zur 
µC-Entwicklung?

fremde Quelltext-Pakete baue ich grundsätzlich in /usr/local
Das ist bei mir eine eigene Platte, die jederzeit schnell entfernt und 
platt gemacht werden kann :)

für die µC-Entwicklung habe ich auch eigene Umgebungen (anderer Benutzer 
;) )

virtualbox verwende ich für Teile, mit denen ich mein System nicht 
verunreinigen will (die avr-Geschichten zählen für mich nicht dazu).

Naja - aber wenn der Compiler crasht, dann ist mir auch klar, warum 
avr-gcc dem gcc bei debian versionsmäßig hinterher hinkt.

> mit ArchLinux. Das ist eine rolling distribution und immer sehr aktuell

Mein Linux ist debian. Mir ist Sicherheit und Stabilität wichtiger, als 
akuellste Pakete zu haben. Habe zwischendurch immer mal wieder andere 
Distris ausprobiert, bislang fand ich keine, die mit debian mithalten 
oder sie gar ersetzen könnte. Das Thema ist für mich durch :)

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich dachte schon, Du wolltest den avr-gcc unter M$ erstellen, was aber 
mit cygwin auch geht. Es gibt ja Leute, die mit M$ Software entwickeln 
;-)

Ah Debian! Davon bin ich schon lange weg, weil ich die vielen Vorteile 
(auch absolute Stabilität und die sehr große Community) von ArchLinux 
sehr genieße!

Nun, dann musst Du halt das richtige

./configure && make && make install

machen ;-)

von Reinhard M. (reinhardm)


Lesenswert?

> Ich dachte schon, Du wolltest den avr-gcc unter M$ erstellen

Lach - M$ gibt es bei mir nur in einer VM ...
z.B. für Atmel Studio
Das starte ich aber nur, wenn es auf die exakten Prozessorschritte 
ankommt ;)

Übrigens:
wenn man gcc aus svn-trunk übersetzt, dann löppt alles wie gewünscht.
Die Ausgabe von avr-size der neuen binutils gefällt mir allerdings 
überhaupt nicht. Da musste ich doch glatt das alte avr-size wieder 
reanimieren ;)

Bin also mit dem neuen Üersetzer wieder arbeitsfähig. Ging doch 
schneller, als erwartet.

von Sheeva P. (sheevaplug)


Lesenswert?

Reinhard M. schrieb:
> @Sheeva Plug
> Sehr beeindruckend!
> Mir gefällt auch, dass Du den Preprozessor verwendest, um die Anzahl der
> Argumente zu reduzieren!

Danke. ;-)

> Was mir noch nicht ganz einleuchtet:
> In der Registerklasse verwendest Du die gleichen Zeigeranweisungen wie
> ich, aber bei Dir wird kein Doppelregister mit dem Zeigerwert geladen.
> Was habe ich übergesehen?

Mir ist nicht ganz klar, was Du mit "Doppelregister" meinst?

> und wieso verbrauchen die Members der Pin-Klasse keinen Speicher?

Bei mir optimiert der Compiler (g++ 5.4.0, zuvor 4.8.1) das weg.

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


Lesenswert?

Johann L. schrieb:
> Ist das der Host as?

Würde ich auch vermuten.

Der Dreh ist halt, dass alle Komponenten der Toolchain mit dem
gleichen --prefix gebaut sein müssen.  Wenn man jetzt versucht,
die avr-binutils aus der Distribution zu nehmen, dann geht das
prinzipiell, aber man müsste deren --prefix auch für Compiler und
avr-libc benutzen.

Sinnvoller ist es daher, immer alle drei Komponenten neu zu bauen.
Wenn man kein --prefix angibt, landen die („Endkunden“-)Binaries unter
/usr/local/bin.

von Sheeva P. (sheevaplug)


Lesenswert?

Reinhard M. schrieb:
> Ich schmeiß mich wech :D

Ach, wirklich? }:D

> jetzt habe ich eine Variante mit der Pin/Register-Variante von Sheeva
> Plug erstellt und übersetzt ...

Bitte verzeih', aber in Deinem Zipfile kann ich meinen Code gar nicht 
finden. Stattdessen sehe ich eine Pin-Klasse, die nur eine entfernte 
Ähnlichkeit mit der meinen hat. Mein Reg-Template und die 
Register{8,16}-Klassen hast Du vorsichtshalber gleich ganz weggelassen, 
dabei könnte man sie in Deinem Projekt ganz wunderbar zur Abstraktion 
der anderen Register (TCCRx, OCRx, ADCSRx, ... you name it) benutzen. 
Aber naja -- zurück zu meinem Code, bzw. zu dessen Unterschieden zu 
Deinem.

Meine main()-Funktion aus "ex0" wird vom Compiler letztlich zu folgendem 
gemacht:
1
main:
2
  34:  cbi   0x17, 0  ; InputPin btn(&DDRB,&PORTB,&PINB, 0)
3
  36:  sbi   0x17, 1  ; OuputPin led(&DDRB,&PORTB,&PINB, 1)
4
  38:  sbis  0x16, 0  ; if btn.isHigh()
5
  3a:  rjmp  .+4      ; (jump to 0x40)
6
  3c:  cbi   0x18, 1  ; led.setLow()
7
  3e:  rjmp  .-8      ; (loop (jump to 0x38))
8
  40:  sbi   0x18, 1  ; led.setHigh()
9
  42:  rjmp  .-12     ; (loop (jump to 0x38))

Du siehst: die set{Low,High}()- und die isHigh()-Methoden werden am Ende 
zu einem einzelnen Maschinenbefehl optimiert. Damit wird klar: Deine 
"cli()"- und "sei()"-Aufrufe in den Methoden sind komplett überflüssig, 
denn eine einzelne Instruktion kann ohnehin nicht unterbrochen werden.
Ebenso überflüssig ist Dein "inline"n meiner Methoden, denn für einzelne 
Instruktionen spendiert der Compiler sicher keinen Funktionsaufruf.

Bei alten AVRs könnte das sei/cbi bei toggle() ansonsten vielleicht noch 
sinnvoll sein, da sind das mehrere Instruktionen. Aber bei den modernen 
AVRs kann man einen Pin toggle()n, indem man einfach eine "1" auf das 
Pin-Register schreibt, was wieder nur eine Instruktion ist. Das wird bei 
mir daher über CAN_TOGGLE_PIN gesteuert.

Andererseits gefällt mir Dein Handling von "PortDesc Port[A-D]" so gut, 
daß ich überlege, es zu übernehmen. Das macht die Sache dann noch etwas 
besser lesbar als mein PINDEF()-Makro.

> Dateigröße ändert sich von 4550 bytes program + 189 bytes data
> auf 4584 bytes program + 165 bytes data
> also 10 bytes mehr :D

Na, das ist eine Differenz von gerade einmal 2,1 Promille. Dafür, daß Du 
meinen Code, nunja, leider "kaputtgemacht" hast, ist das noch ziemlich 
überschaubar, finde ich. Probier' mal meinen Originalcode, da sieht die 
Sache sicher anders aus.

Das tut sie bei mir aber ohnehin: der Code aus Deinem Zipfile kompiliert 
bei mir zu 4500 bytes program + 189 bytes data => 4689 bytes, also 50 
Bytes weniger (avr-g++ 4.9.1 unter Ubuntu 16.04.1 LTS). Wenn ich dazu 
-flto einschalte, komme ich auf nurmehr 4144 bytes program + 189 bytes 
data => 4333 Bytes gesamt: 406 Bytes weniger!

Und wenn ich Deine kontraproduktiven "Optimierungen" herausnehme und das 
Ganze neu übersetze, komme ich auf: 4128 bytes program + 189 bytes data 
=> 4317 bytes insgesamt, also 8,9 Prozent (Prozent, nicht Promille) 
weniger als bei Deinem Elaborat. Das ist allerdings deutlich, finde ich.

> LOL - also ich würde sagen, Meister Buchegg hat eine Super Vorlage
> geliefert :)

Das hat ja niemand bestritten, aber erst macht Du meinen Code kaputt, 
und dann machst Du Dich über das Ergebnis lustig. Ist doch prima: so 
haben wir am Ende beide was zu Lachen gehabt. ;-)

von Reinhard M. (reinhardm)


Lesenswert?

Sheeva Plug schrieb:
> Mir ist nicht ganz klar, was Du mit "Doppelregister" meinst?

Na, vor der LD-Anweisung werden doch zwei Register mit der Adresse der 
Speicherstelle geladen, also 3 Anweisungen für eine Zeigerzuweisung.

>> und wieso verbrauchen die Members der Pin-Klasse keinen Speicher?
>
> Bei mir optimiert der Compiler (g++ 5.4.0, zuvor 4.8.1) das weg.

Hm, könntest Du bitte mal Deine Compiler-Optionen veröffentlichen?
Bei mir wird zwischen 4.8 und 7.0 nichts anders optimiert :(

Jörg Wunsch schrieb:
> Sinnvoller ist es daher, immer alle drei Komponenten neu zu bauen.
> Wenn man kein --prefix angibt, landen die („Endkunden“-)Binaries unter
> /usr/local/bin.

Genauso habe ich es dann auch gemacht. Musste mich nur erst auch an die 
richtige Reihenfolge gewöhnen. Beim gcc 6.2 kommt der oben zitierte 
Fehler, bei der Version aus trunk läuft alles gut.

Allerdings sehe ich keine großen Veränderungen im Output. Wenn ich meine 
Quellen mit dem neuen compiler übersetze, kommt aufs Byte die gleiche 
Firmware heraus.

Sheeva Plug schrieb:
>> jetzt habe ich eine Variante mit der Pin/Register-Variante von Sheeva
>> Plug erstellt und übersetzt ...
>
> Bitte verzeih', aber in Deinem Zipfile kann ich meinen Code gar nicht
> finden.

Das ist korrekt, da ich den ja garnicht hochgeladen habe.
Ich habe inzwischen auch das Buch aus dem Tip überflogen. Der Autor 
scheint realistisch zu sein. Er gehört nicht zu den template-Fanboys, 
sondern schreibt, dass templates die Codegröße positiv beeinflussen 
KANN aber auch zu einer Codegrößen-Explosion führen kann. Mann müsste 
also jeweils eine template-Variante mit einer Non-Template-Variante 
vergleichen. Also im Prinzip das, was ich auch gemacht habe.

Wenn man völlig state-lose Bibliotheken baut, dann kann man noch 
Beispiele erzeugen, bei denen die Codegröße überschaubar bleibt.

Spätestens dann, wenn man aber unterschiedliche Zustände braucht 
und/oder nutzen will, bezahlt man für den Code. Meiner Ansicht nach 
zeigt sich erst dann, was eine Bibliothek wert ist.

> Andererseits gefällt mir Dein Handling von "PortDesc Port[A-D]" so gut,

Das ist nicht "mein" Handling. Das habe ich mir von Meister Buchegg 
abgeschaut und nur eine Referenz aus der Instanzvariablen gemacht.

> aber erst macht Du meinen Code kaputt, und dann machst Du Dich über das
> Ergebnis lustig.

Moment! Da hast Du mein Lachen falsch interpretiert!
Ich habe mich weder über Dich, noch über Deine Bibliothek lustig 
gemacht, sondern über die 10 byte Unterschied.

Wenn ich zwei Varanten übersetzt habe, setz ich mich hin und mach eine 
Kosten-/Nutzen-Rechnung. Und spätestens dann sind alle template-Ansätze, 
die ich bislang fand, vom Tisch. Auch die Beispielanwendung aus dem Buch 
ist viel zu kompliziert für den (Hobby-)Einsatz. Im Buch wird auch nicht 
alles erklärt, was dann in der Beispiel-Anwendung umgesetzt wird. Bleibt 
also vieles in der Grauzone.
Wer ohne das Buch die Bibliothek für eigene Projekte verwenden will, hat 
einen sehr steinigen Weg vor sich. Eine gute Bibliothek sieht für mich 
anders aus.
Somit sind wir wieder am Anfang, d.h. es muss jeder selbst für sich 
heraus finden, was wie teuer ist und was ihm jetzt was wert ist.

von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

hier eine neue Version meiner Klassenbibliothek.
Ich habe die Klassen etwas umgebaut, um sie flexibler verwenden zu 
können.
Dann habe ich auch Unterstützung für weitere Prozessoren zugefügt.

Derzeit übersetzen die Klassen für - mega88, mega16, mega168, mega328
                                   , mega640, mega1280, mega1281
                                   , mega2560 and mega2561

wobei es ab mega1280 aktuell noch Linker-Probleme gibt. Zumindest mit 
gcc-7.0 aus dem trunk.

von Reinhard M. (reinhardm)


Angehängte Dateien:

Lesenswert?

OK, das Linkerproblem scheint behoben zu sein.

Deshalb hier eine neue Version mit korrigiertem Makefile.
Damit werden jetzt alle unterstützten Professoren übersetzt ;)

von Sheeva P. (sheevaplug)


Lesenswert?

Reinhard M. schrieb:
> Sheeva Plug schrieb:
>> Bei mir optimiert der Compiler (g++ 5.4.0, zuvor 4.8.1) das weg.
>
> Hm, könntest Du bitte mal Deine Compiler-Optionen veröffentlichen?

-Os -std=c++11 -Wall -Wexec -Wpedantic

> Ich habe inzwischen auch das Buch aus dem Tip überflogen. Der Autor
> scheint realistisch zu sein. Er gehört nicht zu den template-Fanboys,
> sondern schreibt, dass templates die Codegröße positiv beeinflussen
> KANN aber auch zu einer Codegrößen-Explosion führen kann. Mann müsste
> also jeweils eine template-Variante mit einer Non-Template-Variante
> vergleichen. Also im Prinzip das, was ich auch gemacht habe.

Naja, wenn man weiß, wie Templates funktionieren, ist das gar nicht so 
magisch. Tatsächlich verwendet mein Code Templates, um die Register zu 
abstrahieren. Das erzeugt jeweils eine Register8-Klasse für 8-Bit- und 
eine Register16-Klasse für 16-Bit-Register -- wenn sie gebraucht werden, 
und eben nur dann. Ich halte im Übrigen auch nichts davon, Pinnummern, 
Registeradressen etc. als Template-Parameter zu übergeben -- Templates 
sind nicht als alternativer Mechanismus zur Parameterübergabe gedacht.

> Wenn man völlig state-lose Bibliotheken baut, dann kann man noch
> Beispiele erzeugen, bei denen die Codegröße überschaubar bleibt.

Ja, natürlich. Und wenn ich ein Register abstrahieren will, warum sollte 
ich das nicht stateless machen? Der Status steht ja ohnehin im Register.

> Spätestens dann, wenn man aber unterschiedliche Zustände braucht
> und/oder nutzen will, bezahlt man für den Code. Meiner Ansicht nach
> zeigt sich erst dann, was eine Bibliothek wert ist.

> Moment! Da hast Du mein Lachen falsch interpretiert!

Ah, ok. ;-)

> Somit sind wir wieder am Anfang, d.h. es muss jeder selbst für sich
> heraus finden, was wie teuer ist und was ihm jetzt was wert ist.

Das ist ja immer so.

von Wilhelm M. (wimalopaan)


Lesenswert?

Sheeva P. schrieb:
> Reinhard M. schrieb:
>> Sheeva Plug schrieb:
>>> Bei mir optimiert der Compiler (g++ 5.4.0, zuvor 4.8.1) das weg.
>>
>> Hm, könntest Du bitte mal Deine Compiler-Optionen veröffentlichen?
>
> -Os -std=c++11 -Wall -Wexec -Wpedantic

da würde ich gleich mal auf c++1z gehen, um die neuesten 
Errungenschaften auch benutzen zu können.

> Naja, wenn man weiß, wie Templates funktionieren, ist das gar nicht so
> magisch. Tatsächlich verwendet mein Code Templates, um die Register zu
> abstrahieren. Das erzeugt jeweils eine Register8-Klasse für 8-Bit- und
> eine Register16-Klasse für 16-Bit-Register -- wenn sie gebraucht werden,
> und eben nur dann. Ich halte im Übrigen auch nichts davon, Pinnummern,
> Registeradressen etc. als Template-Parameter zu übergeben -- Templates
> sind nicht als alternativer Mechanismus zur Parameterübergabe gedacht.

Die Entscheidung liegt ja darin, ob man Compiletime-Polymorphie benutzen 
kann (oder etwas zur Laufzeit entschieden werden muss). Das kann 
natürlich zu einer Vergrößerung des MaschinenCode-Umfangs führen. Was 
ich aber meistens als unkritisch empfinde, da oft nicht das flash der 
limitierende Faktor ist, sondern das RAM (zumindest mal auf den 
kleineren AVRs).

Wichtig ist auch, ob man Funktionen als constexpr gestalten kann. 
constexpr ist auch eine wunderbare Neuerung (aber da wiederhole ich 
mich) wie auch variadische templates, um z.b. zur Compilezeit 
Iterationen auszuführen.

: Bearbeitet durch User
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.