AVR und C++ - ein Versuch

OP #4760460
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.
Angehängte Dateien:
Gast #4760525
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.
OP #4760544
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.
Angehängte Dateien:
Gast #4760621
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.
#4760669
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?
OP #4760817
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.
#4760845
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.
Gast #4760883
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.
OP #4760931
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.
Gast #4760980
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.
Gast #4760987
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.
Gast #4761054
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.
Gast #4761091
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
OP #4761106
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.
Angehängte Dateien:
Gast #4761143
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.
Gast #4761198
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
OP #4761282
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
Gast #4761327
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.
Gast #4761397
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.
#4761465
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"
Angehängte Dateien:
(Firma: Torrox.de) #4761577
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
OP #4761717
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
Gast #4761724
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.
OP #4761807
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"
#4761843
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
Gast #4761953
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;
Angehängte Dateien:
Gast #4761989
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.
#4762021
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.
#4762229
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.
OP #4762259
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 :)
Angehängte Dateien:
#4762265
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.
#4762323
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.
Gast #4762342
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.
#4762364
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
#4762372
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.
#4762381
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.
#4762390
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?
#4762410
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?
#4762436
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?
#4762459
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.
#4762466
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.
#4762501
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 ...
#4762619
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.
Angehängte Dateien:
#4762841
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.
#4762846
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.
#4762897
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!
#4762912
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.
OP #4762980
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 ;)
#4763026
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
  };
OP #4763039
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.
#4763062
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.
#4763093
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
OP #4763291
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 ;)
OP #4763395
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?
OP #4763537
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.
Angehängte Dateien:
Gast #4763563
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)
OP #4763624
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
#4763630
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 ...
OP #4763678
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.
#4763680
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 ;-)
#4763684
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 ...
OP #4763759
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.
OP #4763769
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?
Persönliche Seite #4763842
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.
#4764088
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)
#4764171
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.
OP #4764225
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 ;)
OP #4764271
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 :)
#4764275
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 ;-)
OP #4764403
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.
#4769108
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.
Moderator Persönliche Seite #4769135
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.
#4770014
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. ;-)
OP #4770343
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.
OP #4771730
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.
Angehängte Dateien:
#4784996
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.
#4785026
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren