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.
Gast
#4760525
>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.
> 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.
Gast
#4760621
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.
Gast
#4760638
> 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
Gast
#4760641
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.
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?
Gast
#4760714
Man bekommt nichts geschenkt. Jeder Vorteil kostet auch was. Welchen Controller willst du denn einsetzten?
>> 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.
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
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.
> 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
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
> 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
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
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
> 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.
Deine source files sind .c nicht .cpp Was verwendest du für einen Compiler, dass der nicht meckert? Hat es einen besonderen Grund warum .c?
Gast
#4761143
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
#4761164
> ich hab keine Ahnung, ob der GCC auf AVR exceptions unterstützt "Exceptions are not supported" http://www.nongnu.org/avr-libc/user-manual/FAQ.html
Gast
#4761198
> 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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
-> 110 Bytes In C mit inline:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
-> 62 Bytes In C++:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
-> 146 Bytes, also ca 30% Overhead. Und jetzt in C++ mit inline:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
-> 62 Bytes, Überraschung!!! 0% Overhead
@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
#4761295
Bitte korrigiere in C und C inline die Definition in gehe_aus() und compiliere neu.
Gast
#4761327
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
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
> 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.
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"
Okay, ihr habt mich vollkommen überzeugt von C++. Meine Vorurteile muss ich über Board werfen. Nach 4 Stunden probieren und testen kann ich sagen, dass man absolut verlustfrei in C++ coden kann für AVR µC, egal welche Sorte. Vielen Dank für die Inspiration :-)
Gast
#4761568
Thomas W. schrieb: > dass man absolut verlustfrei in C++ coden kann für AVR µC Mit einer Teilmenge von C++, um etwas genauer zu sein.
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
@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
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.
So, ich habe noch einen Vergleich gemacht und zwar mit reinterpret_cast Hier zuerst die alte Klasse:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
und hier jetzt die neue:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
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"
Gast
#4761819
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
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
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?
Gast
#4761953
@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;
Ja, die (Deine?) Bibliothek kenne ich. Die finde ich auch im Ansatz ganz gut! Was ich meinte war etwas wie die avr-libc, die als größeres OSS-Projekt (dauerhaft) Bestand hat.
Gast
#4761989
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.
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.
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.
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
C++ hat lesbare Pin-Klassen, die auch weiter verwendet werden können und braucht genau 8 byte mehr.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
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 :)
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.
Oh cool, die neue pin.h, einfach simpel und gut verwendbar! Gefällt mir. Das "PortA::Pin1 led;" ist auch ein cooler Ansatz.
@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
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.
Bei mir sieht sowas so aus:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
VG
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.
Thomas W. schrieb: > "PortA::Pin1 led;" > > ist auch ein cooler Ansatz. Nur sollte man sich an die Bezeichnungen im Datenblatt halten. Wenn dann PA1 und nicht Pin1. Und hier sieht man schon, dass OO-Abstraktion auf diesem Level absoluter Humbug ist. OOP auf Ebene der Applikation bringt einen echten Mehrwehrt aber doch nicht die Abstraktion über das setzen eines Pins.
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.
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?
Das schreibt man einfach in die (einzige) Implmentierungsdatei:
1 | |
2 | |
3 | |
4 | |
Alles andere sind ja Header. (Hatte mich oben vertan: die HW-Timer liefert den ms-Tick an den SW-Timer. Läuft ein SW-Timer ab, so stellt er einen Event in die EventQueue ...)
Wilhelm M. schrieb: > Das schreibt man einfach in die (einzige) Implmentierungsdatei: > >
1 | |
2 | |
3 | |
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?
Ich denke, solange man avr-libc verwendet, wird man es so machen müssen. Denn der Start-Code crt1 überschreibt den InterruptVektor ...
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.
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?
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.
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.
Ich werfe hier einfach mal eine Buchempfehlung rein: https://www.amazon.de/Real-Time-Efficient-Object-Oriented-Microcontroller-Programming/dp/3642346871 Das hat mir den Einstieg mit C++ auf µCs vereinfacht.
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 ...
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 | |
übersetzen lassen.
Gast
#4762718
Wilhelm M. schrieb: > Darüber habe ich nichts gesagt. Und ja, der Interruptvector steht im > Flash ab 0x0000. Nein, nicht notwendigerweise. Die Interruptvectortabelle kann bei einem ATMega üblicherweise an 5 verschiedenen Stellen im Flash liegen...
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 | |
2 | |
> ü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.
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
Für die Abstraktion von HW-REssourcen macht man keine Objekte, sondern nicht instanziierbare class-templates.
Gast
#4762883
Sieht interessant aus. Wie wendet man sowas nun an?
Gast
#4762886
Gerade das template mit dem DIR:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
Bspw. wenn an Port D, Pin 7 eine LED (Ausgang) ist, schreibt man folgendes:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
Und das ist zero-overhead gegenüber reinem C-Code.
Gast
#4762893
Und wie ist Output definiert?
Folgendermaßen:
1 | |
2 | |
3 | |
4 | |
5 | |
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!
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.
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 ;)
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
@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.
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.
Di P. schrieb: > Ich werfe hier einfach mal eine Buchempfehlung rein: > > https://www.amazon.de/Real-Time-Efficient-Object-Oriented-Microcontroller-Programming/dp/3642346871 > > Das hat mir den Einstieg mit C++ auf µCs vereinfacht. Hab mir das Buch jetzt gekauft, das Probe Exemplar hat mich überzeugt. Danke für den Tip!
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
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 ;)
@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?
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.
Gast
#4763563
Cooler Test
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
Gast
#4763604
Whow gcc version 6.2.0 als avr-Variante? Wo gibts die denn?
Ein Vergleich der Compiler-Ausgaben ergab:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
Auf jedem Linux-System ...
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 ...
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.
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 ;-)
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 ...
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.
übersetzt und installiert ist er inzwischen. Jetzt kommt jedoch gleich die Fehlermeldung:
1 | |
2 | |
Hm, ist der assembler separat? Muss ich den auch noch übersetzen?
Kann sein, dass die bin-utils (assembler / linker) nicht auf dem neuesten Stand sind und die Option nicht verstehen.
Reinhard M. schrieb: >
1 | |
2 | |
3 | |
4 | |
5 | |
> > 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.
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
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)
Reinhard M. schrieb: > übersetzt und installiert ist er inzwischen. > Jetzt kommt jedoch gleich die Fehlermeldung: >
1 | |
2 | |
3 | |
4 | |
5 | |
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.
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 ;)
neuer Pic-Freund schrieb:
> Cooler Test
Yepp - jetzt hatter mich auch :(
Habe probiert, Support für long-long wegzulassen, genauso wie die
goldene Option ...
... macht keinen Unterschied. Es kracht immer :(
Kleiner Tipp grundsätzlich zum Entwickeln: mach die ne VM (bspw. mit VirtualBox) mit ArchLinux. Das ist eine rolling distribution und immer sehr aktuell, und auch v.a. stabil. Etwas Linux-KnowHow ist aber notwendig... Doch gibt es ganz hervorragende Anleitungen für Anfänger.
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 :)
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 ;-)
> 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.
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.
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.
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
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. ;-)
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.
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.
OK, das Linkerproblem scheint behoben zu sein. Deshalb hier eine neue Version mit korrigiertem Makefile. Damit werden jetzt alle unterstützten Professoren übersetzt ;)
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.
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.
