Nachdem ich bei der Frage nach " 1<<PA3 " gründlich abgewatscht wurde, dabei aber auch reichlich gelernt habe, trau ich mich noch ein Problem anzusprechen.
Ich programmiere an einem Modellbauprojekt auf dem ArduinoMega256 in C++ unter Code::Blocks.
Es gibt eine main-Routine, an der ein paar ISR dranhängen, die z.b. Ereignisse zählen (Inkremente usw.)
In der main werden ein paar Klassen instanziert, z.b. die Klasse "segel", die Objekte sind dann "fock", "gross", "besan" usw..
Die Methoden darin heißen dann "bergen", "setzen" und sowas.
Ich würde gerne die genannten Objekte (z.b. fock) nicht in der main, sondern in einem anderen Objekt (z.b. "mannschaft" oder so) instanzieren, um die main übersichtlicher zu machen. (Anschaulich : die main ist der Skipper, die Mannschaft sind die Matrosen, und erst die machen die Arbeit..)
Mein Problem :
Wenn ich ein Objekt nicht in der main, sondern in einem anderen Objekt erzeuge, kann das nicht auf die ISR-Werte aus der main zugreifen (oder andersrum).
Ich hoffe, mich einigermaßen verständlich ausgedrückt zu haben.
Schwierig, ist viel Code...
Hier die letzten Zeilen der main, und der Beginn der ersten ISR.
Die ISR sind in der Datei main integriert, hängen unten dran (nicht im Programmkörper von main).
In der mit "#####" gekennzeichneten Zeile wird ein Attribut der Klasse "segel", instanziert im Objekt "Gross", beschrieben. Das geht so nicht, wenn die Instanzierung von Gross nicht in der main, sondern einem anderen Objekt, geschieht.
Was tun ?
1
}//ende der 10hz-task in der "while 1" -main-schleife
2
};//ende "while 1" - schleife innerhalb main
3
return 0;
}//ende von main ********************************************************
//Hardwarebelegung :
//Timer 0 : PPM-Generator (für Servo und RC-Winden)
//Timer 1 : 10HZ-Generator für NPRM-Scheduler
//Timer 2 : PWM-Generator (A für Maschine )
//Timer 3 : Zähler für PPM-Decoder (vom RC-Sender)
//Timer 4 : PPM für Winden in Gruppen
//Timer 5 : Entprellung der Inkrementgeber
//UART0 : USB zum PC
//UART1 : GPS
ISR (PCINT0_vect) //Inkremente aus Gruppe 0
{
if (int_done0 == 0) //falls das ein neuer interrupt ist
{
TCNT5 = 0; //Timer 5 auf Null
int_done0 = 1; //merk dir, jetzt war ein signal
}
else
{
if (TCNT5 > 500) // letztes Signal länger als 32ms her ?
{
Gross.KlauInkrement ++ ; //############################
Besan.KlauInkrement ++ ;
int_done0 = 0;
}
}
Normalerweise referenziere (instanziere) ich Objekte in der main.
Deren Methoden kann ich dann aufrufen, die Attribute zugreifen.
Von der Klasse "segel" gibts z.b. zwei Objekte in der main : Gross und Fock.
Ich kann jetzt in der main "Gross.setzen()" aufrufen, und "Fock.setzen()" .
Als Wert dafür brauche ich in "setzen" einen Inkrementzähler , der in einer ISR an der main geschrieben wird. den kann ich z.b. mit "Gross.Klauinkrement"
in der ISR schreiben und in "Gross.setzen()" lesen.
Alles prima.
Jetzt möchte ich eine weitere Hierarchieebene einziehen, und die Segelbedienung nicht in der main machen. Ich schreibe ein Objekt "Deckshand",
dass die ganze Arbeit tun soll, main setzt bloß noch wenige Befehle an Deckshand.
Dazu muß ich "segel" in "deckshand" instanzieren, nicht mehr in der main.
(Bildlich : der Skipper rührt die Segel nicht an, er befiehlt bloß)
Aber : das Objekt "Gross", das jetzt in dem Objekt "deckshand" instaziert wurde, kann nicht auf die ISR-Variablen zugreifen !
In welcher Datei eine ISR liegt, spielt keine Rolle. Jedenfalls sind ISR keine C++ Methoden. Wenn ISR auf Objekte zugreifen, brauchst du Zeiger auf diese Objekte, welche für die ISR erreichbar sind.
Für solche global verfügbaren Dinge benutze ich gerne eine Datei die ich global.c nenne und dazu natürlich auch eine global.h die ich dann bei Bedarf inkludieren kann.
int main(void)
{
while(1) //endlosschleife (wie bei ner sps)
{
if (trigger_10hz == 1) //timergesteuerte ausführung
{
trigger_10hz = 0;
//------Hier spielt die Musik-----------------
1
Gross.setzen();
2
3
//Irgendwas ...
4
//--------------------------------------------
5
6
7
}//ende der 10hz-task in der "while 1" -main-schleife
8
};//ende "while 1" - schleife innerhalb main
9
return 0;
}//ende von main
ISR (PCINT0_vect) //Inkremente aus Gruppe 0
{
Gross.Klauinkrement ++ ;
}
Im Objekt "Gross" wird die Methode "setzen" aufgerufen.
In Methode "setzen" wird das Attribut "Klauinkrement" benötigt.
Sowohl das Objekt "Gross" als auch das Objekt "Fock" haben dieses Attribut.
Beide sind aus der ISR zugreifbar, ohne sich zu überschreiben.
Insgesamt soll das eine exemplarische Grundstruktur von OO sein.
Danach werden Methoden durch Vererbung erzeugt usw, usw ...
Wenn jetzt "segel" in "deckshand" instanziert wird statt in main :
#include "segel.h"
segel Grossegel; //Klasse instanzierenn-> Objekt
deckshand::deckshand() //Konstruktorlauf
{
fockfallsollwert=0; //die Fallwinden aus
grossfallsollwert=0;
besanfallsollwert=0;
}
void deckshand::segelsetzen()
{
if (Klauinkrement < 600)
{
//irgendwas
}
}
.. dann klappt das nicht, weil das Attribut Klaukinkrement (siehe ISR oben) natürlich nicht zugegriffen werden kann, es gibt in main ja kein Objekt "Gros" mehr. Ich bräuchte : "Matrose1.Gross.Klauinkrement" , aber das ist wohl Quatsch ;-)
In C++ wird der Begriff "Methode" nicht verwendet. So etwas heißt dort
Funktion, auch wenn es zu einer Klasse gehört.
Da schau her. Zitat Wikipedia :
Was ist eine Methode in C++?
Methoden (englisch method oder member function) sind in der objektorientierten Programmierung Unterprogramme in der Form von Funktionen oder Prozeduren, die das Verhalten von Objekten beschreiben und implementieren. Über die Methoden des Objekts können Objekte untereinander in Verbindung treten.
Wie kann man sich Objekte und ISR gleichzeitig antun ? Aber das Konzept der Objekte wurde verstanden ? Und das Konzept der ISR wurde auch verstanden ?
Objekte kapseln Code und zugehoerige Daten. Ein Objekt hat dafuer passende Code Konstrukte. Und jetzt willst du quer hinein pfuschen mit Interrupts ?
Lass mal - das wird so nichts.
Wikipedia ist nicht der C++-Sprachstandard, sondern spiegelt nur das wieder, was die dortigen Texteschreiber gerade der Ansicht sind, als NPOV durchgehen zu lassen.
Ich hoffe, mich einigermaßen verständlich ausgedrückt zu haben.
Was muß ich lernen, um das zu lösen ??
Für kleine 8Bitter die Programmiersprache anpassen?
Sonst läuft man wie gesehen schnell in die Falle sich mehr mit Sprachbürokratien denn mit dem eigentlichen Problem herumzuschlagen.
In C++ wird der Begriff "Methode" nicht verwendet. So etwas heißt dort
Funktion, auch wenn es zu einer Klasse gehört.
Ist doch Scheiß egal. Jeder weiß, was gemeint war. Nach zig Jahren Berufserfahrung mit zahlreichen Programmiersprachen hat man andere Probleme als solche albernen Eitelkeiten.
Klingt der Funktion nach nach einem Task/process-Scheduler.
Der Linus hat mal zwischen Januar und september1991 einen solchen für x386 geschrieben und später zum Linux-Kernel erweitert.
Nachlesbar im Link wie im Anhang gezeigt, die direkte Wiedergabe des Links wird von der Forumssoftware blockiert. Seufz ....
Ja, das ist ein Scheduler (wenn wir das gleiche meinen). Aber non-premptive, um sich den ganzen Quatsch mit Deadlock, Race und so zu ersparen. NPRM im Klartext.
Globale Variable (extern ?) sind möglicherweise nicht zielführend, weil ich dann ja für die Attribut eines jeden neuen Objekts neue Variablen brauche. Aber es geht natürlich. Ich denke bloß, da muß es im Konzept eine elegantere Methode geben.
Konzeptuell müßte bei der Instanzierung (nicht Referenzierung, sorry) des Objekts irgendwo eine globale Variablenerzeugung möglich sein.
Ich sehe auch nicht, wie das mit einem Zeiger geht, wenn wie hier mehrere Objekte die im "Orginal" (der Klasse) gleichen Attribute hat. Aber hier weiß ich sicher viel zu wenig über Zeiger in C.
Ist aber anders formuliert eigentlich meine Frage :
Wie kann ich im OO-Konzept Objektattribute so erzeugen (instanzieren), daß sie von überall (global) zugreifbar sind ?
Konzeptuell müßte bei der Instanzierung (nicht Referenzierung, sorry)
des Objekts irgendwo eine globale Variablenerzeugung möglich sein.
Wie kann ich im OO-Konzept Objektattribute so erzeugen (instanzieren),
daß sie von überall (global) zugreifbar sind ?
Kann es sein, dass du gar nicht so richtig weißt, was diese Fachbegriffe alle bedeuten? Ich verstehe jedenfalls nur Bahnhof und diese Aneinanderreihung der Begriffe ergibt für mich wenig Sinn.
Zeig doch einfach einmal ein einfaches(!) minimales(!) Stück Code, was dein Problem zeigt.
Ich möchte bei Erzeugen eines Objekts eine "Variable" (besser ein "Attribut", wenn die Zahl der Erbsen eine Rolle spielt) generieren, die die Eigenschaften einer globalen hat. Bitte jetzt keine Diskussion über das alte public/private Spiel..
Wenn "extern" das kann, wäre einer so lieb, mir ein Stück Beispielcode zu zeigen ?
Ich möchte bei Erzeugen eines Objekts eine "Variable" (besser ein
"Attribut", wenn die Zahl der Erbsen eine Rolle spielt) generieren
Dein Text ergibt überhaupt gar keinen Sinn.
Das ist das Problem.
Was ist "das Erzeugen eines Objekts"? Ist es der Aufruf des Konstruktors?
Was meinst du mit "Attribut"? Was ist "generieren"? Warum setzt du "Variable" in Anführungszeichen. Meinst du was anderes?
Ein promovierter Jurist frägt den Ingenieur nach einem Vortrag. "Was ist denn ein Operationsverstärker ? ". Darauf die Antwort (nicht erfunden !!) : "Stellen sie sich einfach einen LM324 vor".
Bei der Instanzierung des Objektes wird die Variable erzeugt. Dann muss
man ihr nur noch einen Wert zuweisen.
Was ggf. der Konstruktor tut. Und weil sie in OO eine spezielle Charakteristik hat, wird sie Attribut genannt, um sie von der klassischen Variablen abzuheben. Aber : darum gehts überhaupt nicht. Ich will hier keine Wortkunde betreiben ...
Static variable in a class is not a part of the subobject of the class. There is only one copy of a static data member shared by all the objects of the class."
Also das friend-Konzept. Ich dachte bisher, das wär was für Functions in namespaces, und mit solchen Dingen hab ich auf den kleinen uP nie zu tun gehabt. Liege ich richtig, wenn ich vermute, daß ein friend-Konstrukt den Zugriff aus einem Objekt auf ein Attribut eines anderen Objekts zuläßt (alles public, oder ?) ? ..Viel zu tun.
Ein promovierter Jurist frägt den Ingenieur nach einem Vortrag. "Was ist
denn ein Operationsverstärker ? ".
Eben, der Juristen formuliert eine trollig falsch formulierte Frage und bekommt denoch eine korrekte Anwort.
Hätte der Jursit statt der Frage "Was ist ein Operationsverstärker", die mit der Nennung der Typbezeichnung völlig korrekt beantwortet ist, gebeten "Bitte erklären Sie mir als elektronisch Ungebildeten die Aufgabe und Funktion eines Operationsverstärkers" hätte er mehr Erfolg gehabt.
Aber wer gibt schon zu, da er zu doof für das Thema "Elektronik" ist.
Wer's im Kontext lustig findet, möge lachen ;-)
Das finden nur Kinder die bespasst werden müßen, lustig. Dist Du so eins?
Ich brauche ne Weile, um deinen Code zu verstehen, sorry. Mein Syntaxvorrat reicht immer genau so weit wie ich zur Lösung von irgendwas brauche, ich hatte nie systematisch C gelernt. (Merkt man ja ;-).
Wenn ichs richtig interpretiere, wird statt des Attributs eine indirekte Adressierung ausgeführt, also ein Pointer benutzt, wie das in C wohl heißt.
Legt der Compiler dann da ne Liste an, welche Adresspeicher (Pointer) zu welchem Objekt gehören ?
Liege ich richtig, wenn ich vermute, daß ein friend-Konstrukt
den Zugriff aus einem Objekt auf ein Attribut eines anderen Objekts
zuläßt (alles public, oder ?)
Das ist kein Attribut. Das ist eine Member-Variable. Ein Attribut ist in C++ etwas anderes.
https://en.cppreference.com/w/cpp/language/attributes
Du verwendest ständig Worte falsch. Und deshalb versteht dich niemand.
Nein, hat nix mit Schiffsgeschwindigkeiten oder ähnlichem zu tun.
Im Kern geht's um Hierarchisierung von Funktionen.
Ich schreibs nochmal als "Prosa", Modellierung ist ja auch ein Bestandteil von OO.
Oberste Ebene : "main" (im Modell : Der Käptn')
erste drunter : Objekte der Klasse "deckshand" : "matrose1", "matrose2"
matrose1 ist eine Instanz von deckshand, die in main instanziert wurde.
Voteile : wenn man neue Funktionen will, wird ein neuer matrose instanziert.
Methoden von deckshand können vererbt usw. werden.
wieder drunter : Objekte der Klasse "segel". Die werden jetzt in den Matrosen instanziert. Vorteile wie üblich in OO. z.b "fock" oder "gross".
so, jetzt kann ich in "matrose1" die methode "fock.setzen()" aufrufen.
-> Ich will aber in main auf Attribute von "fock" in "matrose" zugreifen !
Legt der Compiler dann da ne Liste an, welche Adresspeicher (Pointer) zu
welchem Objekt gehören ?
Mein Beispiel funktioniert nur mit einer Instanz des Schiffes. Der MaWin O. hat meinen obigen Vorschlag zu Recht kritisiert. Denn der Sinn von Objekten ist, dass sie ihre Daten Kapseln. Man sollte niemals von außen direkt auf die Attribute der Objekte zugreifen, schon gar nicht über Zeiger an der Programmiersprache vorbei.
Man kann aber die Instanzen der Objekte global erreichbar machen. Zum Beispiel so:
1
#include<iostream>
2
usingnamespacestd;
3
4
classSchiff{
5
intgeschwindigkeit;
6
7
public:
8
intgetGeschwindigkeit(){
9
returngeschwindigkeit;
10
}
11
12
voidsetGeschwindigkeit(inti){
13
geschwindigkeit=i;
14
}
15
16
voidchangeGeschwindigkeit(inti){
17
geschwindigkeit+=i;
18
}
19
};
20
21
Schiff*schiffe[20];
22
23
// Bei Interrupt wird das erste Schiff verlangsamt
// Erzeuge drei Schiffe
schiffe[0] = new Schiff();
schiffe[1] = new Schiff();
schiffe[2] = new Schiff();
Ich sollte dazu noch etwas ergänzen: Diese Zeilen müssen nicht in der main stehen. Du kannst wie gewünscht ein Schiffsbauer-Objekt programmieren, dass die Schiffe baut (mit new erzeugt) und dem Array zuweist.
Anstelle des Arrays könntest du auch drei einzelne globale Variablen verwenden, wenn dir das lieber ist. Aber auf jeden Fall müssen die Zeiger oder die Referenzen auf auf die Schiffe global sein, sonst kommst du innerhalb deiner ISR nicht heran.
Ich brauche ne Weile, um deinen Code zu verstehen, sorry. Mein
Syntaxvorrat reicht immer genau so weit wie ich zur Lösung von irgendwas
brauche, ich hatte nie systematisch C gelernt.
Deine folgenden Fragen beziehen sich auf meinen (fragwürdigen) Vorschlag:
1
int*globalerZeiger;
2
3
classSchiff{
4
intgeschwindigkeit;
5
6
public:
7
Schiff(){
8
globalerZeiger=&geschwindigkeit;
9
}
10
}
11
12
*globalerZeiger=99
Wenn ichs richtig interpretiere, wird statt des Attributs eine indirekte
Adressierung ausgeführt, also ein Pointer benutzt
Ja, in meinem Vorschlag ist int* ein Zeiger auf eine Integer Variable. Und &geschwindigkeit ist die Adresse der Geschwindigkeit im RAM.
Legt der Compiler dann da ne Liste an, welche Adresspeicher (Pointer) zu
welchem Objekt gehören ?
Der Zeiger hat initial den Wert 0, was allgemein als "zeigt nirgendwo hin" interpretiert wird. Der Konstruktor des Schiffes setzt den Zeiger auf die Adresse seiner Geschwindigkeits-Variable. Erst nach Ausführung des Konstruktors kann man über den Zeiger auf den Speicher der Variable im RAM zugreifen.
Die letzte Zeile im obigen Code-Fragment heißt: Setze den Speicher auf den der Zeiger zeigt, auf den Wert 99. Dadurch wird effektiv die Variable geschwindigkeit von dem Schiff geändert, weil sie sich genau dort im Speicher befindet, wo der Zeiger hin zeigt.
Unsauber ist daran, dass ich dadurch eine eigentlich private Eigenschaft des Objektes global zugänglich gemacht habe, und zwar nicht nur für lesende Zugriffe, sondern auch noch für schreibende.
So Singletons sind meistens unschön. Und wenn schon mit C++ abstrahiert werden soll, dann würde ich die Methodenaufrufe auch nicht direkt in der ISR machen.
Schöner geht das z.B. mit einer EventQueue die auch zyklische Aufrufe erlaubt. Ein instantiiertes Objekt kann sich dann selber in die Queue eintragen. Methodenaufrufe sind nur etwas tricky, die callbacks brauchen den this pointer und die Adresse der Funktion.
Schöner geht das z.B. mit einer EventQueue die auch zyklische Aufrufe
erlaubt. Ein instantiiertes Objekt kann sich dann selber in die Queue
eintragen. Methodenaufrufe sind nur etwas tricky, die callbacks brauchen
den this pointer und die Adresse der Funktion.
Die Sprachkenntnisse des TO liegen irgendwo zwischen Frühgeburt und Analphabet. Da ist dein Vorschlag vielleicht doch etwas verfrüht.
Hmm, na ja, es werden viele Sachen hier durchmischt, glaube ich.
Worum es geht, soweit ich das nachvollziehen kann, ist es, dass der OP ein Objektmodell erzeugen möchte, welches ein Schiffsmodell mit verschiedenen Szenarien beschreibt.
Ein Objekt hat ja grundsätzlich eine Lebenszeit, startend beim Konstruktor und endend beim Destruktor - solange das Objekt 'lebt', hat es Zustand (Membervariablen), deren Inhalt sich im Laufe der Lebenszeit ändern kann. Diese 'Member' sind üblicherweise private und sollten nur über definierte Memberfunktionen/Methoden verändert werden. So die die Theorie.
Singletons bzw. statische Klassen sind im Prinzip ja genau dasselbe, nur dass es eben bloß 1 Exemplar dieser Klasse zur Laufzeit (=1 Objekt) gibt. Ändert fürs Objektmodell eigentlich nicht viel.
Wenn du ein Objekt anlegst/instanziierst, kann das so aussehen, wie Stefan F. das beschrieben hat, mit dem new-Operator instanziiert. Ne andere Variante wäre einfach:
1
class Segel
2
{
3
public:
4
enum class Status
5
{
6
Gerefft = 0,
7
VollesSegel
8
};
9
10
void setzen(enum class Status state )
11
{
12
if(m_status != state)
13
{
14
m_status = state;
15
//... auf aenderung etwas ausloesen
16
}
17
}
18
19
20
private:
21
Status m_status;
22
};
23
24
class Kapitaen
25
{
26
int m_flaschenGrogIntus = 0;
27
public:
28
29
void auftanken()
30
{
31
++m_flaschenGrogIntus;
32
}
33
34
void ablassen()
35
{
36
if(m_flaschenGrogIntus>0)
37
{
38
--m_flaschenGrogIntus;
39
}
40
}
41
42
};
43
44
class Schiff
45
{
46
Segel m_segel;
47
Kapitaen m_kapitaen;
48
49
public:
50
51
Schiff()
52
{}
53
54
void volleFahrt()
55
{
56
m_segel.setzen(Segel::VollesSegel);
57
m_kapitaen.auftanken();
58
}
59
60
void angreifen(Schiff & gegner)
61
{
62
gegner.bumm();
63
}
64
65
void bumm()
66
{
67
m_kapitaen.auftanken();
68
m_kapitaen.auftanken();
69
//untergehen
70
}
71
};
72
73
int main()
74
{
75
Schiff seaMonkey;
76
Schiff leChuckSchiff;
77
seaMonkey.volleFahrt();
78
seaMonkey.angreifen(leChuckSchiff);
79
}
Legst du eine Instanz vom Schiff an (seaMonkey), wird der Konstruktor aufgerufen. Bei der schließenden Klammer } fliegt das Objekt aus dem Scope, die Lebenszeit ist vorbei - der Destruktor wird aufgerufen. Dazwischen kannste Methoden der Klasse aufrufen, wie z.B. volleFahrt, wobei auch gleich der Käptn mitgetankt wird.
In erster Linie erzeugst du hierarchische Abbildung durch Komposition - quasi das große ist die Summe der Teile - im Beispiel enthält das Schiff einen Käptn und ein Segel. Heißt - erzeugst du ein Schiff, wird Segel und Käptn mit erzeugt. Man kann auch Aggregation machen, dann können die Teile auch länger leben als das Schiff.
Mal grob als Idee, hilfreich zum Verständnis sind vielleicht UML Objektdiagramme oder auch ein E/R-Modell.
Das ist nämlich der komplett falsche Ansatz um deine Objekte zu betrachten.
Die main.c (oder main.cpp) ist deine Welt in der du dich bewegst.
In der main.h(pp) musst du alles benennen (nicht definieren) was in der Welt existieren soll. Dabei musst du es nicht unmittelbar machen. Du kannst mit einem #include auch andere Sammlungen von Benennungen, aka andere Headerdateien einbinden.
Deine main.c sollte dann auch nur die Geschehnisse deiner Welt beschreiben. Also wie sie entsteht, dass passiert in der main-Funktion und was sie so macht, das sind die Interruptroutinen.
Da deine Welt über die Header weiß was es alles auf ihr gibt solltest du auch darauf zu greifen können. Wenn es richtig von Grundauf OO ist, dann in der Art Schiff.GetMatrose(Kapitän).GebBefehlAn(Steuermann)
Wie viele schon geschrieben habe, mache die mit den Konzepten betraut und male dir einen Bauplan auf ein Stück Papier.
Nebenfrage: welches main meinst du wieder.
Du selber schreibst weiter oben das es ja main-Funktion und main.h gibt
Die beiden hängen nicht wirklich systematisch zusammen
Syntax ist mir egal, mich interessiert nur das Konzeptuelle.
"Die main.c (oder main.cpp) ist deine Welt in der du dich bewegst.
In der main.h(pp) musst du alles benennen (nicht definieren) was in der
Welt existieren soll."
In Code::Blocks Projekten gibts gar keine main.h
Ich wollte so modellieren wie im Wordbild dargestellt.
Die main.cpp ist bei mir das Deck meines Schiffes, auf dem alle Akteure kommunizieren. Sie sind in der #include - Liste mit ihren .h aufgeführt (quasi meine Decksrolle, Besatzungsliste).
Die Objekte gliedern das Konzept sehr schön, man könnte die Arbeit auch gut verteilen, wenn die Schnittstellen sauber definiert sind.
Käptn gibt Befehle an Matrosen, die haben Elemente des Schiffs (Segel, Ruder, Maschine, Navigationsraum...), dort steht die Hardware (Winden, Motor, GPS...).
Leider ist ein Teil der Messtechnik (z.b. die Inkrementgeber der Winden) in ISR (geht wegen Timing nicht anders). Und die ISR sind hier auf main-Ebene.
Der Pfeil von ISR zur class segel (eigentlich besser zum Objekt Fock z.B.) ist mein Problem. Aber ich denke mit den Zeigern von Stefan F. wird das.
Konzept schlecht ?
(sind ja wohl verschiedene Ansätze möglich...)
an Matrosen, die haben Elemente des Schiffs (Segel,
Ruder, Maschine, Navigationsraum...)
Ich finde es äußerst bizarr, dass du eine Komposition von Matrosen und Schiffselementen machst.
Ein natürlicheres Modell wäre eine Komposition aus Schiff + Schiffsteile und eine weitere Komposition der Besatzung. Die Besatzung würde dann per (kurzlebiger) Referenzen auf die Schiffsteile einwirken.
Der Käptn "bedient" den Matrosen
Der Matrose1 bedient die Fock
Der Matrose2 bedient das Gross
Der Matrose3 bedient den Besan (wobei die Methoden leicht differieren)
Ja. Das ist meiner Ansicht nach ein schlechtes Modell.
Komposition sollte IMO eher durch physikalische Zusammenhänge gebildet werden und "arbeitet mit" sollte mit Referenzen gebildet werden.
Der Vorteil ist, dass man so sehr viel flexibler ist.
Was, wenn ein Matrose mal das Deck schrubben soll? Was, wenn das immer ein anderer Matrose sein soll, weil die Drecksarbeit geteilt wird? Mit einer Komposition ist das nicht abbildbar.
Dem Schiff ist doch egal wer die Winde oder Ruder bedient. Für eine Rechts oder Linkskurve muss ein bestimmter Ablauf eingehalten werden. Ich würde auch die Schiffskomponenten als Objekte sehen.
class Matrose {
...
void schrubbe(deck &Deck) {
...
}
So hab ich das gedacht. z.b. hat jedes Objekt von segel eine Methode "fall_hoch", mit der es per geregelter Winde das Segelchen ein gewünschtes Stück hochziehen kann.
Die AVR GNU Compiler Collection ist ein Set aus mehreren Compilern, unter anderem (hier relevant) der zu C kompatible C++ Compiler mit Spezieller Unterstützung für AVR Mikrocontroller. Interrupthandler sind im Sprachumfang vom C++ Standard nicht vorgesehen. In C sind sie auch nicht vorgesehen, daher die Mikrocontroller spezifische Erweiterung.
Eine Objekt-Funktion (Methode) kann deswegen nicht als Interrupthandler dienen, weil die Interrupt-Vektoren des AVR Mikrocontrollers
a) nur auf Funktionen, nicht auf Objekte zeigen kann. Die Tabelle müsste für jedes Objekt zwei Zeiger enthalten (Funktion und Daten (this)).
b) schreibgeschützt ist. Objekte, die erst zur Laufzeit erzeugt werden, können sich nicht in die Interrupt-Vektoren eintragen.
In modernem C++ arbeitet man auch gutem Grund praktisch nie mit Zeigern.
Dazu habe ich eine ernst gemeinte Frage: Referenzen sind technisch gesehen auch Pointer, haben lediglich eine andere Syntax. Wie erzeugst du Objekt-Instanzen zur Laufzeit, ohne new und ohne Referenzen/Pointer?
Dazu habe ich eine ernst gemeinte Frage: Referenzen sind technisch
gesehen auch Pointer, haben lediglich eine andere Syntax.
Nein. Das ist nicht nur eine andere Syntax.
In C++ gibt es viele verschiedene Arten Objekte zu referenzieren. Klassische Referenzen und auch verschiedene Smartpointer.
Mit Rohzeigern haben die alle eigentlich nichts zu tun.
Aber ich würde auch behaupten, dass man für eine ISR weder Rohzeiger, noch Referenzen, noch Smartpointer braucht. Man kann doch ganz einfach ganz normal auf statisch alloziierte globale Ressourcen zugreifen.
Die AVR GNU Compiler Collection ist ein Set aus mehreren Compilern,
unter anderem (hier relevant) der zu C kompatible C++ Compiler mit
Spezieller Unterstützung für AVR Mikrocontroller.
Was genau soll ein „zu C kompatibler C++ Compiler“ sein? Der erste April war gestern.
Die AVR-gcc-toolchain kommt, wie jeder gcc, mit einem C- und einem C++ -Compiler. Der eine spricht nur vollwertiges C, der andere nur vollwertiges C++. „Kompatibel“ ist das nur in so weit, wie C und C++ gemeinsame Sprachkonstrukte haben.
Aber ich würde auch behaupten, dass man für eine ISR weder Rohzeiger,
noch Referenzen, noch Smartpointer braucht. Man kann doch ganz einfach
ganz normal auf statisch alloziierte globale Ressourcen zugreifen.
Kommt drauf an, was man machen will. Der TO möchte, dass die ISR Eigenschaften seiner Objekt-Instanzen ändert.
Wie erzeugst du Objekt-Instanzen zur Laufzeit, ohne new und ohne
Referenzen/Pointer?
Einfach auf dem Stack?
Der Stack verliert beim Verlassen einer Funktion seine Gültigkeit. Mit der Beschränkung verlierst du die Möglichkeit, Objekte innerhalb von Funktionen zu erzeugen und danach außerhalb dieser zu benutzen. Früher oder später brauchst du das aber.
Der TO braucht es. Seine ISR soll auf Objekte zugreifen, die innerhalb einer Objekt-Funktion (Methode) erzeugt wurden.
Aber auf AVR8 ist es wohl eher generell eine schlechte
Idee dynamische Objekte anzulegen.
Ich möchte es nicht generell verbieten, aber man sollte sich diese Fälle gut überlegen. Insbesondere wegen der möglichen Fragmentierung des Heap.
Dazu habe ich eine ernst gemeinte Frage: Referenzen sind technisch
gesehen auch Pointer, haben lediglich eine andere Syntax. Wie erzeugst
du Objekt-Instanzen zur Laufzeit, ohne new und ohne
Referenzen/Pointer?
Z.B. als lokale Variable, die dann auf dem Stack landet.
Aber ich würde auch behaupten, dass man für eine ISR weder Rohzeiger,
noch Referenzen, noch Smartpointer braucht. Man kann doch ganz einfach
ganz normal auf statisch alloziierte globale Ressourcen zugreifen.
Kommt drauf an, was man machen will. Der TO möchte, dass die ISR
Eigenschaften seiner Objekt-Instanzen ändert.
Das widerspricht sich doch gar nicht.
1
classMyClass{
2
public:
3
voidhandle_irq(){
4
eigenschaft++;
5
}
6
7
protected:
8
inteigenschaft=0;
9
}
10
11
MyClassmyclass;
12
13
ISR(){
14
myclass.handle_irq();
15
}
Der Stack verliert beim Verlassen einer Funktion seine Gültigkeit. Mit
der Beschränkung verlierst du die Möglichkeit, Objekte innerhalb von
Funktionen zu erzeugen und danach außerhalb dieser zu benutzen. Früher
oder später brauchst du das aber.
Das ist eine Behauptung. Kannst du auch ein Beispiel liefern, wo ich das unbedingt brauche?
Der TO braucht es.
Das würde ich in Frage stellen.
Seine ISR soll auf Objekte zugreifen, die innerhalb
einer Objekt-Funktion (Methode) erzeugt wurden.
Das würde ich als kaputtes Konzept beschreiben, weil die Methode ja per Definition zum ISR-Aufruf nicht läuft. Und damit auch alle Objekte innerhalb die Gültigkeit verlieren, wie du schon richtig erkanntest.
Das kann man nicht einmal mit Rohzeigern hinfrickeln.
Das geht einfach nicht.
Zumindest will er es so machen. Lass ihn doch! Du kannst von einem Anfänger nicht erwarten, die hohe Kunst der C++ Programmierung so zu beherrschen, wie einer, der das jahrelang studiert hat.
Auch Anfänger können damit gut funktionierende Sachen hinbekommen - auch wenn sich dem Akademiker dabei die Fußnägel hoch rollen. Ich sage nur: Arduino.
Ich lasse jeden das tun, was er will. Keine Angst. :)
Du kannst von einem
Anfänger nicht erwarten, die hohe Kunst der C++ Programmierung so zu
beherrschen, wie einer, der das jahrelang studiert hat.
Das tue ich auch nicht. Ganz im Gegenteil. Das ist ja der Grund, weshalb ich überhaupt hier Antworte. Ich möchte Anstöße in andere Richtungen geben.
Auch Anfänger können damit gut funktionierende Sachen hinbekommen
Das bestreitet ja auch niemand. Man kann auch in Assembler programmieren und alles funktioniert.
Man kann auch C schreiben (Rohzeiger nutzen) und das mit einem C++-Compiler übersetzen.
Man braucht auch keine Smartpointer und Referenzen zu benutzen und kann trotzdem alles in einen funktionsfähigen Zustand bekommen.
Geht alles.
Aber das schien mir jetzt nicht die Intention des Threaderstellers zu sein.
Du solltest dir unbedingt man etwas >= C++11 anschauen. Es lohnt sich.
Wohl kaum. In meinem Beruf sind ganz andere Programmiersprachen angesagt, da will niemand mehr etwas von C++ hören. Die Sprache ist nur noch für mein Hobby (Mikrocontroller) am Rande neben C ein bisschen interessant.
Mag sein, dass die Sprache im Laufe vieler Jahre allmählich besser geworden ist. Aber sie ist auch zusammen mit der Generation ihrer Entwickler gealtert. In 20 Jahren wenn meiner-einer in Rente geht, arbeitet damit kein Mensch mehr.
Also anstatt ständig den Neuen Aspekten der Sprache hinterher zu laufen, während ich alte Projekte pflegen muss die das eh nicht nutzen können/sollen, lerne ich doch lieber die neuen Programmiersprachen, mit denen die Kollegen links und rechts neben mit erfolgreich Geld für die Firma verdienen. Sonst bin ich nämlich schneller raus aus dem Geschäft, als das Renten-Eintrittsalter vorsieht.
int Fallinkrement;
segel::segel()
{
Fallinkrement = &Fallinkrement;
Im Konstruktorlauf wird dem Objekt ein Zeiger zugeordnet (symbolisch (!!) quasi bei Objekt Fock also "Fock.*Fallinkrement" : Zeiger Fallinkrement, der dem Objekt Fall zugeordnet ist)
und jetzt in der ISR :
Fock.*Fallinkrement ++ ;
Hab ich jetzt ein Syntaxproblem, oder hab ich das Zeiger-Konzept noch nicht verstanden ?
Im Kern stört bei mir, dass jedes Segel 3 Taue hat, die jeweils gleich heißen (Fall, Klau, Piek). Und die Auffächerung in Gross.Piek und Besan.Piek
geschieht ja erst bei Objekterzeugung. In " int* " gibts die noch nicht.
Wohl kaum. In meinem Beruf sind ganz andere Programmiersprachen
angesagt, da will niemand mehr etwas von C++ hören.
Ja gut. Dann verstehe ich allerdings nicht, warum du hier Ratschläge zu C++ gibst.
Für mich gehört es dazu, dass man auf der Höhe der Zeit ist, wenn man Ratschläge gibt.
Die Sprache ist nur
noch für mein Hobby (Mikrocontroller) am Rande neben C ein bisschen
interessant.
Das mag in deiner Blase stimmen.
Das ist aber (leider) ganz und gar nicht die Realität.
Mag sein, dass die Sprache im Laufe vieler Jahre allmählich besser
geworden ist.
C++ hat sich nicht nur "allmählich" entwickelt, sondern hat riesige Sprünge in den letzten 15 Jahren gemacht.
Das ist nun eine vollkommen neue Sprache, die noch rückwärtskompatibel zu 90er-Jahre-C++ ist. (Zu C war C++ noch nie vollständig rückwärtskompatibel)
Aber sie ist auch zusammen mit der Generation ihrer
Entwickler gealtert. In 20 Jahren wenn meiner-einer in Rente geht,
arbeitet damit kein Mensch mehr
Sportliche Vorhersage. Ich hoffe, dass du recht behältst. Ich befürchte aber, dass es nicht so sein wird.
Du willst in der ISR auf den globalen Zeiger "Fallinkrement" zugreifen, nicht auf das Attribut der Instanz der "segel" Klasse. An dieser Stelle ist es unvorteilhaft, dass die globale Variable genau so heißt, wie das Attribut der Klasse. Nenne die Variable z.B. "FallinkrementPtr", dann wird es vielleicht klarer.
Und wenn schon gerätselt wird ob meiner Intention :
Das Schiffchen krieg ich auch ohne Objekte zum fahren, das ist nicht das Thema. Es geht ums Lernen, hier insbesonders um Konzepte in OO. Für UML bin ich aber leider zu faul.
Wenn hier einer was von SPS versteht : so wie Lasal zum Beispiel, das fand ich konzeptuell extrem spannend. Brauchen tu ich's nicht.
Brauchen tu ich gar nix, hab die Rentengrenze längst überschritten :-))
Es gibt aber drei Fallinkremente.
Je eines vom Inkrementgeber an Fockfallwinde, Grossfallwinde und Besanfallwinde.
Wenn ich nur Objekte in main nutze (die Matrosen also quasi an Deck mitreden lasse) werden die durch Fock.Fallinrement, Gross.Fallinkrement usw. sauber unterschieden, wie es Attribute von Objekten eben tun.
Habe ich dich falsch verstanden, daß ich einen Zeiger im Konstruktorlauf machen kann, der dann dem Objekt zugeordnet ist ?
Dann verstehe ich allerdings nicht, warum du hier Ratschläge zu
C++ gibst. Für mich gehört es dazu, dass man auf der Höhe der
Zeit ist, wenn man Ratschläge gibt.
Das ist schon der richtige Weg an solchen Übungsaufgaben zu lernen.
Solange das ganze Schiff und seine Besatzung statisch sind kann man alles global anlegen und braucht keine Zeiger. Wenn Segel statisch angelegt wird und den Fallinkrement Zeiger setzt, dann kann in der ISR auch gleich direkt auf das Objekt zugegriffen werden. Wie von MaWin O schon gezeigt.
Interessant wird es wenn das ganze dynamisch wird, das Schiff z.B. aus einer Konfigurationsdatei zusammengesetzt wird. Dann spielt das OO Konzept seine Stärken aus.
In main wird nur ein Schiff instanziiert, main ist nur der Lebensraum für Objekte. Das Schiff hat Kapitän, Rudergänger, Matrose. Jetzt muss man überlegen was diese sein sollen, so wie du es wolltest sind es Dienstleister die etwas ausführen können (Methoden). Das Schiff hat auch verschiedene Segel oder Motoren, das sind auch Objekte die ein Schiff hat. Diese kann man auch statisch anlegen weil sie ja fest zum Schiff gehören.
Wenn ein Matrose jetzt ein Segel setzen soll, dann muss man ihm auch sagen welches. Das gross, besan usw. statisch angelegt wurden, können diese als Referenz übergeben werden. Das sind auch Zeiger, aber die werden nicht dynamisch angelegt und können damit nicht nullptr sein, das macht den Umgang angenehmer. Kann dann aber nicht dynamisch zusammengesetzt werden.
Interessant wird es wenn das ganze dynamisch wird, das Schiff z.B. aus
einer Konfigurationsdatei zusammengesetzt wird. Dann spielt das OO
Konzept seine Stärken aus.
Richtig. Aber auch dann braucht man in C++ keine Rohzeiger.
Dann spielen Smartpointer ihre Stärken aus.
Dort wird eine eine Referenz auf das Segel an die Memberfunktion übergeben.
Über diese Referenz kann die Funktion auf das Segel zugreifen und es auch verändern.
ja, ist mMn aber eine Feinheit die jetzt für ein Modell keine Rolle spielt. Die Smartpointer sollen ja z.B. die Speicherleck Probleme entschärfen. In C gibts malloc/free, es ist einfach die nicht balanziert aufzurufen.
Ich denke dem TO fehlt der Unterschied Zeiger und Referenz bzw. die Anwendung dieser.
Folglich brauchst du drei Zeiger (oder Referenzen), die jeweils direkt auf die gewünschten Attribute zeigen, oder auf die Objekte, welche die Attribute enthalten.
Ich möchte daran erinnern, dass man eigentlich nicht an der Programmiersprache vorbei direkt auf Attribute der Objekte zugreifen soll. Außerdem sind diese drei Winden Bestandteil eines Schiffes, also genügt es, global auf das eine Schiff zu zeigen oder zu referenzieren:
Mit Referenzen:
1
classWinde{
2
public:
3
intfallinkrement;
4
};
5
6
classSchiff{
7
public:
8
Windefockfallwinde;
9
Windegrossfallwinde;
10
Windebesanfallwinde;
11
};
12
13
Schiffschiff;
14
15
ISRwhatever(){
16
schiff.fockfallwinde.fallinkrement++;
17
schiff.grossfallwinde.fallinkrement++;
18
schiff.besanfallwinde.fallinkrement++;
19
}
Oder mit Zeigern und dynamischer Speicherverwaltung (bei Mikrocontrollern mit Fallstricken behaftet):
Ich möchte daran erinnern, dass man eigentlich nicht an der
Programmiersprache vorbei direkt auf Attribute der Objekte zugreifen
soll.
Jetzt eine Variante mit indirekten Zugriffen über Objektmethoden, was ich viel sauberer finde:
1
classWinde{
2
intfallinkrement;
3
4
public:
5
voidändereIncrement(intwert){
6
fallinkrement=fallinkrement+value;
7
}
8
};
9
10
classSchiff{
11
Windefockfallwinde;
12
Windegrossfallwinde;
13
Windebesanfallwinde;
14
15
public:
16
voidändereIncrementDerWinden(intwert){
17
fockfallwinde.ändereIncrement(wert);
18
grossfallwinde.ändereIncrement(wert);
19
besanfallwinde.ändereIncrement(wert);
20
}
21
22
WindegetFockfallwinde(){
23
returnfockfallwinde;
24
}
25
26
WindegetGossfallwinde(){
27
returngrossfallwinde;
28
}
29
30
WindegetBesanfallwinde(){
31
returnbesanfallwinde;
32
}
33
};
34
35
Schiffschiff;
36
37
ISRwhatever(){
38
schiff.ändereIncrementDerWinden(+1);
39
}
40
41
// oder:
42
43
ISRwhatever2(){
44
schiff.getFockfallwinde().ändereIncrement(+1);
45
schiff.getGossfallwinde().ändereIncrement(+1);
46
schiff.getBesanfallwinde().ändereIncrement(+1);
47
}
An den MaWin O hätte ich die Bitte, dieses Beispiel auf die neuen besseren Features von C++ 11 zu heben, die mir nicht bekannt sind. Dann müssten wir zu einem guten Schluss kommen, denke ich.
Ein Objekt 'Winde' statt 'Segel' wie Stefan schrieb passt besser. Klasse Winde kann gleich sein für die Segel, als Segel würde dann ein enum { gross, besan, fock} reichen.
Ein Objekt 'Winde' statt 'Segel' wie Stefan schrieb passt besser. Klasse
Winde kann gleich sein für die Segel, als Segel würde dann ein enum {
gross, besan, fock} reichen.
Ja sorry, ich habe von Schiffen absolut keine Ahnung, deswegen sind meine Beispiele diesbezüglich vielleicht etwas holprig.
Ja sorry, ich habe von Schiffen absolut keine Ahnung, deswegen sind
meine Beispiele diesbezüglich vielleicht etwas holprig.
ich auch nicht, im Detail wird es irgendwann Geschmacksfrage und mehrere Lösungen sind möglich.
Wenn die Winden unterschiedliche Eigenschaften haben, dann können das verschiedene Objekte sein. Oder eine Basisklasse und die Varianten sind Ableitungen. Möglichkeiten gibt es viele.
Das dynamisch zu machen wäre wie genannt z.B. das Schiff aus einer Konfigdatei zu erstellen. Schönes Beispiel dafür ist 3D Drucker SW Marlin vs. Smoothieware: Marlin hat config in defines und Änderungen müssen neu kompiliert werden, Smoothie kann config aus Datei lesen und zur Laufzeit Pins zuordnen.
sieht nicht verkehrt aus. Üblicherweise schreibt man die Klassen groß, die Instanzen klein. Und zwischen Variable und ++ kein Leerzeichen, ist kein Fehler, sieht aber blöd aus.
Matrose muss global über der ISR angelegt sein. Wie ist die Fehlermeldung?
Finde ich auch. Bei mir compiliert das, wenn ich "ISR(..)" durch "void test()" ersetze, das liegt aber daran, dass ich mein Testprojekt als "Linux Desktop Anwendung" angelegt habe, statt für AVR.
nix geht, Compilerfehler hier : Matrose.Fock.FallInkrement ++ ;
Kann ich so nicht nachvollziehen. Zeige die ganze Fehlermeldung oder hänge dein Projekt in compilierbarer Form an, so dass wir den gleichen Fehler bekommen, wie du.
Da Rainer nach wie vor main() und main.cpp fröhlich durcheinander wirft, könnte es sein, daß er diesmal tatsächlich main() meint. Und dann gehts natürlich nicht.
Jetzt kapier ich erst, was du meinst.
Die Datei main.cpp (in der auch die ISR enthalten sind), und die main, in der die Endlosschleife zur Programmausführung läuft.
Ich war vermutlich ungenau mit der Bezeichnung ...
Ich hab das C::B - Projekt gezippt und angehängt.
Macht euch aber bitte nicht zuviel Arbeit.
Fehlermeldung :
main.cpp|138|error: 'class deckshand' has no member named 'Gross'|
Dafür musst du nur in die deckshand.h reinschauen.
Gibt es einen Member namens "Gross"? Nein.
Du kannst mit deinen Objekten nur Member und Methoden verwenden die public zur Verfügung stehen. Wenn das deine eigenen Klassen sind solltest du das Wissen.
Selbst wenn du das
1
Matrose.Gross.KlauInkrement++;
2
Matrose.Besan.KlauInkrement++;
korrigierst auf
1
Matrose.KlauInkrement++;
2
Matrose.KlauInkrement++;
gibt es keinen Member namens "KlauInkrement". Weil Matrose ist ja eine Instanz von deckshand.
Fock, Gross und Besan sind Objekte von segel. Die können nur auf public von segel zugreifen.Hier steckt auch der Member KlauInkrement drin. Wahrscheinlich meinst du
1
Gross.KlauInkrement++;
2
Besan.KlauInkrement++;
Hier ist einfach zu viele Copy & Paste im Spiel. Erschwerend kommt hinzu das Gewürfel mit Groß- und Kleinschreibung. Schreibe Klassennamen Anfangs Groß und Member und Methoden Anfangs Klein. Zudem KlauInkrement unglücklich ist. Was machst beim Dekrementieren dieser Variablen?
Matrose ist eine Instanz von deckshand. Deckshand enthält weder ein Gross, noch ein Besan, noch ein Deckshand.
KlauInkrement, PiekInkrement sind FallInkrement sind Attribute der Klasse segel. In deckshand.cpp gibt es drei globale (!) segel mit Namen Fock, Gross, und Besan. Diese sind also nicht Bestandteil von deckshand sondern davon völlig losgelöst.
Du musst die Segel in die Klasse deckshand verschieben. Siehe Anhang.
Hmm deine Segel sind ja auch eigenschändig und schwirren irgendwo in deiner Welt herum.
Wenn du die Segel als Objekte deines Matrosen haben willst, dann solltest du das auch machen.
Das in deckshand.cpp
1
segelFock;
2
segelGross;
3
segelBesan;
muss dazu in deine Klasse deckshand hinein. also in deckshand.h
1
classdeckshand
2
{
3
[..hierdeinganzerandererKram]
4
5
segelFock;
6
segelGross;
7
segelBesan;
8
}
Aber man sollte auf Klassenmember nur über Methoden zu greifen. Also alles das was du da drin hast, sollte private sein.
Und nur die Methoden sollten public sein.
Wie schon gesagt, OO heißt auch dass man sich Gedanken über den Blick auf seine Welt macht. ;-)
Anderes Thema:
Füge bei Gelegenheit Include Guards in alle deine Header Dateien ein, damit sie keine Probleme machen, wenn sie mehrfach eingebunden werden. Beispiel:
Winde wird segel, schiff wird deckshand. In der ISR dann die
entsprechenden Angabe.
Oh weh, ich kapier's nicht ...
Dann solltest du aber auch beachten das Stefan die Winden-Objekte in der Klasse Schiff erzeugt hat. Und eben nicht im Luftleeren Raum einer anderen Datei.
Weiter oben hat jemand bemängelt, dass hier das Schiff und die Matrosen
zu sehr miteinander vermischt werden. Da hat er wohl Recht.
Definitiv.
Die Matrosen sollten auf/mit dem Schiff arbeiten, aber nicht Bestandteil
des Schiffes sein. Ebenso ist das Schiff kein Körperteil der Matrosen.
Richtig, das Schiff hat mehrere Matrosen und ein Matrose hat maximal ein Schiff. Die können aber wechseln, dann hin und wieder geht so ein Matrose über Bord und muß im nächsten Hafen durch einen neuen ersetzt werden.
Das sollte man nochmal gründlich umstrukturieren.
Ähnlich ist es mit den Aufgaben der einzelnen Matrosen. Die können auch nicht statisch verteilt werden, denn mitunter werden auch erfahrene Segler seekrank, verletzen sich, oder fallen anderweitig aus. Und wer Törns über längere Strecken fährt, braucht irgendwann einen Wachwechsel... auch dann müssen die Aufgaben neu verteilt werden.
Alles, was hir bisher geschrieben wurde, sind die absoluten Basics von OOP - eigentlich nicht mal das.
Ich steige mal an anderer Stelle: was soll denn die Klasse i2c modellieren? Ok, die TWI-Schnittsteller als I2C. Aber was erwartest Du von dem Konstruktor. Was passiert, wenn Du mehr als i2c-Objekt erzeugts?
Das ist übrigens bei allen Deine HW-Abstraktionen so.
Beim Einbau deines Codes gabs zunächst Probleme, weil Code::Blocks sich daran stört, daß die Klasse und ein Attribut beide "segel" heißen.
Kein Problem, Attribut in "segelbefehl" umbenannt.
Läuft immer noch nicht. Die Definitionszeile "class segel" wird bemängelt :
segel.h|1|error: redefinition of 'class segel'|
Wenn ich jetzt "#include segel.h" aus der main nehme, gehts.
Wieso ? Die include-Zeile ist doch keine Definition der class segel, oder ?
Also : jetzt ist der Compiler zufrieden.
Hätte ich ohne eure Hilfe so nicht hingekriegt.
Und vor Allem : sowohl über Modellierung als auch über Pointer viel gelernt !!
Wenn ich jetzt "#include segel.h" aus der main nehme, gehts.
Wieso ? Die include-Zeile ist doch keine Definition der class segel,
oder ?
Bitte beachte Stefans Ausführungen zum Thema "Include Guards". Diese verhindern, daß dieselbe Datei mehrmals inkludiert wird, und es zu Fehlern wie "redefinition of 'class segel'" kommt.
Alles, was hir bisher geschrieben wurde, sind die absoluten Basics von
OOP - eigentlich nicht mal das.
Das ist mir klar. Vor langer Zeit war das mal ein Versuch, die grundlegendsten Basics von Objektorientierung auszuprobieren.
Im Kern sollten nur die Begriffe : Klasse, Objekt, Methode, Attribut am Beispiel genutzt werden, völlig ohne Mdellierung usw...
So wie :
Klasse ist Obst
Objekt ist Apfel
Methode ist faulen
Attribut ist Farbe (Wert ist rot)
Na ja, und viel weiter bin ich dann in der Anwendung auch nicht gekommen.
Ich hab zwar mit Lasal und Codesys automatisiert und ein paar solche Modellprojekte programmiert, aber im strikt low(est) level.
Läuft immer noch nicht. Die Definitionszeile "class segel" wird
bemängelt : > segel.h|1|error: redefinition of 'class segel'|
Wenn ich jetzt "#include segel.h" aus der main nehme, gehts.
Wieso ?
Die include-Zeile ist doch keine Definition der class segel, oder ?
Die include Zeile nicht, aber der Inhalt der Datei die du an dieser Stelle includierst. Bevor dein Quelltext compiliert wird, wird die Zeile
#include "segel.h"
durch den Inhalt der genannten Datei ersetzt. Du inkludierst aber auch
#include "deckshand.h"
welche wiederum
#include "segel.h"
includiert. Nun hast du den Inhalt der Datei segel.h zweimal in main.cpp eingefügt. Man kann halt nicht zwei (oder mehr) Klassen mit gleichem Namen haben.
Ich kann dir auch nur dringend empfehlen, die Programmiersprache auf einem PC zu lernen/üben.
Mikrocontroller legen da noch einiges an Komplexität oben drauf und sind schwerer zu debuggen. Es ist gut, wenn man die Sprache dann schon in den Grundzügen verstanden hat und wenigstens ein bisschen Erfahrung gesammelt hat.
Du könntest z.B. mein Tutorial durch gehen, und danach ein Program,m schreiben, dass ein Schiff auf den Bildschirm malt. Natürlich indem du es schön aus Objekten zusammen setzt. Du wirst dabei viel lernen.
Bitte beachte Stefans Ausführungen zum Thema "Include Guards"
Im Eifer des Gefechts vernachlässigt.
Hab jetzt erst mal nachgelesen, was das tut.
Bisher hab ich diese Zeilen immer aus neuen class-Definitionen rausgelöscht, weil ich bloß Code haben will, den ich auch verstehe. Rein zufällig gabs bisher damit keine Probleme. Conclusio : nochmal was gelernt !
Apfel ist auch eine Klasse, und zwar von Obst abgeleitet. Das Bild zeigt drei Instanzen von der Klasse Apfel, oder anders gesagt, drei Objekte oder drei Ausprägungen.
Ein Objekt ist ein Ding, das man erzeugen, anfassen und benutzen kann. Eine Klasse beschreibt nur die Eigenschaften einer Klasse von Objekten, ist aber keine konkrete Ausprägung.
Ein Traum.
Kleiner bescheidener Satz : Ihr würdet nicht glauben, wie weit man mit so einer Murkserei kommt. Hab bisher noch alles zum Laufen gekriegt, aber (wichtig !) nie in professionellem (!) Kontext von Softwareentwicklung.
Ich sehe das wie meinen Fernseher : da kapier ich 90% vom Menü auch nicht
Korrektur. Segel als Klassenname sollte Groß geschrieben werden.
Hallo,
Hinweis am Rande. Verwende gleich pragma in den Headerfiles.
1
#pragma once
2
3
classSegel
4
{
5
// ... irgendwas etwas sinnvolles ...
6
}
Ansonsten empfehle ich Reiner sich nochmal im Kleinen mit Klassen zu beschäftigen. Man sieht ja das du in deinen eigenen Klassen nicht durchblickst. Nochmal paar Schritte zurückgehen und den Aufbau überdenken.
Hatte ich irgendwann im PHP-Zusammenhang zitiert :
Classes are nothing without objects! We can create multiple objects from a class. Each object has all the properties and methods defined in the class, but they will have different property values.
$apple and $banana are instances of the class Fruit
Auch "godfather of education", (die Uni Harvard), nimmt dieses Beispiel in der Einführungsvorlesung "Objektorientierung"
Ihr würdet nicht glauben, wie weit man mit
so einer Murkserei kommt
Oh doch. Ich (bzw mein Chef) hat damit gut Geld verdient.
Ich sage immer: Softwareentwicklung ist wie ein Adventure spiel. Zuerst muss man herausfinden, was überhaupt machen soll, was das Ziel ist. Dann muss man heraus finden, wie man da hin kommt. Dabei macht man viele Fehler, manche mehrfach. Und wenn man es dann am Ende geschafft hat, kommt schon das nächste Projekt mit neuen Herausforderungen.
Wenn du eine Simulation schreiben wolltest und Kapitän, Matrosen usw wären Spielfiguren dann wären Klassen dafür sinnvoll.
Aber für ein Modellschiff mit einem AVR zu steuern ???
Du bist der Kapitän mit deiner Fernsteuerung in der Hand, der Empfänger mit dem AVR sind deine Offiziere die deine Befehle als Aktionen an die Matrosen(Servo) weiter geben.
Weit gefehlt ! Der AVR ist sogar eigentlich zu klein, mir gehen sowohl die externen Interrupts (deshalb die PCINT) als auch die Timer aus, ich muß sogar Timer multiplexen... Wenn ichs nochmal bauen würde, würde ich die Navigation und den Skipper wohl mit einem Raspi unter Python machen, zwecks der Gaudi.
Das Ding soll :
Ferngesteuert fahren (na ja, kein Ding, da hast du recht..).
Alle Segel bergen und setzen können (kann kaum ein Modellboot).
Auf Wunsch viele Waypoints (GPS-Koordinaten) unter Motor anfahren können mit FPV, also einer Funkkamera, die zeigt, was da zu sehen ist.
Bei Senderausfall (oder einfach ausschalten) automatisch Segel bergen und zum Ausgangspunkt ("home") unter Motor zurückfahren.
Die härteste Forderung, nämlich unter Segeln Waypoints anzufahren, habe ich zurückgestellt. Einen reibungsfreien Verklicker habe ich zwar gebaut, aber die Kreuz gegen den Wind macht Schwierigkeiten.
Ich habe das Gefühl, dass du zu viel mit Interrupts umsetzen willst.
Man kann Eingänge auch nacheinander abfragen. Multitasking kann man mit mehreren parallel ablaufenden Zustandsautomaten erreichen.
Interrupts benutzt man typischerweise nur dort, wo es unbedingt notwendig ist, die laufende Arbeit sofort zu unterbrechen um auf das Ereignis schnellstmöglich (binnen weniger Millisekunden oder noch schneller) zu reagieren.
Zum Beispiel nutzt man eine ISR, um seriell empfangene Bytes in einen Puffer zu übertragen. Schließlich wartet der Sender der Daten nicht den Moment ab, wo du empfangsbereit ist. Die Verarbeitung der Daten sollte aber nicht in der ISR stattfinden.
Einen Schaltkontakt sollte man aber normalerweise nicht an eine ISR koppeln, sondern aktiv abfragen.
Mit zu vielen Interrupts verzettelt man sich ganz schnell. Bedenke, dass dein AVR sowieso nur eine ISR gleichzeitig ausführen kann. Alle anderen Interrupts müssen eh warten, bis er wieder ins Hauptprogramm zurückgekehrt ist. Durch viele Interrupts wird die Abarbeitung von Ereignissen unter Umständen sogar langsamer, als eine sequentielle Abfrage der Eingänge im Hauptprogramm.
Wenn man mal bedenkt, dass kein Motor so schnell anlaufen und stoppen kann, frage ich mich, was das in deiner Anwendung wohl sein mag. Empfangene Kommandos in einen Puffer eintragen wäre ein Fall. Und sonst? Wofür brauchst du Interrupts?
Weit gefehlt ! Der AVR ist sogar eigentlich zu klein, mir gehen sowohl
die externen Interrupts (deshalb die PCINT) als auch die Timer aus, ich
muß sogar Timer multiplexen...
Dann ist was falsch an Deinem Konzept.
Den Empfänger hast Du bestimmt über SBus, IBus oder Hott angeschlossen, das ist eine serielle Schnittstelle -> UART
Das hast Du ggf. Telemetrie über SPort, IBus oder Hott-Sensor, ebenfalls seriell -> der 2. UART
Dann liest Du GPS -> der 3. Uart
Dann produzierst Du PWM für die Servos -> 1 Timer (bis zu 16 Servos)
//Timer 0 : PPM-Generator (für Servos und RC-Winden)
//Timer 1 : 10HZ-Generator für NPRM-Scheduler
//Timer 2 : PWM-Generator (CompA für Maschine )
//Timer 3 : PPM-Decoder (vom RC-Sender)
//Timer 4 : PWM für Fall-Winden in Gruppen
//Timer 5 : Entprellung der Inkrementgeber
//UART0 : USB zum PC
//UART1 : GPS
Klar könnte ich z.b. die Motorsteuerung mit nem Brushless und einem Stellerl machen, oder ein "Fahrtregler" für den DC-Motor. Aber warum kaufen, wenn das auch mit Software geht. Mein Empfänger kann nicht seriell, also Auswertung des PPM-Summensignals. Die Winden brauchen Inkrementgeber, weil ich die Wege und die Geschwindigkeit steuern will, also Interrupts. Die Inkrementgeber "prellen" beim 1/0-Übergang, wohl optische Gründe, könnte man auch filtern, aber SW ist lustiger. Und für Regler und digitale Filter brauche ich harte Echtzeit, also der Scheduler.
Ein Katamaran, der geregelt auf einer Kufe fährt
Eine Modellrakete, die wie ein Cruise-missile fliegt
Werde nicht albern, lerne erst mal programmieren.
Hast du eigentlich einen Debugger? Wenn nicht, dann ist jetzt vielleicht noch eine gute Gelegenheit, den ATmega 2560 (?) durch ein STM32 Nucleo Board zu ersetzen, da hast du den Debugger direkt mit drauf.
Ja, also das wird sicher mit AVR machbar sein.
Aber ich würde mir das in diesem Fall heute auch nicht mehr mit AVR antun, wenn es nicht unbedingt sein muss.
Da würde ich eher einen billigen 32-Bit Mikrocontroller wie z.B. den ESP32 nehmen. Die maximal paar Milliwatt Mehrverbrauch fallen hier gar nicht ins Gewicht.
Den kann man dann auch in Python programmieren, wenn man das möchte. Oder halt auch in vielen compilierten Sprachen wie C, C++ und Rust.
AVR nutze ich eigentlich nur noch da, wo es auf absolut geringen Energieverbrauch ankommt, oder wo die Anwendung trivial ist.
Außerdem würde ich unabhängig vom Controller so wenig Hardwaretimer wie möglich einsetzen. Ein simples Realtime-Scheduling mit festen Zeitscheiben ist viel einfacher und IMO auch robuster als etliche Interrupts, die sich ständig unterbrechen können. Das Scheduling kann von einem HW-Timer ableiten.
Die restlichen HW-Timer dann für z.B. PWM, was autonom in HW generiert wird.
Außerdem würde ich die Aktivitäten in den verbliebenen Interrupt Service Routinen auf das absolute Minimum beschränken.
Bei all diesen Projekten ist der uC nicht das Problem, oder ob das schönes C wird, sondern eher die Regelungstechnik (und bei der Rakete vielleicht die Servogeschwindigkeit). Versuche sind schon gelaufen.
Andere Beispiele für echt harte Regelprojekte : Quadrocopter oder self-balancing (siehe segway).
//Timer 0 : PPM-Generator (für Servos und RC-Winden)
//Timer 1 : 10HZ-Generator für NPRM-Scheduler
//Timer 2 : PWM-Generator (CompA für Maschine )
//Timer 3 : PPM-Decoder (vom RC-Sender)
//Timer 4 : PWM für Fall-Winden in Gruppen
//Timer 5 : Entprellung der Inkrementgeber
//UART0 : USB zum PC
//UART1 : GPS
Timer5 brauchst Du nicht bzw. kannst das mit Deinem Timer1 zusammenfassen.
Und warum brauchst Du so viele externe PinChange-Interrupts?
Ich steige mal an anderer Stelle: was soll denn die Klasse i2c
modellieren? Ok, die TWI-Schnittsteller als I2C. Aber was erwartest Du
von dem Konstruktor. Was passiert, wenn Du mehr als i2c-Objekt erzeugts?
Das ist übrigens bei allen Deine HW-Abstraktionen so.
Diesem Problem solltest Du Dich auch noch mal stellen (s.a. scheduler).
Find ich extrem spannend. Ich hab die letzten Jahre viel mir Python gespielt, natürlich nicht auf deinem Niveau, und rumgesucht, ob das auch auf dem AVR möglich ist. Ich hab leider nix vernünftiges gefunden.
Wenn du erlaubst, stelle ich dir dazu später ein paar Fragen. Soll ich da ein extra topic machen, oder wie geht das ?
brauchst Du so viele externe PinChange-Interrupts?
Ich hab aktuell 5 Winden, es sind aber noch drei Segel ausstehend (nochmal Winden) jede Winde einen Inkrementgeber, weil ich nicht bloß ein/aus steuern möchte.
Die normalen externen Interrupts sind schon überwiegend belegt an den Ports, und umlöten tun ich da nix mehr (du kannst dir vermutlich vorstellen, wie die Platine von hinten aussieht, wenn ein 65-jähriger mit Zitterfingern so einen Haufen Zeug mit Draht frei verlötet ;-).
Timer5 brauchst Du nicht bzw. kannst das mit Deinem Timer1
zusammenfassen.
Das Problem liegt andersrum.
Ich habe da einen Fehler im System : die Inkrementgeber "prellen", wahrscheinlich springt die Optik kurz nach der Kante ein paarmal hin-und her.
Wenn ich nur einen Timer dafür benutze (klassische bounce-Lösung mit Totzeit),
kann nur eine Winde zu einer Zeit laufen : sehr unsportlich !
Ich bräuchte im Kern sehr viele Timer um parallel setzen/bergen/reffen zu können, einen pro aktiver Winde.
Grübel. Hardwarelösung, also rummessen, analogen Filter entwerfen, oder einen Coprozessor (AtMegas hab ich genug). Weiß nicht. uP ist lustiger.
Diesem Problem solltest Du Dich auch noch mal stellen (s.a. scheduler).
Ich weiß. Die OO für die HW-Komponenten kommt aus einem anderen (alten) Projekt, und passt nicht ins Modell, das ist mir klar. Damals hatte ich einfach ausprobiert, ob ich die SW-Struktur mit Objekten, Methode usw. überhaupt zum Laufen kriege.
Vielleicht nochmal grundsätzlich: der Titel ist "C++ Objektorientierung". Dir sollte bewusst sein, dass C++ keine rein objektorientierte Programmiersprache ist. Sondern C++ ist eine Sprache, mit der man auch(!) objektorientiert programmieren kann. Aber auch anders: imperativ-prozedural, funktional, generisch, meta-programmatisch, ...
Und der Code, den Du vorgestellt hast, ist mitnichten OOP. Denn die Grundprinzipien von OOP sind Kapselung in Klassen, Vererbung, Polymorphie und spätes Binden. Von diesen Prinzipien hast Du nichts umgesetzt.
Außerdem ist Deine Anwendung wohl so, dass Du auch nicht direkt OOP benötigst, da so ziemlich alles statisch.
Aber auch ist Dein Ansatz noch nicht einmal konsequent objektbasiert.
Zudem hast Du auch Klassen die etwas der internen Peripherie modellieren sollen. Hier ist es konzeptionell falsch, Instanzen von Klassen zu erzeugen, es sei denn, Du verwendest ein Muster zur Kapselung von globalen Zuständen in multiplen Instanzen wie etwas das Monostate-Pattern.
Also alles in allem: so wie Du das anfängst, hast Du keinerlei Vorteile gegenüber rein imperativ-prozeduraler Vorgehensweise. Und OOP lernt man nicht mit µC, sondern ganz normal auch dem PC. Aber da wiederhole ich mich.
Hier ist es konzeptionell falsch, Instanzen von Klassen zu
erzeugen,
Wieso ?
Daß es zum Modell nicht passt ist eine Sache, aber warum darf eine funktionierende Programmiertechnik quasi "grundsätzlich" nicht für dies oder das benutzt werden ?
Ähnlich Frage :
(Ich weiß, ist sakrosankt) Wieso immer Kapselung ? Ich verstehe die Vorteile in entsprechendem Kontext. Aber in so einem kleinen Projektchen, das nicht im Team entwickelt wird ? so what ? alles public und fertig ...
Laß mich ein Beispiel geben : wenn ich am Holm eines großen Segelflugzeuges klebe, nehme ich das beste Epoxi und mache vorher einen Klebekurs.
Bei Spielzeug klebe ich mit Uhu, fertig.
Wieso ?
Daß es zum Modell nicht passt ist eine Sache, aber warum darf eine
funktionierende Programmiertechnik quasi "grundsätzlich" nicht für dies
oder das benutzt werden ?
Stellt Dir vor, Du erzeugst an unterschiedlichen Stellen in Deinem Code mehrere Instanzen der Klasse "scheduler" oder "i2c". Was erwartest Du, was dann passiert?
Ich habe nicht gesagt "grundsätzlich" falsch, sondern konzeptionell falsch.
Das ist jetzt Nachhilfe pur, und ich danke dir dafür !
Ich schätze mal, mehrere Instanzen von I2C werden rein softwaremäßig funktionieren.( Wobei ich das Problem, das bei mehrmaligem include einer .h passiert, und mit "Include Guards" gelöst wird, noch nicht so genau verstanden habe)
Wenn dann aber die verschiedenen Objekte auf die nur einmal vorhandene Hardware zugreifen, knirscht es aber. Es mag mulitinstanzfähige Lösungen geben, die auf dem AVR ist es sicher nicht.
So war es aber auch nie gedacht, es ging damals nur um die Funktionsfähigkeit des Syntax-Gebäudes "Klasse-Objekt-Attribut usw...".
Und, auch akzeptiert : das muß raus aus dem Schifferl ;-)
Ich schätze mal, mehrere Instanzen von I2C werden rein softwaremäßig
funktionieren.( Wobei ich das Problem, das bei mehrmaligem include einer
.h passiert, und mit "Include Guards" gelöst wird, noch nicht so genau
verstanden habe)
Hier verwechselst Du "nicht compilieren" mit "zur Laufzeit was Falsches machen".
Das Thema "include guard" ist ein simples Thema der Strukturierung des Codes innerhalb der Übersetzungseinheiten. Hier hast Du in C++ alle Freiheiten, Hauptsache Du machst es richtig. Trotzdem gibt es ein übliches Verfahren, etwa die Klassendefinition und Definition der Elementfunktionen in Header und Implementierungsdatei zu unterteilen. Zusammen mit include-guards hilft dies zuverlässig, die ODR einzuhalten und Mehrfachdefinitionen innerhalb einer Übersetzungseinheit auszuschließen.
Das hat aber nicht mit dem Laufzeitproblem zu tun, was ich angesprochen habe.
Mehrere Exemplare des Typ i2c werden natürlich irgendwie "funktionieren", aber nicht das tun, was Du willst. Das ist dann ein semantisches Problem. Und ja, Du hast es erkannt: Objekte, die nicht ihren eigenen Zustand, sondern einen globalen Zustand (der, der internen Peripherie) modellieren sollen, benötigen besonderes Augenmerk. Eine Lösung ist das Monostate-Pattern. Eine andere ist es, eine Klasse nur mit static Elementen zu schreiben und es zu verbieten, Objekte davon zu erzeugen.
Laß mich ein Beispiel geben : wenn ich am Holm eines großen
Segelflugzeuges klebe, nehme ich das beste Epoxi und mache vorher einen
Klebekurs.
Bei Spielzeug klebe ich mit Uhu, fertig.
Ob Hobbybastler oder Profi....
Beide wollen am Ende eine funktionierende/stabile Software.
So verwenden auch beide die gleichen Techniken.
Manches mal sind die Hobby Leute gar noch verbissener.
Ich kenne aus der Industriewelt multiinstanzfähige HW, die also mehrere "gleichzeitige" Requests auffangen und geordnet abarbeiten kann. Hätte der I2C-Teil im AVR diese Fähigkeit, wären die parallelen Instanzen wohl möglich, oder ?
Ich kenne aus der Industriewelt multiinstanzfähige HW, die also mehrere
"gleichzeitige" Requests auffangen und geordnet abarbeiten kann. Hätte
der I2C-Teil im AVR diese Fähigkeit, wären die parallelen Instanzen wohl
möglich, oder ?
Die hast Du ja auch bei AVR: es gibt mehrere HW-Instanzen eines UART ;-)
Jetzt benötigst Du idealerweise eine Lösung, die es Dir erlaubt, passend zu den 3 HW-Uarts auch drei unterschiedliche Instanzen zu erzeugen: Instanz 1 bezieht sich auf Uart 1, Instanz 2 bezieht sich auch Uart 2, u.s.w., Instanz 42 bezieht sich auf ... was? Und idealerweise sollte dies Problem nicht erst zur Laufzeit auftauchen, sondern zur Compilezeit.
In C++ gibt es noch eine Art von "Instanz", und zwar wenn man aus einem Template eine Klasse erzeugt spricht man auch von Instanziieren eines Templates. Denn instanziieren bedeutet nur, aus etwas Abstraktem etwas Konkretes zu erzeugen: aus einer Klasse ein Objekt, oder aus einem Template eine Klasse.
Der Template-Instanziierungsprozess läuft zur Compilezeit, und deswegen kann man hier dann die obige Prüfung einbauen, damit eine fehlerhafte Parametrierung gar nicht erst compiliert. Laufzeitfehler zu suchen is hässlich ;-)
Mehrere I2C Ports, dann auch mehrere z.B. Wire Instanzen.
nochmal nachgefragt : Ich kann z.B auf dem AtMega256 aus zwei Instanzen "gleichzeitig" (also innerhalb des Befehlszyklus) auf die I2C-Schnittstelle zugreifen (also z.b. zwei verschiedene Peripheriegeräte abfragen) ??
Mehrere I2C Ports, dann auch mehrere z.B. Wire Instanzen.
nochmal nachgefragt : Ich kann z.B auf dem AtMega256 aus zwei Instanzen
"gleichzeitig" (also innerhalb des Befehlszyklus) auf die
I2C-Schnittstelle zugreifen (also z.b. zwei verschiedene
Peripheriegeräte abfragen) ??
Ich kann z.B auf dem AtMega256 aus zwei Instanzen
"gleichzeitig" (also innerhalb des Befehlszyklus) auf die
I2C-Schnittstelle zugreifen
Kannst du, das wird aber Chaos verursachen.
Du kannst entweder nacheinander auf eine I2C-Schnittstelle zugreifen, oder gleichzeitig auf verschiedene. Alles weitere bliebt dir überlassen.
Wenn du alles public machst ist es nur eine Frage der Zeit bis dein
Schoner auf Grund läuft.
Danke für das schöne Bild ;-)
Ich weiß, ich kämpfe gegen Windmühlen, trotzdem :
Ich brauche keine getter/setter, wenn ich eh keine Fehlerprüfung mache.
Was hat ein setter dann für einen Sinn ?
In C++ sagt man auch nicht unbedingt getter/setter. Die Unterscheidung kann hier der Compiler anhand des const-Attributes machen.
"Setter" haben die Aufgabe, nicht vorgesehene Objektzustände in Form von Zusicherungen auszuschließen und ggf. Abbildungen zwischen extern sichtbarem und intern modelliertem Zustand zu machen.
Stell Dir vor eine Klasse Point, die intern die Koordinaten in Polarkoordinaten modelliert, jedoch nach außer auch Cartesische Koordinaten anbietet.
Wiederverwendbarkeit und sowas ist gut für Profi-Projekte, ich brauch
das nicht in meinem Spielzeugkontext.
Dur wirst Dich freuen, wenn Du nach 2 Wochen Pause Deinen eigenen Code wieder anschaust.
Du könntest dir auch das Leben leichter machen und Platinchen (Matrosen) bauen. Ein kleiner MC und die Motortreiber darauf. Das würde dein Softwareproblem und den Kabelverhau reduzieren und skalierbar sein.
Ich brauche keine getter/setter, wenn ich eh keine Fehlerprüfung mache.
Was hat ein setter dann für einen Sinn ?
Wiederverwendbarkeit und sowas ist gut für Profi-Projekte, ich brauch
das nicht in meinem Spielzeugkontext.
Es geht ja weder darum, dass man so etwas zwingend braucht oder ob etwas ein "Profiprojekt" ist. Natürlich kann man ohne auskommen.
Aber insgesamt lebt es sich mit einer sauberen Datenkapselung (getter/setter) deutlich einfacher, je größer das Projekt wird. Die mentale Last wird sehr reduziert, wenn du dir nicht ständig Gedanken darüber machen musst, wer denn jetzt wieder von wo direkt auf irgendeine Variable zugreifen könnte. Was heute eine einfache Variable ist, kann morgen komplexer sein. Und mit einer sauberen Kapselung sind solche Anpassungen meist Klasseninterna.
Außerdem finde ich es lustig, wie zwischen Hobby- und Profiprojekt unterscheidest. :)
Ich kann dir versichern, dass so manches Profiprojekt aussieht, als wäre es von Amateuren zusammengestümpert worden.
Wenn ich eins gelernt habe, dann: Überall wird gefrickelt. Nirgendwo wird alles perfekt oder nach der "reinen Lehre" gemacht. Und am Ende funktioniert es dann meistens doch irgendwie zuverlässig.
Man kann sich aber sehr sehr viel Arbeit sparen, wenn man einige Dinge von vorne herein richtig macht. Und da gehört eine vernünftige Datenkapselung IMO mit an die erste Stelle.
Nochmal : ok, ihr habt mit den meisten Aussagen wohl recht
Nochmal : danke !
Verzeiht provozierende Rückfragen, ich versuche nur, den Kern der jeweiligen Thematik freizulegen.
Wie ist das allgemein hier ?
Technisches Forum mit möglichst knapper, präziser Info, oder ist auch Geplauder erlaubt ? Wenn nicht erwünscht, sollten wir das Kaffeekränzchen stoppen, ansonsten macht's Spaß.
Geplauder (Abschweifungen und ähnliches) gehört IMHO immer dazu wenn man tiefer ins Thema einsteigt und sich auch prima austauscht so wie das hier im Thread grade der Fall ist. Wichtig ist eigentlich nur, dass man auch den Bogen zum Threadthema wieder zurück findet.
Du könntest dir auch das Leben leichter machen und Platinchen (Matrosen)
bauen. Ein kleiner MC und die Motortreiber darauf. Das würde dein
Softwareproblem und den Kabelverhau reduzieren und skalierbar sein.
Da hast du mit Sicherheit recht.
Wär ja auch spaßig, jede relevante Arbeit von nem kleinen Nano oder sowas machen lassen, quasi "Hardwareobjekte", und das über ein Bussystem zu vernetzen.
(Sowas hatte ich schon mal : ein Raspi mit Python hat kommuniziert (OPCuA)und gerechnet, ein Ardu seriell dran hat eine Delta-HW gesteuert.
Aber Hardware ist aktuell nicht mein Ding, ich tippe grad lieber und gehe auf die Reise durch die neue Welt der Objektorientierung. Den Python-Gedanken hat keiner aufgegriffen : AVR und Python, geht das ?