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


von Peter D. (peda)


Lesenswert?

Hier gibts ja viele Meinungen zu C++, aber wenn man es selber probieren 
will, gibt es große Hürden.
Es gibt kein verstehbares Lehrbuch und wenn man sich anderen Code 
ansieht, dann ist der riesen groß und unübersichtlich und geht überhaupt 
nicht auf MC-spezifische Abläufe ein (IO-Zugriffe, Statemaschine usw.).
Vielleicht kann ja mal ein C++ Guru mir etwas auf die Sprünge helfen.

In C wird ja für jeden Quark eine Funktion benötigt, was nicht besonders 
gut lesbar ist.
In C++ könnte man Zuweisungen nehmen, wenn man nur wüßte, wie man das 
implementiert.
Z.B. mal ganz einfach das Setzen von Ausgängen und Entprellen von 
Eingängen.
Ich möchte gerne folgendes erreichen:
1
  LED0 = KEY0.state;           // an solange Taste gedrückt
2
  LED1.toggle = KEY1.press     // wechsel bei jeder Drückflanke
3
  LED2.off = KEY2.press_short; // kurz drücken - aus
4
  LED2.on = KEY2.press_long;   // lang drücken - an

Geht sowas in C++ und wie könnte eine Implementierung aussehen?

Besonders interessant wäre die Entprellung portweise parallel und nur 
das Auswerten der Tasten einzeln. Also irgendwie, jede Taste ist Teil 
der Klasse entprelle_port, die wiederum für mehre 8Bit-Ports verwendet 
werden kann. Z.B. 16 Tasten an 2 Ports.

von Cyblord -. (cyblord)


Lesenswert?

Irgendwie scheinst du merkwürdige Vorstellungen von C++ zu haben.

Deine Beispiel ist doch nonsense.

Grade bei OOP (was man ja meist will wenn man C++ nimmt) hast du eher 
mehr Funktionen weil du Datenkapselung via getter/setter machst.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Nahezu alles, was man in C machen kann, kann man auch in C++ machen. 
Naja, so gut wie alles. 99 Prozent.

http://stackoverflow.com/questions/1201593/c-subset-of-c-where-not-examples
http://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B

: Bearbeitet durch User
von Ahab (Gast)


Lesenswert?

Wenn man PORT/PINs als Template-Parameter übergeben kann:

Evtl. ein Konstrukt wie:

Entpreller<PINA> KEY0;
Entpreller<PINB> KEY1;


die Entpreller-Klasse enthält dann die Counter etc.


Zum Zugriff dann:

if (KEY0.press.BUTTON1) ...
if (KEY1.long_press.BUTTON4) ...

(mit press/long-Press einfach als Bitfields)

Im Timer muss dann
KEY0.update();
KEY1.update();
aufgerufen werden.

Durch inlining sollte fast dasselbe wie in deinem genialen C-Entpreller 
herauskommen...

von Dr. Sommer (Gast)


Lesenswert?

Die Features von C++ sind hauptsächlich zur Strukturierung & Abstraktion 
von Daten, Verwaltung von Speicher gedacht. Sie erlauben nicht magisch 
eine parallele Verarbeitung, wie du sie durch diese Zeilen

Peter Dannegger schrieb:
> LED0 = KEY0.state;           // an solange Taste gedrückt
>   LED1.toggle = KEY1.press     // wechsel bei jeder Drückflanke
>   LED2.off = KEY2.press_short; // kurz drücken - aus
>   LED2.on = KEY2.press_long;   // lang drücken - an

andeutest. Das sieht mehr nach VHDL oder modellbasierter Programmierung 
ala Simulink als nach einer imperativen Sprache wie C(++) aus. Sicher, 
man könnte Objekte LED0 und KEY0 so anlegen, dass jede dieser 
Anweisungen ein Objekt erzeugt das die angedeutete Funktion erfüllt, 
sich in eine Liste einträgt und dann von einem Timer-Interrupt o.ä. 
verarbeitet wird. Aber parallele deklarative Verarbeitung dieser Art ist 
nicht gerade C++' Kernkompetenz, und es ist fraglich ob man das so 
will... Was viel eher in die imperative Struktur von C(++) passt wäre 
vielleicht so etwas:
1
void TIM1_IRQ () {
2
  LED0.state = KEY0.state;
3
  if (KEY1.state)
4
    LED1.state = !LED1.state;
5
  if (KEY2.pressedTime >= 1000)
6
    LED2.state = true;
7
  else if (KEY2.pressedTime >= 100)
8
    LED2.state = false;
9
}

Mark Brandis schrieb:
> Nahezu alles, was man in C machen kann, kann man auch in C++
> machen.
> Naja, so gut wie alles. 99 Prozent.
Aber auch in Brainfuck. Warum verwendet niemand Brainfuck? Es ist 
supereinfach zu lernen, verwenden und implementieren.

von Peter D. (peda)


Lesenswert?

cyblord ---- schrieb:
> Deine Beispiel ist doch nonsense.

Ich denke aber, es wird klar, was es machen soll.
Ich will mir nicht überall merken müssen, welche Led, Taste an welchem 
Port hängt und welche Polarität und welche Aktion.
Ich will das einmal und nur an einer einzigen Stelle angeben müssen und 
fürderhin kümmert sich C++ darum, das entsprechend aufzulösen.
C kann das ja nicht, da kann ich schreiben:
1
  PORTC |= 1<<PB7;
und es meckert in keinster Weise.

Wenn meine Aufruf-Syntax falsch ist, dann bitte korrigieren.

von Sepp (Gast)


Lesenswert?

Leider nicht.

C++ wird wie C Zeile für Zeile abgearbeitet. Um die dauer des 
Tastendrucks festzustellen benötigt man auch hier eine ISR oder 
zumindest eine periodische Schleife.

Der größte Vorteil von C++ ist das Daten und Funktionen zusammen als ein 
Objekt gespeichert werden. Man spart sich also viele Parameterübergaben 
oder globale Variablen. Wenn das ganze dann noch als "static" definiert 
ist, dann erzeugt ein aktueller C++ Compiler Code der genau so schnell 
ausgeführt wird wie der C Code...

Eine Klasse ist im einfachsten Fall sozusagen eine struct mit Methoden 
drinnen (das kann man auch wirklich so verwenden, wenn man möchte).
Jede Variable die dann den Typ diesen Typ hat wird dann einfach Objekt 
genannt...

Es gibt natürlich die ganzen netten Sachen von Objektorientierter 
Programmierung, wie Vererbung, Polymorphismus usw... Ob man die im 
embedded Bereich sinnvoll verwenden kann hängt sehr stark von der 
Anwendung ab.
Im allgemeinen kann man aber viel portableren und wiederverwertbaren 
Code schreiben wie in C.

Was mit persönlich an C++ im Embedded Bereich sehr gefällt sind die 
Generics (bei C++ Templates genannt). Damit kann man einen großteil der 
statischen DEFINES in einem typeischen C Programm weg bringen und das 
ganze dazu noch kapseln.

z.B. hab ich Filterklassen als Template realisiert denen man bei der 
Instantierung nur die Ordnung und die Charakteristik mitgibt. Der vom 
Compiler erzeugte Code ist dann exakt genau so schnell wie C code. Der 
Vorteil ist das man mit einer Zeile einen neuen Filter hat - ganz ohne 
Code Duplication, globale Variablen, Precompiler DEFINES usw... sauber 
halt.

Es geht natürlich auch mit C genau so gut. Nur halt umständlicher und 
nicht so sauber, aber für nicht C++ affine Programmier halt dafür viel 
verständlicher...

Liebe Grüße,
Sepp

von abcdefg (Gast)


Lesenswert?

Den Syntax welchen du zu errecihen versuchst ist standard C++. Sowas ist 
möglich per "property" implementierung und z.B. Teil von der Borland C++ 
Spracherweiterung. Normales C++ bietet sowas nicht ohne höheren Aufwand.
Hier müsstest du für jeden Member eine subklasse schreiben und den 
"=-Operator" überladen. Möglich ist das, schön aber nicht.
Zu lesen in deiner Fragestellung ist ebenfalls, dass dir sowohl 
Programmier- als auch Systemkenntnisse fehlen, um mit OOP überhaupt 
etwas zu erreichen.
Dann wüsstest du, dass der Syntaktische Zucker eigentlich keinene 
Overhead im Code erzeugt und wie dein gewünschter Syntax auch am PC zu 
implementieren wäre -> denn der Unterschied == 0.

von Peter II (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich denke aber, es wird klar, was es machen soll.
> Ich will mir nicht überall merken müssen, welche Led, Taste an welchem
> Port hängt und welche Polarität und welche Aktion.
> Ich will das einmal und nur an einer einzigen Stelle angeben müssen und
> fürderhin kümmert sich C++ darum, das entsprechend aufzulösen.

dafür macht man sein in paar makros.

sieht mir mir so aus.

#define LED_ROT_PORT PORTD
#define_LED_ROT_PIN PIN1
#define LOD_ROT_INVERT 1

#define TASTER_EIN_PORT PORTD
#define_TASTER_EIN_PIN PIN2
#define TASTER_EIN_INVERT 0

im code kann ich dann über ein paar makros einfach

EIN( LED_ROT );

oder

if ( IS_ON( TASTER_EIN ) ) ..

schreiben. Dafür braucht man doch kein C++.

von Dr. Sommer (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich will das einmal und nur an einer einzigen Stelle angeben müssen und
> fürderhin kümmert sich C++ darum, das entsprechend aufzulösen.
Na das ist ja was völlig anderes. Das ist kein Problem (Beispiel 
anhand STM32F4):
1
class Pin {
2
  public:
3
    constexpr inline Pin (uint8_t iPort, uint8_t iPin) : m_iPort (iPort), m_iPin (iPin) {}
4
    inline bool operator = (bool val) const { ODR () |= (val << m_iPin); }
5
    inline bool operator = (const Pin& p) const { ODR () |= (p.get () << m_iPin); }
6
    inline bool operator get () const { return IDR () & (1 << m_iPin); }
7
  private:
8
    const uint8_t m_iPort, m_iPin;
9
    volatile uint32_t& ODR () { return *reinterpret_cast<volatile uint32_t*> (0x40020000 + (0x400 * m_iPort) + 0x14); }
10
    volatile uint32_t& IDR () { return *reinterpret_cast<volatile uint32_t*> (0x40020000 + (0x400 * m_iPort) + 0x10); }
11
};
12
13
static constexpr Pin LED0 (1, 4); // B4
14
static constexpr Pin KEY0 (0, 3); // A3
15
16
int main () {
17
  LED0 = true;
18
  while (1)
19
    LED0 = KEY0;
20
}
Nicht getestet, wer Fehler findet darf sie behalten...

von Peter D. (peda)


Lesenswert?

Sepp schrieb:
> Um die dauer des
> Tastendrucks festzustellen benötigt man auch hier eine ISR

Das ist klar.
Es ging mir auch haupsächlich darum, wie ich in der Mainloop die 
Ereignisse auswerten kann und wie ich die Klassen definieren muß.

Z.B. ich definiere eine Klasse LED und ein Klasse KEY und darin alle 
Aktionen und Initialisierungen dafür. Die Aktionen benutze ich dann in 
der Mainloop.
Schön wäre es noch, wenn die Klasse automatisch den passenden 
Interrupthandler aktiviert.
Füge ich eine neue Taste der Klasse KEY hinzu, wird geprüft, ob deren 
Port bereits entprellt wird und wenn es ein neuer Port ist, wird der 
Code dafür hinzugefügt.

von Klaus W. (mfgkw)


Lesenswert?

Peter Dannegger schrieb:
> Geht sowas in C++ und wie könnte eine Implementierung aussehen?

Gehen wird sowas schon, aber man muß sicher eine Weile darüber 
nachdenken, damit es sinnvoll wird.

Für deine Zuweisungen könnte ich mir vorstellen, daß Funktionsobjekte 
zugewiesen werden, und die operator=() entsprechend überschrieben 
werden.
Die Funktionsobjekte können dann ggf. mit Parametern wie Frequenzen, 
Parameter zum Entprellen etc. manipuliert werden.
(Ein paar Konstanten zu definieren werden dafür nicht reichen, weil du 
offenbar erreichen willst, daß nach der Zuweisung noch irgendwas im 
Hintergrund weiterläuft).

Für einen kleinen AVR ist das vielleicht etwas viel Code, aber für einen 
dickeren atmega oder einen ARM sollte das machbar sein.
Wird dann sicher nicht jeden Puristen überzeugen (siehe den paralellen 
Thread mit dem C++-Geprügel), aber könnte nett aussehen.

Wenn es dich dann noch juckt, könnte ich übernächstes WE ein Grundgerüst 
als Vorschlag machen - macht bestimmt Spaß.

von FH-Student (Gast)


Lesenswert?


von Cyblord -. (cyblord)


Lesenswert?

FH-Student schrieb:
> cyblord ---- schrieb:
>> via getter/setter machst
>
> Alt aber nach wie vor aktuell:
>
> 
http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html

Der Tag mag kommen an dem FH-Studenten mir Nachhilfe in 
Programmierkonzepten erteilen, aber heute ist das sicher noch nicht ;-)

Die Bedenken sind in der Tat alt und wohl bekannt. Und auch korrekt. 
Trotzdem erhält man im OOP Umfeld Daten mittels Funktionen und übergibt 
Daten mittels Funktionen.(Das man dies nicht durch sture getter/setter 
sondern auf höherer Abstraktionsebene machen sollte, das stimmt 
natürlich).

Was ich damit eigentlich nur sagen wollte, dass eben bei OOP eher MEHR 
Funktionsaufrufe im Code stehen als bei reinem C. Peter meinte ja das 
Gegenteil.

von Mal Luna-AVR testen (Gast)


Lesenswert?

Auschnitt aus der Luna Beschreibung:

"Luna ... bietet im Gegensatz zu einfacheren Basic-Sprachen, wie z.Bsp. 
BASCOM, wesentlich komplexere Möglichkeiten auf technischer Augenhöhe 
mit Pascal, Modula und C/C++."

http://avr.myluna.de/doku.php?id=de:about

Vielleicht ist das ja was für den TO?

von Peter D. (peda)


Lesenswert?

Ich meine, die Syntax "name.action" schon recht häufig gesehen zu haben.

Wichtiger ist mir aber, daß ich Schreibarbeit und damit 
Fehlerträchtigkeit spare.
In plain C muß ich zu jeder LED auch das Richtungsbit definieren, bzw. 
zu jeder Taste das Pullup-Bit.
Daher dachte ich, wenn ich eine Klasse definiere und darin die LED als 
Member, wird für jedes Member automatisch die Initialisierung mit 
aufgerufen.
Und wenn ich im Main eine Member-Aktion mache, wird der entsprechende 
Code der Klasse ausgeführt.
Damit sollte exakt der gleiche Code entstehen, wie in plain C, also kein 
Mehrverbrauch.

von Ahab (Gast)


Lesenswert?

cyblord ---- schrieb:
> Was ich damit eigentlich nur sagen wollte, dass eben bei OOP eher MEHR
> Funktionsaufrufe im Code stehen als bei reinem C.

Im Quelltext lassen sich aber viele Funktionsaufrufe verstecken.

Copy-Construktoren, conversion-operatoren, andere overloads, 
destruktoren, ...
1
{
2
  MeinObjekt x=y; // Versteckter "funktions"-Aufruf Copy-Construktor
3
  x++; // Versteckter Aufruf "++"-Operator
4
  LED0 = x; // z.B. versteckter Aufruf "bool"-Operator von X, versteckter Aufruf "bool"-Zuweisung von "LED"  
5
} // Versteckter Aufruf Destruktor

Also: Weniger Funktionsaufrufe im Quelltext sichtbar, obwohl evtl. mehr 
tatsächlich ausgeführt werden.

von Rolf M. (rmagnus)


Lesenswert?

Peter Dannegger schrieb:
> In plain C muß ich zu jeder LED auch das Richtungsbit definieren, bzw.
> zu jeder Taste das Pullup-Bit.
> Daher dachte ich, wenn ich eine Klasse definiere und darin die LED als
> Member, wird für jedes Member automatisch die Initialisierung mit
> aufgerufen.

Woher soll die Klasse denn wissen, wie du es eingestellt haben willst, 
wenn du das nicht explizit hinschreibst?

von Falk B. (falk)


Lesenswert?

@ Peter Dannegger (peda)

>Ich meine, die Syntax "name.action" schon recht häufig gesehen zu haben.

>Wichtiger ist mir aber, daß ich Schreibarbeit und damit
>Fehlerträchtigkeit spare.

Vielleicht ist dann Arduino dar Richtige für dich?

SCNR
Falk

von BastiDerBastler (Gast)


Lesenswert?

Ich denke hier versteht gerade niemand niemanden. Ich kann das Ziel im 
OP noch nicht so ganz deuten und vor allem kommt es mir so vor, dass in 
den Folgeposts was ganz anderes gemeint ist. Hier besteht 
Aufkärungsbedarf!

von Nils (Gast)


Lesenswert?

code.google.com/p/savr

Geht das in die richtige Richtung? Kann man zumindest mal als Grundlage 
nehmen.

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Woher soll die Klasse denn wissen, wie du es eingestellt haben willst,
> wenn du das nicht explizit hinschreibst?

Ich hatte gedacht, daß eine Klasse Code enthalten kann, der dann für 
jedes Member aufgerufen wird.
Ich habe also ein Objekt vom Typ LED und dafür definiere ich die 
Initialisierungen und Aktionen in der Klasse LED als Code.

von Peter D. (peda)


Lesenswert?

BastiDerBastler schrieb:
> Ich kann das Ziel im
> OP noch nicht so ganz deuten

Ich möchte z.B. LEDs und Tasten als Objekte behandeln und dafür nur 
bestimmte Aktionen zulassen bzw. vorbelegen.
Es kann sein, daß OOP primär was völlig anderes im Sinn hat, aber ich 
hatte den Eindruck, daß es mit C++ möglich wäre.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:

> Z.B. ich definiere eine Klasse LED und ein Klasse KEY und darin alle
> Aktionen und Initialisierungen dafür.

Das kannst du natürlich schon
1
class LED
2
{
3
public:
4
  LED( volatile uint8_t* Port, uint8_t Bit )
5
  : Port_( Port ), Bit_( Bit )
6
  {  DDR_From_Port( Port ) |= ( 1 << Bit_ ); }
7
8
  void On()  { Port_ &= ~( 1 << Bit_ ); }
9
  void Off() { Port_ |=  ( 1 << Bit_ ); }
10
11
private:
12
  volatile uint8_t* const Port_;
13
  const uint8_t           Bit_;
14
};
15
16
17
int main()
18
{
19
  LED ErrorLed( PORTB, PB0 );
20
21
  ErrorLed.On();
22
  ErrorLed.Off();
23
}



Mit den Tasten ist das dann nicht mehr ganz so einfach. Da müsste es 
wohl einen übergeordneten Koordinator geben, der sich um den Timer bzw. 
die ISR kümmert und die KEY Objekte da einschleust.


> Füge ich eine neue Taste der Klasse KEY hinzu

Du hast da eine Konzeptdiskrepanz. Eine Klasse ist der Bauplan. Die KEY 
Klasse beschreibt daher eine Taste. Erst eine übergeordnete Klasse, 
nennen wir sie mal Keyboard, hat dann die Fähigkeit mehere Keys zu 
verwalten. Klassen willst du nicht wirklich ändern, nur weil du in 
deiner Anwendung mehrere Objekte davon hast.

: Bearbeitet durch User
von FelixW (Gast)


Lesenswert?

Hallo Peter,

Peter Dannegger schrieb:
> Daher dachte ich, wenn ich eine Klasse definiere und darin die LED als
> Member, wird für jedes Member automatisch die Initialisierung mit
> aufgerufen.
> Und wenn ich im Main eine Member-Aktion mache, wird der entsprechende
> Code der Klasse ausgeführt.
> Damit sollte exakt der gleiche Code entstehen, wie in plain C, also kein
> Mehrverbrauch.

das macht man auch so. Etwas Mehrverbrauch entsteht je nach Compiler. 
(schon alleine, da jedes Bit jeder LED einzeln initialisiert wird)
In C++ würde ich das mit Templates umsetzen. Jede LED bekommt damit eine 
eigene Klasse. Dann braucht man keine Variable die Pin und Port 
speichert und entsprechend viel RAM belegt.

Led<PortA, 7> Led_gruen;
Led_gruen.An(); // oder mit Operatorüberladung Led_gruen = true;

Die Template Parameter verhalten sich ähnlich wie ein Macro, werden also 
beim Compilieren in den Quellcode eingefügt. Den Rest macht die 
Optimierung.

Wenn du jetzt einen ganzen Port entprellen möchtest und einzeln 
auslesen:
Male ein Diagramm, was für Objekte du hast und was diese tun sollen. Das 
ist OOP und hat nichts mit C++ zu tun. C++ macht es dir nur einfacher ;)

Grüße Felix

von Karl H. (kbuchegg)


Lesenswert?

FelixW schrieb:

> In C++ würde ich das mit Templates umsetzen. Jede LED bekommt damit eine
> eigene Klasse. Dann braucht man keine Variable die Pin und Port
> speichert und entsprechend viel RAM belegt.

Wenn man alles const macht, kann man darauf spekulieren, dass das alles 
wieder wegoptimiert wird.

Ganz im Gegenteil würde ich als einen der Bausteine eines C++ Frameworks 
ein Template ansehen, welches die Kombination Port+Pin kapselt. Das wäre 
so ziemlich die unterste Ebene und wäre recht nützlich um genau diese 
Einheit an andere Klassen weiterzugeben, wie zb ein LCD, dem man 
mitteilt auf welchen 'Anschlüssen' seine Steuerleitungen sitzen. So 
wenig ich mich mit der Arduino Philosophie der durchnummerierten 
Anschlüsse auch anfreunden kann, eines muss man doch zugeben: diese 
Nummerierung vereinfacht vieles.

von Peter D. (peda)


Lesenswert?

Karl Heinz schrieb:
> Das kannst du natürlich schon
> class LED

Danke, das sieht schon sehr gut aus.
Wo gibt es das DDR_From_Port()?

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:

>> class LED
>
> Danke, das sieht schon sehr gut aus.
> Wo gibt es das DDR_From_Port()?

Das hab ich gerade eben erst erfunden :-)
Und es ist das leidige Problem, zuverlässig aus der Portangabe die DDR 
Adresse zu ermitteln.

Wie ich an Felix auch schon geschrieben habe. Ich denke ein wirklich 
nicht unwesentlicher Punkt wäre es, für diese Low-Level Sachen ein gut 
aufgebautes Framework zu haben, welches diese ganzen Port, Pin, DDR 
Sachen sauber kapselt OHNE dabei Codemässig stark aufzutragen. Gedanken 
hab ich mir dazu allerdings noch nie gemacht, so dass ich da auch nichts 
aus dem Ärmel schütteln kann.

: Bearbeitet durch User
von FelixW (Gast)


Lesenswert?

Karl Heinz schrieb:
> Ganz im Gegenteil würde ich als einen der Bausteine eines C++ Frameworks
> ein Template ansehen, welches die Kombination Port+Pin kapselt.

rechtgeb

Aber ein Framework ist Schritt n+1 ... Peter erst einmal anfangen 
lassen.

@Karl Heinz,
das mit der Wegoptimierung der const Variablen habe ich noch nicht 
ausprobiert, spekulieren will ich nicht. Ich verstehe davon zu wenig 
wie/was der Compiler optimieren kann.

Grüße Felix

von Peter D. (peda)


Lesenswert?

Karl Heinz schrieb:
> Das hab ich gerade eben erst erfunden :-)

Vielleicht sollte man nur A,B,C usw. übergeben und dann PORT##A, DDR##A, 
PIN##A draus basteln.

von Karl H. (kbuchegg)


Lesenswert?

FelixW schrieb:
> Karl Heinz schrieb:
>> Ganz im Gegenteil würde ich als einen der Bausteine eines C++ Frameworks
>> ein Template ansehen, welches die Kombination Port+Pin kapselt.
>
> *rechtgeb*
>
> Aber ein Framework ist Schritt n+1 ...

Ja, ok. Ersetz Framework durch einen anderen Begriff. Der passt wirklich 
nicht besonders.

Man bräuchte eine einfache Möglichkeit einen physikalischen Pin in 
seinen Eigenschaften zu beschreiben. Welches Port Register? Welches 
DDR-Register? Welches Bit?

Klar kann man einem Pin Template zb das DDR Register mitgeben. Aber das 
find ich persönlich recht unelegant. Im besten Fall sag ich dem Teil 
einfach nur "Am Port B, und dort das Bit x" und damit muss bereits alles 
klar sein.

Edit:
Ich würde das gerne So benutzen können
1
int main()
2
  Pin<PortB, 3> LedPin;
3
  LED ErrorLed( LedPin );

oder dann natürlich auch
1
int main()
2
{
3
  LED ErrorLed( Pin<PortB, 3> );

wird klar, wo ich gerne hin möchte?
Mir gehts momentan nur darum, der LED zu beschreiben, wo ihr Anschluss 
liegt. Und in dieser Beschreibung soll alles enthalten sein, so dass ich 
die Pin Klasse auch um das korrekte DDR Register befragen kann.

: Bearbeitet durch User
von Bronco (Gast)


Lesenswert?

Hallo Peter,

ich bin mir nicht ganz sicher, ob ich Dich richtig verstehe, aber ich 
meine, Du hast da ein Verständnisproblem:

- Du könntest eine Klasse led_t anlegen.
1
class let_t {...};

- Und könntest eine Instanz der Klasse anlegen.
1
let_t LED4;  // Hängt an Port2.6

Aber:
Um den Port2.6 zu bedienen, muss in die Instanz (!!!) "LED4" das 
Wissen hinein, dass diese LED an Port2.6 hängt. Dieses Wissen kannst Du 
nicht von vornherein in "led_t" hineinpacken, denn sonst würde "led_t" 
ja nur für den Port2.6 funktionieren.

Dies wäre z.B. so möglich:
1
class let_t 
2
{
3
public:
4
  void Configure(uint8_t portnum_u8, uint8_t pinnum_u8)
5
  { portnummer_u8 = portnum_u8; pinnnummer_u8 = pinnum_u8; }
6
private:
7
  uint8_t portnummer_u8;
8
  uint8_t pinnnummer_u8;
9
};
Nach dem Instanziieren von "LED4" mußt Du dann die Instanz 
konfigurieren:
1
let_t LED4;  // Hängt an Port2.6
2
LED4.Configure(2, 6);

Das ist aber nur eine Möglichkeit von vielen. Man könnte auch die 
Konfiguration direkt im Constructor übergeben etc.

Hoffe, das hilft.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> oder dann natürlich auch
>
1
> int main()
2
> {
3
>   LED ErrorLed( Pin<PortB, 3> );
4
>
>
> wird klar, wo ich gerne hin möchte?
> Mir gehts momentan nur darum, der LED zu beschreiben, wo ihr Anschluss
> liegt. Und in dieser Beschreibung soll alles enthalten sein, so dass ich
> die Pin Klasse auch um das korrekte DDR Register befragen kann.

Dann könnte die LED Klasse wiederrum so aussehen
1
class LED
2
{
3
public;
4
  LED( const Pin& pin )
5
  : pin_( pin )
6
  { pin_.DDr() |= pin_.bitmask() }
7
8
  void On()  { pin_.port() |= pin_.bitmask(); }
9
  void Off() { pin_.port() &= ~pin.bitmask(); }
10
11
private:
12
  const Pin pin_;
13
};

oder überhaupt gleich das Setzen bzw. Löschen in das Pin Template 
verfrachten
1
class LED
2
{
3
public;
4
  LED( const Pin& pin )
5
  : pin_( pin )
6
  { pin_.toOutput(); }
7
8
  void On()  { pin_.setZero(); }
9
  void Off() { pin_.setOne(); }
10
11
private:
12
  const Pin pin_;
13
};

insbesonders letzteres sieht doch schon recht ordentlich aus.


Edit: hab noch ein paar const bzw. die Referenz im Argument nachgetragen

: Bearbeitet durch User
von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Auweia.. Angriff der C++ Fetischisten Teil II. Da kann ich einfach nicht 
anders als meinen Senf dazu geben.

Also erst einmal zur Entzauberung: C++ und OPP ist mit Sicherheit nicht 
schwerer als C oder gar Assembler zu programmieren. Im Gegenteil. Aber 
offensichtlich diesem Mythos erlegen glauben einige dieses 
Sprachkonstruckt nun für alles verwenden zu müssen ob’s nun Sinn macht, 
oder nicht. Ist ja im Prinzip auch legitim. Am Ende kommt eh Assembler 
bzw. Maschinencode heraus. Die einen können den selber Programmieren und 
beeinflussen wie er was macht, die anderen schreiben lieber vorher 
Romane und überlassen die Arbeit letzten Endes denen die den Compiler 
programmiert haben.

Die Verwendung von C++ auf kleinen µC ist meiner Meinung nach aber 
Nonsens –und ich schreibe Nonsens und nicht Verbrechen- weil es 
keinerlei  Vorteil bringt. Es sei man glaubt die Mär vom kompakteren und 
schnelleren Code. Dies kann aber eigentlich nur  ein Grenzdebiler 
glauben. Hinzu kommt bei den ausgewiesenen Hochsprachen mit starker 
Abstrahierung dass der ganze Misst nicht mehr vernünftig zu debuggen 
ist. Und das muss jeder der hardwarenah programmiert eben tun und damit 
meine ich nicht den eigenen Prozessor im Focus habend, sondern die immer 
wieder neue und unbekannte periphere HW mit der ein µC Entwickler zu tun 
hat und der vor der Aufgabe steht anhand eines Datenblattes seinen Code 
anzupassen.

Ich Entwickle seit über 10 Jahren für OSX und iOS und davor für Windows. 
Für solche Systeme führt fast kein Weg an C++ bzw. den 
Weiterentwicklungen C“#“ und „Objectice C“ vorbei. Hier stellt quasi OOP 
mit all seinen Vorteilen und den >>bereits vorhandenen<< mächtigen und 
kaum zu überschauenden Klassen und Schnittstellen die einzige 
Möglichkeit dar überhaupt noch mit einem so komplexen System zu 
kommunizieren und dessen Funktionen zu verwenden. Dafür ist diese 
Sprache das einzige Mittel.

Gäbe es für alle Mikrocontroller und jede aktuelle mögliche periphere 
Hardware  frei verfügbare und funktionierende Klassen und einem Compiler 
der C++ fehlerfrei unterstützt zuzüglich einer funktionierenden 
Möglichkeit adäquat zu debuggen dann sähe die Sache für den einen oder 
anderen Fall möglicherweise etwas anders aus.  Denn dann wäre wie bei 
den „Hochbetriebssystemen“,  OOP eine Erleichterung und würde seine 
eigentliche Stärke ausspielen. Das tut es aber nicht und wird es 
vermutlich nie geben. Aus vielen verschiedenen und offensichtlichen 
Gründen.  Und unter den gegebenen Voraussetzungen ist C++ ein Hemmschuh. 
Weil das erstellen, Testen  und optimieren der Klassen erst einmal 
einfach wahnsinnig viel Mehrarbeit ist.

Kleine Ausnahme: Die kleine Auswahl an „Arduino Kontrollern für die 
einige pfiffige ein paar nützliche Kassen geschrieben haben, damit auch 
Kid’s und Künstler programmieren können. Sketch LED Blinken: 1KB, Sketch 
ADC einlesen und UART ausgeben 4KB, weiter auf die Performance muss ich 
an dieser stelle wohl kaum eingehen.

Wer also zur Selbstbestätigung seiner intellektuellen Omnipotenz 
trotzdem gerne C++ benutzt um kleine µC zu programmieren, dem wünsche 
ich einfach viel Freude an seiner Arbeit oder wie in den meisten Fällen 
hier an seinem Hobby. Es reicht ja auch oft einfach den C++ Compiler zu 
benutzen und trotzdem plain C zu  schreiben. Mutti merkt’s nicht.

von Klaus W. (mfgkw)


Lesenswert?

Thomas Holmes schrieb:
> Da kann ich einfach nicht
> anders als meinen Senf dazu geben.

Danke, wäre aber nicht nötig gewesen - es hilft leider nicht, die Frage 
zu beantworten.

Peter Dannegger ist wohl alt genug, zu wissen warum er seine Frage 
stellt.

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Ich werd verrückt.
Ich hab mal einfach was zusammengebraut.
Siehe Cpp-File.

Und vor allen Dingen: Siehe LSS File!
1
0000005e <main>:
2
public:
3
  Pin( const PortDesc& port, uint8_t pin )
4
  : port_( port ), pin_( pin )
5
  {}
6
7
  void toOutput() { *port_.DdrReg_ |= ( 1 << pin_ ); }
8
  5e:  bb 9a         sbi  0x17, 3  ; 23
9
  void setZero()  { *port_.PortReg_ &= ~( 1 << pin_ ); }
10
  60:  c3 98         cbi  0x18, 3  ; 24
11
  62:  ff cf         rjmp  .-2        ; 0x62 <main+0x4>

Da kann man doch wirklich nicht meckern!

Edit:
Für alle Nörgler: Natürlich bleibt das nicht so. Die einzelnen Klassen 
kommen natürlich in ihre Header Files, etc.
Im Endeffekt schreibt man dann nur
1
#include "Led.h"
2
3
int main(void)
4
{
5
  LED ErrorLed( Pin( PortB, 3 ) );
6
7
  ErrorLed.On();
8
9
  while(1)
10
  {
11
  }
12
}

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Peter Dannegger schrieb:

> Geht sowas in C++ und wie könnte eine Implementierung aussehen?

Ich vermute, Du bist definitiv auf C++ festgelegt?
Ansonsten könnte man - völlig unabhängig von der
Implementierungssrache - gewisse Anleihen bei der
SPS-Technik aufnehmen.

von Karl H. (kbuchegg)


Lesenswert?

Ich denke, man muss PeDa nicht wirklich in Dingen Softwaretechniken 
beraten. Der hat mehr als genug auf dem Kasten, um das selbst 
einschätzen zu können.
Wenn PeDa nach C++ und seinen Möglichkeiten fragt, dann nur aus einem 
Grund: weil er genau wie wir alle in der Situation ist, dass es 
bestimmte Fehler in der AVR Programmierung gibt, die einfach nur lästig 
sind. Die Kombination aus Register und zugehörigem Bit für eine 
bestimmte Funktionalität ist einfach eine fehleranfällige Sache. Je 
weiter man die aus der Anwendungsprogrammierung rauskriegt, umso besser.
Auch ist das zusammenkopieren von Codeschnipseln in einem neuen Projekt 
eine lästige Arbeit. Auch wenn die PeDa Entprellung in 5 Minuten 
eingebaut ist, so ist es doch eine manuelle Arbeit und als solche 
fehleranfällig.
PeDa geht es sicher nicht um Code-Techniken sondern darum, wie er sich 
in der Entwicklung das Leben leichter machen kann, indem er die 
Programmiersprache soweit ausreizt, dass sie ihm Fehlerfälle nach 
Möglichkeit abfangen kann. Und sei es nur, dass er die falsche Pinnummer 
an eine Funktion übergibt.

von kopfkratzer (Gast)


Lesenswert?

Karl Heinz schrieb:
> Ich denke, man muss PeDa nicht wirklich in Dingen
> Softwaretechniken
> beraten. Der hat mehr als genug auf dem Kasten, um das selbst
> einschätzen zu können.
> Wenn PeDa nach C++ und seinen Möglichkeiten fragt, dann nur aus einem
> Grund: weil er genau wie wir alle in der Situation ist, dass es
> bestimmte Fehler in der AVR Programmierung gibt, die einfach nur lästig
> sind. Die Kombination aus Register und zugehörigem Bit für eine
> bestimmte Funktionalität ist einfach eine fehleranfällige Sache. Je
> weiter man die aus der Anwendungsprogrammierung rauskriegt, umso besser.
> Auch ist das zusammenkopieren von Codeschnipseln in einem neuen Projekt
> eine lästige Arbeit. Auch wenn die PeDa Entprellung in 5 Minuten
> eingebaut ist, so ist es doch eine manuelle Arbeit und als solche
> fehleranfällig.
> PeDa geht es sicher nicht um Code-Techniken sondern darum, wie er sich
> in der Entwicklung das Leben leichter machen kann, indem er die
> Programmiersprache soweit ausreizt, dass sie ihm Fehlerfälle nach
> Möglichkeit abfangen kann. Und sei es nur, dass er die falsche Pinnummer
> an eine Funktion übergibt.

kopfkratz
Also wenn er seine Entprellung in C++ umsetzen möchte sollte er das 
ganze nicht nur kapseln sondern auch z.B. mit überladenen Operatoren 
arbeiten und für den Fehlerfall eine Exception werfen.
Um die Deklaration der Hardware kommt man damit allerdings nicht herum.
Da wäre ein Ansatz sich in einem .h File genormte Spezifikationen zu 
setzen, so wie das ja im AVR-GCC auch gemacht wird.
Die Kardinalfrage ist halt ob es einen Sinn ergibt bei kleinen µCs mit 
wenig Ressourcen C++ einzusetzen und sich damit einige Byte oder 
Kilobyte an "Overhead" einzuhandeln.
Von der teilweisen schlechten Umsetzung der jeweiligen Compiler mal 
abgesehen ...

von Rolf M. (rmagnus)


Lesenswert?

Karl Heinz schrieb:
>> Wo gibt es das DDR_From_Port()?
>
> Das hab ich gerade eben erst erfunden :-)
> Und es ist das leidige Problem, zuverlässig aus der Portangabe die DDR
> Adresse zu ermitteln.

Gab es da nicht von Atmel zu den AVR-Typen xml-Files, die die 
beschreiben, aus denen dann die Include-Files für C und Assembler 
generiert werden? Das könnte man doch für C++ genauso machen.

von chris_ (Gast)


Lesenswert?

Thomas Holmes (Firma: CIA) (apostel13) schrieb:
>Kleine Ausnahme: Die kleine Auswahl an „Arduino Kontrollern für die
>einige pfiffige ein paar nützliche Kassen geschrieben haben, damit auch
>Kid’s und Künstler programmieren können. Sketch LED Blinken: 1KB, Sketch
>ADC einlesen und UART ausgeben 4KB, weiter auf die Performance muss ich
>an dieser stelle wohl kaum eingehen.

Na dann guck Dir mal diese Lib an

http://sensorium.github.io/Mozzi/

mir scheint die Realisierung in C++ ganz nützlich. Performant ist es auf 
jeden Fall programmiert. Auiosignalerzeugung auf einem 8Bit Controller 
ist nicht so einfach.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

chris_ schrieb:
> Na dann guck Dir mal diese Lib an
>
> http://sensorium.github.io/Mozzi/

Kenne ich udd es unterstreicht das was ich geschrieben habe.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Klaus Wachtler schrieb:
> Peter Dannegger ist wohl alt genug, zu wissen warum er seine Frage
> stellt.

An Rande:


Seine Frage im übrigen hat mich sehr erfreut. Zum einen weil sie etwas 
über C++ aussagt und zum anderen natürlich weil es bald auch endlich 
eine Endprellungsversion in C++ gibt, nachdem diese in Assembler und C 
ja bereits schon jedem zur Verfügung steht.

Wobei es das eigentlich schon gibt. Die Arduinogemeinde hat etliche 
Button Klassen hervorgebracht. Eineige mit und einige ohne Entprellung.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Karl Heinz schrieb:
> Ich denke, man muss PeDa nicht wirklich in Dingen Softwaretechniken
> beraten. Der hat mehr als genug auf dem Kasten, um das selbst
> einschätzen zu können.
> Wenn PeDa nach C++ und seinen Möglichkeiten fragt, dann nur aus einem
> Grund:

Offensichtlich hat er sogar einen Pressesprecher. Alle Achtung!

:-)

von Heiliger Bimbam (Gast)


Lesenswert?

Ist Euch die Programmierung in den gängigen Sprachen noch nicht
kompliziert genug?
Muß man unbedingt einen Elefanten auf eine Mücke setzen?

von C+++ (Gast)


Lesenswert?

Heiliger Bimbam schrieb:
> Muß man unbedingt einen Elefanten auf eine Mücke setzen?

Bitte diese Diskussion "nebenan" führen:

Beitrag "Richtiges C++ hardwarenah"

Hier sind nur die, die C++ auf AVR für eine gute Idee halten. :)

von Peter D. (peda)


Lesenswert?

Heiliger Bimbam schrieb:
> Ist Euch die Programmierung in den gängigen Sprachen noch nicht
> kompliziert genug?

Karl Heinz hat das ganz richtig erkannt, es geht mir vorrangig darum, 
das Programmieren einfacher und sicherer zu machen.

Z.B. stört mich auch beim plain C, daß es keine Typprüfung für Enums 
gibt. Man kann ganz leicht ein Enum in der falschen Statemaschine 
verwenden und der Compiler lacht sich ins Fäustchen.

Und wie es aussieht, erzeugt C++ keinen Overhead, wenn die Argumente 
schon zur Compilezeit bekannt sind.
Sind sie es nicht, dann hat man auch unter plain C mit Macros oder 
Inlinefunktionen den Overhead.

Und C++ ist ja schon im AVR-GCC includiert, man braucht also an der 
Programmierumgebung nichts zu ändern. Einfach nur *.c nach *.C 
umbenennen.

: Bearbeitet durch User
von C+++ (Gast)


Lesenswert?

Karl Heinz schrieb:
> Ich werd verrückt.
> Ich hab mal einfach was zusammengebraut.
> Siehe Cpp-File.

Wow. Das schaut wirklich so aus, als könnte man auf die Weise ein 
"Arduino in Gut" basteln.

Für eine effiziente LCD-Lib bräuchte man als Grundlage noch ein 
"Multi-Bit-GPIO" für den Datenbus mit integrierter Maskierung, und ggfs 
__builtin_avr_insert_bits zum Umsortieren der Datenleitungen.

Dann ein paar ähnliche (aber umfangreichere) Konstrukte für TWI, SPI, 
UART usw.

Für Timer (eine der ganz großen Arduino-Schwachstellen IMHO) fällt mir 
grad keine vernünftige Lösung ohne Virtuelle Methoden etc. ein. Evtl 
kriegt man mit Templates was gebastelt?

Für Tastenentprellung könnte vmtl. das atomic/locking ein Problem 
werden?

von Peter D. (peda)


Lesenswert?

C+++ schrieb:
> Für Tastenentprellung könnte vmtl. das atomic/locking ein Problem
> werden?

Funktioniert die atomic.h unter C++ nicht mehr?

von tictactoe (Gast)


Angehängte Dateien:

Lesenswert?

Karl Heinz schrieb:
> Ich hab mal einfach was zusammengebraut.

Ich hab' mal meine port.h angehängt. Verwendet wird's ungefähr so:
1
#include "port.h"
2
3
typedef Pin<PortB, PB0> PinLED;
4
typedef Pin<PortD, PD3> PinTaste;
5
6
int main()
7
{
8
  PinTaste::SetPullUp();
9
  if (PinTaste::IsSet())
10
    PinLED::Set();
11
}
Nachteil meiner Implementierung: Sie verwendet reinterpret_cast<>, um 
von der Port-Adresse eine Integer-Konstante für ein Template-Argument zu 
machen, was der GCC nur gnadenhalber als Konstante interpretiert; streng 
genommen ist es nicht C++-konform.

Ich habe davor auch mit deiner Variante gespielt, da habe ich aber 
attribute((force_inline)) benötigt, um GCC das Inlinen schmackhaft zu 
machen.

von Nop (Gast)


Lesenswert?

Könnte der unterschiedliche Datentyp durch das Template nicht evtl zum 
Problem werden?
Wenn ich z.B an meine LCD Klasse die Pins als Objekte übergebe, wie 
könnte man das dann einheitlich definieren?
Interface?

von Karl H. (kbuchegg)


Lesenswert?

tictactoe schrieb:

> Ich habe davor auch mit deiner Variante gespielt, da habe ich aber
> attribute((force_inline)) benötigt, um GCC das Inlinen schmackhaft zu
> machen.

Das was mich am allermeisten verblüfft hat. Ich hatte eigentlich damit 
gerechnet, dass ich da noch ein paar const mit einbauen müsste. 
Zumindest hatte ich damit gerechnet, in der Hauptschleife irgendeine 
in-or-out Sequenz zu sehen. Das da direkt ein sbi bzw. cbi auftaucht, 
damit hab ich nicht gerechnet.

Aber in deinem Header File hab ich mir noch ein paar andere Dinge 
abgeschaut, bei denen ich mir auf die Stirne klopfe und mir denke: Mann, 
so simpel und ich komm nicht drauf.

> Für eine effiziente LCD-Lib bräuchte man als Grundlage noch ein
> "Multi-Bit-GPIO" für den Datenbus mit integrierter Maskierung,
> und ggfs __builtin_avr_insert_bits zum Umsortieren der Datenleitungen

Ich hatte in Gedanken schon mit einer Nibble Klasse gespielt. Aber noch 
ist mir nicht klar, wie ich die Fälle "Alle 4 Leitungen schön 
regelmässig angeordnet" versus "komplettes Durcheinander" im Code so 
trennen kann (je nach Konstruktor Aufruf), dass dann nur der Code Teil 
übrig bleibt, der wirklich gebraucht wird. P-Fleury hat zwar eine Lösung 
in Form einer #ifdef Origie, aber so richtig happy bin ich damit nicht. 
Das müsste auch eleganter gehen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

C+++ schrieb:

> Für Timer (eine der ganz großen Arduino-Schwachstellen IMHO) fällt mir
> grad keine vernünftige Lösung ohne Virtuelle Methoden etc. ein. Evtl
> kriegt man mit Templates was gebastelt?

Da hab ich auch noch keine Idee dazu. Mit virtuellen Funktionen könnt 
ich (für mich) sogar leben. Da bin ich nicht mehr der Takt-Zähler.

Templates, denke ich, werden dir da nicht helfen. Denn Timer leben 
davon, dass ich Funktionalität in die ISR einbringen kann. Das können ja 
auch mehrere Funktionale Einheiten sein. Gleichzeitig in der ISR 
entprellen und eine Uhr treiben, wobei Entpreller und Uhr 
unterschiedliche Klassen sind - ich seh da keinen anderen Ausweg als 
virtuelle Funktionen.

: Bearbeitet durch User
von tictactoe (Gast)


Lesenswert?

Nop schrieb:
> Könnte der unterschiedliche Datentyp durch das Template nicht evtl zum
> Problem werden?
> Wenn ich z.B an meine LCD Klasse die Pins als Objekte übergebe, wie
> könnte man das dann einheitlich definieren?
> Interface?

Du definierst deine Klasse als Template:

template<class CLOCK_PIN, class DATA_PIN>
class LCD { ... };

Eine Klasse macht man sich ja, weil man irgendwelchen State behalten 
will. In der LCD-Klasse wäre z.B. eine Cursor-Position als State 
geeignet. Aber die Pins sind kein State: Die werden sich in deiner 
Anwendung nie nicht ändern und sind deshalb als (konstante) 
Template-Argumente geeignet.

von Karl H. (kbuchegg)


Lesenswert?

tictactoe schrieb:
> Nop schrieb:
>> Könnte der unterschiedliche Datentyp durch das Template nicht evtl zum
>> Problem werden?
>> Wenn ich z.B an meine LCD Klasse die Pins als Objekte übergebe, wie
>> könnte man das dann einheitlich definieren?
>> Interface?
>
> Du definierst deine Klasse als Template:
>
> template<class CLOCK_PIN, class DATA_PIN>
> class LCD { ... };

Ich seh schon.
Ich muss auf meine alten Tage doch noch lernen mit Templates umzugehen. 
Bisher hab ich mich ja erfolgreich davor gedrückt.

von Nop (Gast)


Lesenswert?

Hm, ich sehe schon( :-) )... die Template Geschichte reicht sich dann 
immer weiter hoch.

Aber für die Nibbel Geschichte von K.H. ist ja ein Template optimal.

von Ret (Gast)


Lesenswert?

Warum tut man sich das denn eigentlich an? Warum erdenkt man sich 
kompliziert zu lesende C++ Gebilde für etwas was andere Sprachen viel 
einfacher und damit im Lernaufwand schneller lösen können? Warum nicht 
gleich mit einer speziell zugeschnittenen Sprache wie hier

http://avr.myluna.de/doku.php?id=de:features

sich genau den Teil der Objektorientiertheit herauspicken, den es auch 
wirklich braucht?

Wozu muss man die Dinge nur immer unnötig verkomplizieren? 
Verkomplizierung bringt neue Fehleranfälligkeit durch 
Verständnisprobleme bei unnötig komplizierten Ausdrücken.

Warum also macht man das? Um sich anschließend selbst zu beweihräuchern? 
ICH kann C++ SOGAR auf MC und DU dümpelst mit deiner "schlichten Sprache 
für JEDERMANN" nur im allgemeinen Fahrwasser? Ich bin "die Elite", DU 
hingegen bist "der Depp", der nur das kann, was jeder andere gut lernen 
und damit umsetzen kann?

Wo ist der wirkliche MEHRWERT? Ist das Endergebnis wirklich besser? 
Lohnt der Aufwand wirklich?

von tictactoe (Gast)


Lesenswert?

Karl Heinz schrieb:
> Templates, denke ich, werden dir da nicht helfen. Denn Timer leben
> davon, dass ich Funktionalität in die ISR einbringen kann. Das können ja
> auch mehrere Funktionale Einheiten sein. Gleichzeitig in der ISR
> entprellen und eine Uhr treiben, wobei Entpreller und Uhr
> unterschiedliche Klassen sind - ich seh da keinen anderen Ausweg als
> virtuelle Funktionen.

Wie machst du dem Interrupt-Handler eine variable Anzahl von Clienten 
bekannt? Hmm... Halt! Variable Anzahl? Die gibt's bestimmt nicht, denn 
du hast nur eine konstante Anzahl von Tastern und Uhren an deinem 
Controller hängen.

Dann können wir also z.B. einen Array mit Pointern auf die Clienten 
anlegen, damit wir die virtuelle Handler-Funktione aufrufen können. 
Vielleicht so:
1
static const OVF_Handler* OVF_clients[] = {
2
  &uhr,
3
  &taste1,
4
  &taste2
5
};
6
ISR(TIMER0_OVF_vect) {
7
  for (auto client: OVF_clients)
8
    client->OVF_vect();
9
}
Aber das kriegt man auch mit Template-Metaprogramming hin (Stichwort 
Parameter Packs). (Wie genau, müsste ich mir erst überlegen -- bin noch 
nicht so geübt darin.) Sieht auf der User-Seite dann ungefähr so aus:
1
ISR(TIMER0_OVF_vect) {
2
  const OVF_Handler<Uhr, Taste, Taste> clients(uhr, taste1, taste2);  // by-reference
3
  clients();  // operator(), ruft OVF_vect() von den Clients mittels Meta-Programming-"Schleife" auf
4
}
Und schon sind wir die Pointer los, und OVF_vect() in den Clients 
braucht nicht mehr virtuell sein; die Clients müssen nicht einmal eine 
gemeinsame Basisklasse haben, solange sie eine Funktion OVF_vect() 
implementieren.

von db8fs (Gast)


Lesenswert?

Thomas Holmes schrieb:
> Also erst einmal zur Entzauberung: C++ und OPP ist mit Sicherheit nicht
> schwerer als C oder gar Assembler zu programmieren. Im Gegenteil.

So? Ich denke, schlechter Code ist schnell geschrieben, gerade bei der 
Komplexität, die C++ bietet. Gerade durch die Bandbreite an Stilmitteln 
wird's doch kompliziert — angefangen von der saumäßig lahmen RTTI, über 
die reine Behandlung statisch typisierter Objekte bis hin zur 
effizienten, aber gruselig lesbaren, template-Ebene: man sollte schon 
wissen, was man tut. Was Karlheinz und Peter hier diskutieren ist die 
Verwendung der Sprache für effizientest möglichen Code, der trotzdem die 
Grundlage für halbwegs objektorientierte Ansätze sein kann. Gerade auch 
durch Metaprogrammierung mit templates sollte einiges drin sein.

Die Implementierung von Karlheinz ist super: sie erzeugt relativ 
selbsterklärenden und gut lesbaren Code und ist effizient.

> Die Verwendung von C++ auf kleinen µC ist meiner Meinung nach aber
> Nonsens –und ich schreibe Nonsens und nicht Verbrechen- weil es
> keinerlei  Vorteil bringt.

Für wen bringt es keine Vorteile? So üblen Code produzieren die Compiler 
gar nicht - auf kleinen ARMs würde ich überhaupt keinen Asm mehr 
anfassen, noch nicht mal für NEON der Cortexe oder so, sondern pauschal 
nicht. Und auf AVR — wenn es eine libC++ gibt, die Arduino-like Dinge 
kapselt, dann halte ich es mit Don Knuth, der vor vorschnellen 
Optimierungen warnt. Man kann auch mit C Grütze schreiben und 
softwaretechnischen Unsinn, der einzeln effektiv, aber im Gesamtsystem 
uneffizient ist.

> Es sei man glaubt die Mär vom kompakteren und
> schnelleren Code. Dies kann aber eigentlich nur  ein Grenzdebiler
> glauben. Hinzu kommt bei den ausgewiesenen Hochsprachen mit starker
> Abstrahierung dass der ganze Misst nicht mehr vernünftig zu debuggen
> ist.

Persönlich bin ich ein großer Fan von Abstraktion, eben weil sie 
Portabilität bzw. Testbarkeit von Einzelkomponenten ermöglicht und somit 
Debugging eigentlich im Vorfeld vermeiden kann. Natürlich werden auch 
Fehler versteckt, das liegt in der Natur der Sache. Abstraktion lässt 
aber Optionen offen: nämlich Komponenten zu bauen, die z.B. besonders 
performant (z.B. Karlheinz Code-Demo) oder auch gut wiederverwendbar 
sind. Wenn man halt das volle Programm haben will, sowohl Performanz als 
auch Wiederverwendbarkeit, muss man auch entsprechend viel Zeit 
investieren. Was aber leider nicht immer vermittelbar ist, weil C++ ja 
angeblich so pille-palle ist und bloß durch die reine Verwendung die 
Time-To-Market reduziert.

Und das muss jeder der hardwarenah programmiert eben tun und damit
> meine ich nicht den eigenen Prozessor im Focus habend, sondern die immer
> wieder neue und unbekannte periphere HW mit der ein µC Entwickler zu tun
> hat und der vor der Aufgabe steht anhand eines Datenblattes seinen Code
> anzupassen.

Sollte doch genauso möglich sein. Ich persönlich mag C auch (besonders 
c99), weil es mit vergleichsweise wenig Sprachumfang sehr effizient ist.
In der einfachsten Denke fasst C++ halt gewisse Sachen einfach durch ein 
Schlüsselwort zusammen. In C machste 'ne struct mit Funktionspointern, 
in C++ isses 'ne Klasse. Der umgekehrte Weg - Klassen mit C-Strukturen 
darstellen - ist imho auch nicht verkehrt, das würde auch eine Art 
Programmierung gegen Schnittstellen sein. In C++ ist das halt gleich 
durch die Syntax von 'class' mit dabei, was dir eine vtable erzeugen 
kann.

> Ich Entwickle seit über 10 Jahren für OSX und iOS und davor für Windows.
> Für solche Systeme führt fast kein Weg an C++ bzw. den
> Weiterentwicklungen C“#“ und „Objectice C“ vorbei. Hier stellt quasi OOP
> mit all seinen Vorteilen und den >>bereits vorhandenen<< mächtigen und
> kaum zu überschauenden Klassen und Schnittstellen die einzige
> Möglichkeit dar überhaupt noch mit einem so komplexen System zu
> kommunizieren und dessen Funktionen zu verwenden. Dafür ist diese
> Sprache das einzige Mittel.

Polymorphie hat Vorteile, hat aber auch Nachteile. Für die Modellierung 
ist sie schön, wenn sie richtig angewandt wird. Ich hab sie auch selber 
schon oft genug selber falsch angewendet - sei es durch unbekannte oder 
zu flexible Requirements / Constraints und sicher auch einfach durch 
manchmal ungeschickte Platzierung.

> Gäbe es für alle Mikrocontroller und jede aktuelle mögliche periphere
> Hardware  frei verfügbare und funktionierende Klassen und einem Compiler
> der C++ fehlerfrei unterstützt zuzüglich einer funktionierenden
> Möglichkeit adäquat zu debuggen dann sähe die Sache für den einen oder
> anderen Fall möglicherweise etwas anders aus.  Denn dann wäre wie bei
> den „Hochbetriebssystemen“,  OOP eine Erleichterung und würde seine
> eigentliche Stärke ausspielen. Das tut es aber nicht und wird es
> vermutlich nie geben. Aus vielen verschiedenen und offensichtlichen
> Gründen.  Und unter den gegebenen Voraussetzungen ist C++ ein Hemmschuh.

Im Zweifelsfall sollte auch der sportliche Aspekt gelten: einfach mal 
gucken, wie gut es im Endeffekt wirklich funktioniert.

von Frank (Gast)


Lesenswert?

Leute, der Thread heißt:
"C++ auf einem MC, wie geht das?"

Könnt ihr das philosophieren nicht auf den andren heute gestarteten 
Laber Thread verschieben?

von chris_ (Gast)


Lesenswert?

>Ich möchte gerne folgendes erreichen:

>  LED0 = KEY0.state;           // an solange Taste gedrückt
>  LED1.toggle = KEY1.press     // wechsel bei jeder Drückflanke
>  LED2.off = KEY2.press_short; // kurz drücken - aus
>  LED2.on = KEY2.press_long;   // lang drücken - an

Man könnte ein Konzept verwenden, welches bei Java oder C# übliche ist.
Dort gibt es die sogenannten "Listener" also Zuhörer, die bei einer 
Ereigniserzeuger eingetragen werden. In Java würden die KEYs übrigens 
"Button" heißen.

Ich wandle das Prinzip aus Java etwas ab, damit es auf einfache Weise 
zum Tastenproblem passt.
1
Key key0=new Key();
2
Key key1=new Key();
3
Key key2=new Key();
4
5
Led led0=new Led();
6
Led led1=new Led();
7
Led led2=new Led();
8
9
key0.addKeyPressedListener(led0);
10
key1.addKeyToogleListener(led1);
11
key2.addShortLongListener(led2);

von Frank (Gast)


Lesenswert?

Es geht hier mittlerweile auch darum möglichst guten Code zu erzeugen 
;-)

Im Normalfall weiß man zur Compilezeit wieviele Tasten angeschlossen 
sind.
Eine dynamische Erzeugung ist somit überflüssig!

von tictactoe (Gast)


Lesenswert?

tictactoe schrieb:
> Dann können wir also z.B. einen Array mit Pointern auf die Clienten
> anlegen, damit wir die virtuelle Handler-Funktione aufrufen können.
> Vielleicht so
> ...
> Aber das kriegt man auch mit Template-Metaprogramming hin

Ich muss hier nochmal nachhaken. Mein Post ist eine Werbung für 
Template-Metaprogramming. Man muss hier aber einen Schritt zurück machen 
und noch mal die Ausgangssituation betrachten. Gerade weil man auf einem 
µC keine dynamischen Listen von (Uhren, Tastern...) hat, könnte man 
Template-Metaprogramming verwenden (weil alle teilnehmenden Klassen zur 
Compile-Zeit bekannt sind). Aber aus dem selben Grund kann man genau so 
gut auch schreiben:
1
ISR(TIMER0_OVF_vect) {
2
  uhr.OVF_vect();
3
  taste1.OVF_vect();
4
  taste2.OVF_vect();
5
}
Wozu also das ganze Gedöns um virtuelle Funktionen (braucht man bei 
dieser Form nicht) und Template-Metaprogramming? Hab' ich was an den 
Anforderungen nicht verstanden?

von chris_ (Gast)


Lesenswert?

>Es geht hier mittlerweile auch darum möglichst guten Code zu erzeugen
>;-)

Gut ist in diesem Zusammenhang eine relativ unklare Metrik. Erkläre 
"gut".

von temp (Gast)


Lesenswert?

tictactoe schrieb:
> Wozu also das ganze Gedöns um virtuelle Funktionen (braucht man bei
> dieser Form nicht) und Template-Metaprogramming? Hab' ich was an den
> Anforderungen nicht verstanden?

Das sehe ich auch so. So schön die ganze Template Geschichte auch ist, 
man kann kaum richtig einschätzen was der Compiler am Ende wirklich 
daraus macht. Wenn man dann aber erst im Assemblerlisting rumkramen muss 
um die Qualität der Umsetzung zu beurteilen wir es lästig und fraglich. 
Noch dazu bei einem Interrupt. Dann ist die traditionelle Variante:
1
ISR(TIMER0_OVF_vect) {
2
  uhr.OVF_vect();
3
  taste1.OVF_vect();
4
  taste2.OVF_vect();
5
}
wirklich besser.

von Ahab (Gast)


Lesenswert?

>> Wozu also das ganze Gedöns um virtuelle Funktionen (braucht man bei
>> dieser Form nicht) und Template-Metaprogramming? Hab' ich was an den
>> Anforderungen nicht verstanden?

Vermutlich: Damit sich ein Instanziiertes Template selbständig irgendwie 
automatisch am Timer registrieren kann.

Also
1
#include <IRPM>
2
3
...
4
5
int main() {
6
  IRMP receiver< Pin< PORTB, 3 > >;
7
8
  while (1) {
9
    if (receiver.gotCode()) { .... }
10
  }
11
}

Ohne dass man selber in der ISR ein "receiver.poll()" Aufrufen müsste...

von Ahab (Gast)


Lesenswert?

Ahab schrieb:
> IRMP receiver< Pin< PORTB, 3 > >;

Ist so natürlich verkehrt herum... aber man verstehts hoffentlich 
trotzdem.

von BastiDerBastler (Gast)


Lesenswert?

Geht ziemlich in die Richtung von "Flow-Based Programming", oder nicht?
(http://en.wikipedia.org/wiki/Flow-based_programming)
Den Gedanken mag ich sehr, aber mir fiel nie eine vernünftige 
Ausdrucksweise in C/C++ ein, dem dem PROGRAMMIERER nützt und noch 
irgendwie effizient ist. Da bräuchte man eigentlich eine höhere Sprache, 
die das dann beispielsweise in C übersetzt... Mit all den Nachteilen die 
man sich da einhandelt.

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Ich dachte mir, ich könnte eine C++11 alternative zu <avr/io.h> 
schreiben, aber ich schaffe es nicht, dass am Schluss ein sbi dabei 
rauskommt. Warum will avr-gcc 4.8.2 hier nicht stärker optimieren?

Was ich erwartet habe:
1
  PORTA |= 1;
2
  3a:   70 9a           sbi     0x0e, 0 ; 14

Was immer herauskommt, egal welche Optimierungseinstellung:
1
  3a:   ee e0           ldi     r30, 0x0E       ; 14
2
  3c:   f0 e0           ldi     r31, 0x00       ; 0
3
  3e:   80 81           ld      r24, Z
4
  40:   81 60           ori     r24, 0x01       ; 1
5
  42:   80 83           st      Z, r24

von svb (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Man kann ganz leicht ein Enum in der falschen Statemaschine
> verwenden und der Compiler lacht sich ins Fäustchen.

Das umgeht man entweder dadurch, dass man die Sichtbarkeit
einschränkt, indem man die Enumeration (meistens Typdefinition + 
Enumeration) nur in dem C-File hinzufügt, in dem auch die Funktion
steckt, die das Enum nutzen soll/darf

oder

mittels Defines nur die notwendigen Enums aus einem Header-File
inkludiert (ifdef ichbin's, dann include nur meinen relevanten Code).

von svb (Gast)


Lesenswert?

Da wäre z.B. der Tipp sich mal den MID Innovator anzusehen
bezüglich SA/SD/Implementierung.

http://www.mid.de/

Der erledigt dann z.B. die Sichtbarkeit von Handles automatisch.

von chris_ (Gast)


Lesenswert?

Peter Danneger schrieb:
>Ich möchte gerne folgendes erreichen:
>  LED0 = KEY0.state;           // an solange Taste gedrückt
>  LED1.toggle = KEY1.press     // wechsel bei jeder Drückflanke
>  LED2.off = KEY2.press_short; // kurz drücken - aus
>  LED2.on = KEY2.press_long;   // lang drücken - an

Hallo Peter,

da mich das Thema auch ein wenig interessiert, habe ich ein wenig Code 
geschrieben. Mir geht es eher um die "Architektur" des Programms, 
deshalb habe ich der Einfachheit halber einen Arduino verwendet mit 
seinen langsamen Libs verwendet und die Sachen nicht 
Geschwindigkeitsoptimiert. Der Arduino liegt hier meistens herum und ich 
muss nicht erste ein Steckbrett mit MCU aufbauen.
Da das Prinzip der Listener sich auf den PCs durchgesetzt hat, habe ich 
dieses implementiert.
So weit ich weiß, hast Du hier im Forum irgenwo eine sehr gute 
Entprellroutine für MCs geschrieben, die sehr viel Anklang findet.
Dein Code ist überlicherweise kurz, klar und gut, deshalb mögen die 
langen Funktionsnamen im folgenden Beispiel etwas abschreckend sein, 
aber ich versuche selbst erklärenden Code zu produzieren und die 
Funktionsnamen für sich sprechen zu lassen.
1
class Led
2
{
3
  public:
4
    uint8_t ledPin;  
5
    
6
    Led(){};
7
    
8
    Led(uint8_t p){
9
      ledPin=p;
10
      pinMode(ledPin,OUTPUT);
11
    };
12
    
13
   void set(uint8_t onOff){
14
    digitalWrite(ledPin,onOff);
15
   }; 
16
};
17
18
#define PRESSED 0
19
#define TOOGLE 1
20
#define SHORTLONG 2
21
22
class Key{
23
  public:
24
     uint8_t keyPin;
25
     Led * oneLed;
26
     uint8_t action;
27
     uint8_t oldKey;
28
     uint8_t keyToogleState;
29
    
30
      Key(){
31
        keyToogleState=0;
32
      };
33
    
34
      Key(uint8_t p){
35
        keyPin=p;
36
        pinMode(keyPin,INPUT);
37
        
38
        keyToogleState=0;
39
      };
40
  
41
  void addKeyPressedListener(Led *led){
42
    oneLed=led;
43
    action=PRESSED;
44
  };
45
  void addKeyToogleListener(Led *led){
46
    oneLed=led;
47
    action=TOOGLE;
48
  };
49
  void addShortLongListener(Led *led){
50
    oneLed=led;
51
    action=SHORTLONG;
52
  };
53
  
54
  void update()
55
  {
56
    uint8_t k=digitalRead(keyPin);
57
    if(k!=oldKey) keyToogleState=!keyToogleState;
58
    oldKey=k;
59
    
60
    switch(action)
61
    {
62
      case PRESSED:{
63
        oneLed->set(k);  
64
      };break;
65
      
66
      case TOOGLE:{
67
        oneLed->set(keyToogleState);
68
      };break;
69
      
70
      case SHORTLONG:{
71
      
72
      };break;
73
      default:{} break;
74
    }
75
76
  }; 
77
};
78
79
void setup() {                
80
81
}
82
83
void loop() {
84
  
85
  Led *led1=new Led(12); // led on x
86
  Led *led2=new Led(13);
87
  Led *led3=new Led(11);
88
  
89
  Key key1(4);
90
  Key key2(5);
91
  Key key3(6);
92
  
93
  key1.addKeyPressedListener(led1);
94
  key2.addKeyToogleListener(led2);
95
  key3.addShortLongListener(led3);
96
  
97
  
98
  while(1)
99
  {
100
    key1.update();
101
    key2.update();
102
    key3.update();
103
    
104
    delay(100);
105
  }
106
}

von BastiDerBastler (Gast)


Lesenswert?

Jetzt kann man da nur LEDs anschließen und das "new" dort ist wohl 
unnötig... Für PRESSED etc. besser enum oder enum class...

von F. F. (foldi)


Lesenswert?

Dr. Sommer schrieb:
> Aber auch in Brainfuck. Warum verwendet niemand Brainfuck? Es ist
> supereinfach zu lernen, verwenden und implementieren.

Habe mir das mal angeguckt - ich hätte fast gekotzt.

von Moby (Gast)


Lesenswert?

Daniel A. schrieb:
> aber ich schaffe es nicht, dass am Schluss ein sbi dabei
> rauskommt.

Dann nimm doch einfach Asm ;-)

Ret schrieb:
> Wo ist der wirkliche MEHRWERT? Ist das Endergebnis wirklich besser?
> Lohnt der Aufwand wirklich?

Auf diese alberne Frage gibts HIER doch keine Antwort. Da sind andere 
Dinge wirklich wichtiger!

von chris_ (Gast)


Lesenswert?

>Jetzt kann man da nur LEDs anschließen und das "new" dort ist wohl
>unnötig... Für PRESSED etc. besser enum oder enum class...

Hmm, man kann LEDs und Taster anschließen. Ich dachte, das sei die 
Aufgabe. Peter hatte ja ganz am Anfang dieses Threads den "Beispielcode" 
gepostet, der in C++ umgesetzt werden soll.

von W.S. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Hier gibts ja viele Meinungen zu C++, aber wenn man es selber probieren
> will, gibt es große Hürden.

Peter Dannegger schrieb:
> Besonders interessant wäre die Entprellung portweise parallel und nur
> das Auswerten der Tasten einzeln.

Peter, ich verstehe dich wirklich nicht. Was soll das bloß?
Also erstens, wozu über C++ nachdenken? Um für einen µC eine Firmware zu 
schreiben, nimmt man sinnvollerweise etwas, das dem Teil angemessen ist 
und das man kriegen kann und bezahlen kann und womit man selber auch 
zurecht kommt. Das läuft zumeist auf simples C hinaus, wobwi allerdings 
einige Compiler auch genausogut C++ verstehen.

Wo sind deine Hürden?

Ich sehe das so, daß viele Leute einfach völlig unstrukturiert an ihre 
Probleme herangehen und einfach brüllen "Ich brauche jetzt&hier eine 
Entprellung meiner 7 Tasten, aber PRONTO!" anstatt sich irgend einen 
Gedanken über eine Strukturierung ihrer Firmware zu machen.

Dazu hätte gehört, zwischen quasi einer Anwendung und quasi einem Satz 
echter Hardwaretreiber zu unterscheiden, wo das geeignete Abfragen und 
Behandeln von solchen Dingen wie Tasten, Drehgebern, Endlagenschaltern 
und so weiter sauber und gekapselt und sicher für's System stattfindet.

An solchen Niederungen ist das, was manche unter C++ verstehen wollen, 
einfach fehl am Platz.

Peter Dannegger schrieb:
> LED1.toggle = KEY1.press

Eben. So ein Gedanke (wenn man das denn so nennen will) ist sowas von 
strunzfalsch, daß mir dafür die geeigneten Worte fehlen. Stattdessen 
sieht das sinnvollerweise eher so aus:
1. Keytreiber stellt fest, daß KEY1 immer noch ununterbrochen gedrückt 
ist, obwohl die Repetierzeit vorbei ist, also ruft er den Eventhandler 
auf mit AddEvent(Key1_pressed)
2. Die zentrale Eventverwaltung der Firmware arbeitet die aufgelaufenen 
Events ab und wird dann auch "Key1_pressed" an die zuständigen Teile der 
Anwendung weiterreichen.
3. Ein Programmteil im Anwendungsbereich der Firmware kriegt diesen 
Event ab und tut daraufhin, was er tun soll, z.B. die Heizung 
abschalten. Das signalisiert er dann mit der zuständigen Signallampe, 
indem er LED1 ausschaltet. Das macht er direkt im zuständigen Port, weil 
sowas eine nebeneffektfreie Aktion ist.

Wo siehst du da bloß einen Ansatzpunkt, um Tasten per C++ zu entprellen?

W.S.

von chris_ (Gast)


Lesenswert?

>Peter, ich verstehe dich wirklich nicht.

Da liegt wahrscheinlich Dein Problem.
Peter kennt sich mit Treibern zur Tastenentprellung ziemlich gut aus:
http://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29

Ich denke hier geht es eher um die Frage: wie kann die Methoden und 
Techniken die C++ und die Objektorientierung bieten auch für eine 
Tastenentprellung auf einem MC nutzen.

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Daniel A. schrieb:
> aber ich schaffe es nicht, dass am Schluss ein sbi dabei
> rauskommt. Warum will avr-gcc 4.8.2 hier nicht stärker optimieren?

Ich habe es jetzt herausgefunden, es war mein Fehler: Als ich die 
Adressen der Ports aus <avr/iotn48.h> abgeschrieben habe, habe ich 
übersehen, dass makro _SFR_IO8 einen offset von 0x20 hinzugefügt hat. 
Dadurch hatte gcc die falsche Adresse und konnte kein sbi benutzen.

Im Anhang sind alle geänderten Dateien. Jetzt verwendet gcc immer ein 
sbi, und wenn ich das aus dem C++ code generierte hexfile mit dem 
hexfile aus folgendem C-Code vergleiche, sind diese Identisch!
1
#include <avr/io.h>
2
int main(void){
3
  PORTA |= 1;
4
  return 0;
5
}

von Conny G. (conny_g)


Lesenswert?

Peter Dannegger schrieb:
> Sepp schrieb:
>> Um die dauer des
>> Tastendrucks festzustellen benötigt man auch hier eine ISR
>
> Das ist klar.
> Es ging mir auch haupsächlich darum, wie ich in der Mainloop die
> Ereignisse auswerten kann und wie ich die Klassen definieren muß.
>
> Z.B. ich definiere eine Klasse LED und ein Klasse KEY und darin alle
> Aktionen und Initialisierungen dafür. Die Aktionen benutze ich dann in
> der Mainloop.
> Schön wäre es noch, wenn die Klasse automatisch den passenden
> Interrupthandler aktiviert.
> Füge ich eine neue Taste der Klasse KEY hinzu, wird geprüft, ob deren
> Port bereits entprellt wird und wenn es ein neuer Port ist, wird der
> Code dafür hinzugefügt.

Ich finde die Stossrichtung diese Dinge mal objektorientiert zu 
betrachten hervorragend und lese neugierig mit.

Mir gehen dazu ein paar Gedanken durch den Kopf, die ich Euch einfach 
mal so hinwerfen wollte:

Was Pin, Key und Debounce angeht sind das für mich aus einer 
Datenmodell-Perspektive 3 verschiedene Dinge.
Pin ist die "physikalische Schicht", die Hardware.
Key ist ist ein Device, das am Pin hängt.
Debounce ist ein Service, die die beiden verbindet.
Desweiteren ist Debounce nicht 1:1 mit Pin oder Key verbunden, sondern 
ist ein "Service", der mehrere Pins abfragt und als Key an die 
Applikation liefert, also:

Pin \                         / Key
Pin  +-- Debounce-Service -- +  Key
Pin /                         \ Key

Wahlweise können Keys dann an Pins hängen, wenn die Hardware-entprellt 
sind, also müsste die Key-Klasse mit beidem umgehen können.

Der Debounce-Service wäre also eine Klasse, die im Interrupt hängt, 
einen oder mehrere Ports bedient (könnte sogar selbst wissen, wann es 
das tun muss) und mehreren Keys als Input dient.
Keys haben also eine Referenz auf ein Bit des Debounce-Objekts, das 
einen Port bedient und alternativ eine direkte Referenz auf ein Pin.

Weiters sollte m.E. ein Key nicht mit einer LED verbunden sein, 
zumindest nicht 1:1. Denn üblicherweise ist der Zweck eines Keypress 
nicht, eine LED zu schalten, sondern die LED ist ein Signal für einen 
Zustand des Systems.
Also wäre der primäre Vorgang mit einem Keypress einen Vorgang im System 
auszulösen, eine Zustandsänderung. Und die Zustandsänderung ist mit der 
LED verknüpft.
Angenommen also, dass ein bestimmter Zustand eine richtige Statemachine 
in einen anderen Zustand bringt, dann müsste die LED mit der 
Statemachine verbunden sein und bei erreichen eines bestimmten State 
geschaltet werden. Und zwar am besten nicht explizit mit "LED.on()", 
sondern implizit durch die State Machine, weil die LED mit dem State 
verknüpft ist.

von Thomas W. (wagneth)


Lesenswert?

Moin !

Ich finde den OOP Gedanken sehr schick.

Aktuell habe ich mal wieder ein Projekt bei dem :

- Ein digitales Signal vermessen wird. (Pulsdauer und Startzeitpunkt)
- Die Daten durch eine simple Berechnung gehen.
- Und am entsprechenden Pin wieder digital zappeln muss.

Das ganze ist vierfach vorhanden.
Die Daten liegen alle in einem Struct.daten1, Struct.daten2 ...
Die "Vermessprozedur" sowie die "Ausgabe" werden mit ihrem 
entsprechenden Pointern aufgerufen.
Dabei wird auch ein Pointer auf den entsprechenden Port übergeben.
(nicht sehr schick)

Das schreit doch förmlich nach OOP !?
Die Frage ist jetzt nur wie weit treibe ich den Spuck.

Für meine Problemstellung würde ich folgende Klassen benötigen :

GPIO       - Digitale Ein und Ausgabe.
SENSOR     - Vermessen des Signal und Trigger für Startzeitpunkt
CALCULATOR - Signal für die Ausgabe berechnen
AKTOR      - Gibt bei Trigger von SENSOR die berechnete Pulslänge aus.

Obiges könnte man aber auch zu GPIO und EINSPRITZDING zusammenfassen.

Vielleicht wäre es auch sinnvoll wenn GPIO den PORT erbt.
Bestimmte PORT haben etwas mit GPIO, UART, SPI etc zu tun.
Aber SPI und UART müssten auch auf GPIO zugriff haben.

In echtem guten OOP müsste ich das doch nachbilden ?
Aber klar ist doch das verschiedene GPIO an SENSOR und AKTOR übergeben 
werden.

Für mein Problem reicht ein einfaches GPIO.
Macht man es gescheit wirds ne dicke Kuh.

Ich denke ich baue das Programm nochmal in C++ und OOP nach.
Mal schauen was rauskommt...

von Ret (Gast)


Lesenswert?

F. Fo (foldi) schrieb:

Dr. Sommer schrieb:
>> Aber auch in Brainfuck. Warum verwendet niemand Brainfuck? Es ist
>> supereinfach zu lernen, verwenden und implementieren.

> Habe mir das mal angeguckt - ich hätte fast gekotzt.

Da kann ich dich voll verstehen! Denn mir geht es genau nicht anders. 
Wer so einen hingekotzten Zeichensalat (ich hab extra nochmal 
nachgeschaut) wie Brainfuck ernsthaft als Programmiersprache empfiehlt, 
verarscht entweder seine Mitdiskutanten oder hat schlicht nicht alle 
Tassen im Schrank. Langsam nähren sich in mir die Anzeichen, dass ich 
solche Leute künftig an anderer Stelle nicht mehr für voll nehmen 
sollte. Und wenn mir der gleiche Herr demnächst dann seine C++ 
Template-Orgien schmackhaft unter die Nase reiben will, denke ich mir 
darauf, "hab Nachsicht mit ihm! Das ist einer, der auf Brainfuck steht. 
Von dem kann einfach nichts Brauchbares oder Gescheites kommen".

So kann man sich seine Glaubwürdigkeit hier auf einen Schlag verspielen. 
Aber vielleicht passt auch alles gut zusammen. Wer auf Brainfuck steht, 
der findet manche Brainfuck-Ausdrücke, die in Form von C++ so anfallen, 
eben auch "geil" und ergötzt sich daran. Nur verschont bitte die noch 
normal gebliebenen Landsleute unter uns damit, die einfach in 
vertretbarem Aufwand, LESBAREN UND WARTBAREN CODE programmieren möchten, 
ob für den PC oder für µC, ob in C, Assembler, Pascal (Delphi, Lazarus), 
LunaAVR, Processing oder einem modernen BASIC-Dialekt.

Esoterische Hirnkrampf-Programmiersprachen sind was für Leute, die zu 
viel gelangweilte Freizeit zur Verfügung haben, konsumtechnologisch 
total übersättigt sind, fortwährend nach einem neuen Kick suchen und in 
ihrem Umfeld schließlich durch zu viel nerdiges Hipster-Verhalten 
anderen chronisch auf die Hutschnur gehen. Einfach künftig mal die 
Klappe halten und lieber anderen zuhören ist der erste Schritt zur 
Heilung. ECHTE Hilfe anbieten ist dann Schritt Nr. 2 usw. Brainfuck 
braucht es dazu nicht.

von F. F. (foldi)


Lesenswert?

Hier wird jetzt viel geschrieben, a la "geht nicht", "ist nicht anders 
vom Aufwand", "ist zu langsam und zu viel overhead". Andere sagen dann 
wieder, "geht doch" ...

Mal anders gefragt: Wer von euch programmiert seine µC's mit C++ und was 
sind das für Programme?

Wenn jemand wie Peda an sowas denkt und das für eine gute Idee hält, wo 
er doch so ein C Spezi ist, dann ist das sicher mal ein berechtigter 
Gedanke.

Die einzige wirklich (für mich) brauchbare Aussage habe ich hier 
gelesen:

Sepp schrieb:
> Was mit persönlich an C++ im Embedded Bereich sehr gefällt sind die
> Generics (bei C++ Templates genannt). Damit kann man einen großteil der
> statischen DEFINES in einem typeischen C Programm weg bringen und das
> ganze dazu noch kapseln.
>
> z.B. hab ich Filterklassen als Template realisiert denen man bei der
> Instantierung nur die Ordnung und die Charakteristik mitgibt. Der vom
> Compiler erzeugte Code ist dann exakt genau so schnell wie C code. Der
> Vorteil ist das man mit einer Zeile einen neuen Filter hat - ganz ohne
> Code Duplication, globale Variablen, Precompiler DEFINES usw... sauber
> halt.
>
> Es geht natürlich auch mit C genau so gut. Nur halt umständlicher und
> nicht so sauber, aber für nicht C++ affine Programmier halt dafür viel
> verständlicher...

Und genau darum geht es doch.

von Moby (Gast)


Lesenswert?

Conny G. schrieb:
> Ich finde die Stossrichtung diese Dinge mal objektorientiert zu
> betrachten hervorragend

Thomas W. schrieb:
> Ich finde den OOP Gedanken sehr schick.

Viele (die meisten) Programmierer würden wohl unterschreiben, daß man 
die Dinge möglichst einfach halten sollte, verfallen aber dann doch dem 
intellektuellen Reiz, das OOP Prinzip Problemstellungen jedweder Art 
überzustülpen. Die OOP Sichtweise hat offensichtlich geradezu was 
Ideologisches, dem sich manche nicht mehr entziehen können. Wenn

Peter Dannegger schrieb:
> Ich möchte gerne folgendes erreichen:  LED0 = KEY0.state;           //
> an solange Taste gedrückt
>   LED1.toggle = KEY1.press     // wechsel bei jeder Drückflanke
>   LED2.off = KEY2.press_short; // kurz drücken - aus
>   LED2.on = KEY2.press_long;   // lang drücken - an

würde man doch nur
- in einem zyklischen Timerinterrupt vorhandene Keys ein paarmal 
abfragen und einen über mehrere Zyklen festgestellten gleichen Zustand 
als aktuellen für Key X zuweisen (Entprellung)
- mit dem Wechsel des aktuellen Zustands von Key1 LED1 toggeln
- mit den über im selbigen Timerinterrupt feststellbaren verschieden 
langen Key2-ON Statusperioden LED2 aus- und einschalten.

Was zum Teufel muß einen reiten, über die Codierung dieser klar 
begrenzte Funktionalität hinaus Gedankenakrobatik mit 
unterschiedlichsten OOP Konstrukten zu betreiben?

von Klaus W. (mfgkw)


Lesenswert?

Moby schrieb:
> Was zum Teufel muß einen reiten, über die Codierung dieser klar
> begrenzte Funktionalität hinaus Gedankenakrobatik mit
> unterschiedlichsten OOP Konstrukten zu betreiben?

Auch wenn ich wenig Hoffnung habe, daß du es irgendwann einsehen willst, 
noch ein Versuch: es gibt auch Programme, die über Pinwackeln 
hinausgehen.

Ein Programm kann durchaus mehrere Ebenen haben, die man intelektuell zu 
erfassen hat (siehe z.B. OSI - 7 Schichten, oder Anwendung - USB-Stack - 
HW-Ansteuerung).

Wer in einem Programm ab einer gewissen Komplexität (a) Code, der die 
Logik eines Webservers abbildet, mit (b) Code, der IO-Pins setzt, 
vermatscht, programmiert ziemlichen Mist.
Das ist Pfusch und keine SW-Entwicklung.

Insofern ist es durchaus sinnvoll, Sachen, die nicht zusammengehören, 
auseinander zu ziehen.

Assembler ist jetzt halt geeignet, Prozessorbefehle auszudrücken.
Wenn das Programm aus nicht wesentlich mehr besteht, ist das vollkommen 
in Ordnung. Deine "klar begrenzte Funktionalität" ist aber nicht immer 
die ganze Welt, sondern hier nur ein Beispiel.

Mit ABS und ESP nur in Assembler geschrieben würde ich mich nicht 
anfreunden wollen, wenn jemand irgendwas unstrukturiert hinrotzt.

Für die darüber liegenden Ebenen gibt es einfach bessere Sprachen.

Ob das jetzt C++ ist oder was anderes, steht wieder auf einem anderen 
Blatt.
Die ursprüngliche Frage geht jetzt halt nun dahin, wo die Grenze 
sinnvollerweise zu ziehen ist und wie man sie gestalten könnte.

Daß du die Frage nicht verstanden hast, weiß inzwischen jeder. Belass es 
doch einfach dabei, bevor es noch peinlicher wird.
Mag sein, daß du nur Probleme hast, bei denen deine Arbeitsweise passt.
Mag auch sein daß deine Probleme nicht zu dieser Arbeitsweise passen, du 
aber totzdem so arbeitest. Das tut hier aber alles nichts zur Sache.

Hier geht es darum, wie man es auch anders machen könnte.
Also für Leute, die auch mal etwas weiter schauen wollen...

: Bearbeitet durch User
von F. F. (foldi)


Lesenswert?

PSOC geht ja so ein bisschen den Weg Hardware von eigentlichem Code zu 
trennen. Die Hardware zeichnet man, stellt dort alles ein was so ein Pin 
soll und den Rest in Software.

Was ich hier überhaupt nicht verstehe und für mich wieder zeigt wie 
viele hier mehr reden als wirklich machen, da doch immer mehr die 
Cortexe bevorzugt werden, mit ihren viel höheren Geschwindigkeiten und 
mehr Speicher, wieso dann nicht auch gleich die Programmiersprache mit 
wechseln?
Auf jeden Fall habe ich schon mal wieder das C++ Buch aus dem Regal 
genommen.

von Conny G. (conny_g)


Lesenswert?

Erinnerung, dass dies kein Ideologie-Thread ist, sondern die konkrete 
Frage von PeDa, ob und wie man seine Fragestellung in C++ lösen kann.

von Moby (Gast)


Angehängte Dateien:

Lesenswert?

Klaus Wachtler schrieb:
> sondern hier nur ein Beispiel

Nein nein Klaus- nicht "nur" ein Beispiel.
Wohin man auch schaut wenn man es denn konkret tut: Überall dieselben 
"Beispiele"! Und nein, wir reden hier nicht von einem Webserver oder 
einem ausladenden SAP-Programm, sondern ganz konkreten

Peter Dannegger schrieb:
> MC-spezifische Abläufe ... (IO-Zugriffe, Statemaschine usw.)

Die liegen allermeistens immer noch unter

Klaus Wachtler schrieb:
> Programm ab einer gewissen Komplexität

was Du hier offensichtlich meinst.

Klaus Wachtler schrieb:
> es gibt auch Programme, die über Pinwackeln
> hinausgehen.

Tatsächlich?
Dann weißt Du aber auch, daß sich vielschichtigere Programme beileibe 
nicht nur mit OOP-Zauberspuk, sondern ganz klassisch über ein 
Interruptsystem realisieren lassen. Das hat nämlich meist den Vorteil, 
kürzer, schneller, punktgenauer und übersichtlicher zu sein. Man nutze 
nur die Ressourcen seines Controllers, dann klappt es vielleicht auch 
mit der Reduktion vermeintlich nötiger Ebenen.

F. Fo schrieb:
> wieder das C++ Buch aus dem Regal

Oh da oben stehen bei mir auch noch welche...
Leider haben sie bislang nur Zeit gekostet, fürs E-Hobby aber nix 
gebracht ;-(

von Conny G. (conny_g)


Lesenswert?

Bei soviel Kritik an der Idee muss ich doch nochmal was dazu schreiben.

Mit einer mutigen Vision entstehen oft hervorragende Dinge.
Wenn man sich vorab denkt "wer braucht das?", dann wird man sich nie auf 
den Weg machen eine neue Kategorie von Lösung zu erforschen.
Schaut "node.js" - da hab ich mir vor ein paar Jahren gedacht - so ein 
Quatsch, wer braucht Javascript auf dem Server.
Und inzwischen ist es signifikant populär und ich hab selber node.js für 
ein Projekt am Laufen, weil es das nur so gibt. Hätte ich nicht gedacht.
Oder Java vor 20 Jahren - "wer braucht eine hardwareunabhängige 
Programmiersprache für Geräte??". Ich arbeite fast nur noch mit Java.

D.h. das Argument "braucht man nicht, wenn ..." (oder "Hammer immer 
schon so gemacht") ist kein Innovationstreiber :-)
(V.a. was soll denn das bringen, diejenigen, die diese Idee verfolgen 
Niederzuargumentieren und Ihnen den Mut zu nehmen? Innovation bremsen?)
Irgendein Wahnsinniger (oder eine Gruppe von Wahnsinnigen) muss einfach 
damit voran machen und die anderen überzeugen.
Wem's nicht gefällt der braucht ja nicht mitmachen.

Es gibt sicher auch einen großen Unterschied zwischen 
Hobby-Eletronikern, die moderat viele Dinge umsetzen, deren Komplexität 
meist begrenzt bleibt und die auch nur wenig Zeit haben sich mit der 
"Schönheit" der Lösung zu beschäftigen, und beruflichen 
Mikroprozessor-Lösungs-Entwicklern, die ihre Softwarearbeit skalierbar 
gestalten wollen.

Und aus der Ecke scheint PeDas Frage zu kommen:
zuviele Lösungen, die alle in sich die Gefahr tragen mal einen Pin im 
Code zu verwechseln etc.
Wenn man das und weitere Komplexität hübsch in OOP einpacken kann, 
sodass die Definition der Objekte bereits Fehler verhindert (strenge 
Typisierung) und das Ganze einfach übersichtlich macht (Kapselung), dann 
ist viel Debug-Zeit erspart und es wird auch pflegbarer und teamfähiger.

Jetzt Schluss mit Infragestellung von PeDas Frage und weiter an der 
Lösung!!

von W.S. (Gast)


Lesenswert?

Conny G. schrieb:
> Erinnerung, dass dies kein Ideologie-Thread ist, sondern die konkrete
> Frage von PeDa, ob und wie man seine Fragestellung in C++ lösen kann.

Reichen die bisherigen Beiträge denn WIRKLICH nicht aus, diese Frage 
final geklärt zu haben???

Nein?

Dann gebe ich hier mal zwei Antworten unterschiedlicher Art:
1. die Flapsige: Nimm einen C++ Compiler und gib ihm die bisherigen C 
Quellen zum Übersetzen. Dann kann man sich rühmen, C++ benutzt zu haben.

2. die Eigentliche: Setze dich hin und versuche zu allererst, deine 
Gedanken zu strukturieren. Genau DAS hat nämlich an der eigentlichen 
Fragestellung gefehlt. Im Grunde ist es nämich scheissegal, welche 
Programmiersprache man zum Erstellen seiner µC Firmware benutzt, 
stattdessen ist es nur wichtig, wie man die gewünschte Funktionalität 
strukturiert. Ob man da als Geradeausprogrammierer alle Ebenen der 
Firmware durcheinanderschmeißt und partout sowas wie Peter anvisiert:

Peter Dannegger schrieb:
> Ich möchte gerne folgendes erreichen:
>   LED0 = KEY0.state;           // an solange Taste gedrückt
>   LED1.toggle = KEY1.press     // wechsel bei jeder Drückflanke
>   LED2.off = KEY2.press_short; // kurz drücken - aus
>   LED2.on = KEY2.press_long;   // lang drücken - an

oder ob man sich Gedanken über sinnvolle Hardwarekapselung - verbunden 
mit einer firmwareinternen HAL macht, hängt eben davon ab, ob man 
gewillt ist, zu allererst mal seine Gedanken zu strukturieren.

Apropos HAL: Damit meine ich NICHT solche eher blödsinnigen 
Möchtegern-Kapselungen wie PortX.SetzeBit(2), sondern was Echtes, also 
derart, daß die konkrete Hardware im Interface zum übergeordneten 
Programm überhaupt nicht mehr auftaucht. Wer jetzt schreit "Ich brauche 
aber PortX und dort Bit 2 in meiner Routine!", hat den Sinn einer HAL 
nicht verstanden und seine Firmware schlichtweg falsch oder garnicht 
strukturiert.

Fazit: Peters Vorstellung, so etwa wie das oben Zitierte in C++ 
formulieren zu wollen, ist m.E. inhaltlich nicht sinnvoll. Deswegen 
mein Rat, so etwas bleiben zu lassen und stattdessen das gesamte Thema 
völlig anders anzugehen:
Tastaturtreiber --> Events --> Eventverteilung im System --> eigentliche 
Funktionalroutinen --> Signalisierungsroutinen.

W.S.

von F. F. (foldi)


Lesenswert?

W.S. schrieb:
> sondern was Echtes, also
> derart, daß die konkrete Hardware im Interface zum übergeordneten
> Programm überhaupt nicht mehr auftaucht.

Ich glaube das ist ja sein Ziel. So habe ich das verstanden.

Wenn du das schon gemacht hast, dann stelle doch mal hier was vor!

von BastiDerBastler (Gast)


Lesenswert?

Also ich hab die letzten Tage mal weiter drüber nachgedacht. Es gibts ja 
letztlich 2 Ebenen, die man zu betrachten hat.

1) Low-Level. Also das, was man sieht wenn man in die 
Peripherie-Treiber-Schicht reinschaut. Hier ist wohl kaum mit 
OOP-Techniken irgendetwas zu gewinnen.

2) High-Level. Also das, was man mit dem Low-Level-Teil macht. Hier 
hängt es doch sehr stark von der Applikation ab, wie man ihn umsetzt. 
Wenn man riesige dynamische Gebilde hat, kommt vielleicht OOP in Frage, 
C++ könnte einen hier unterstützen. Vielleicht aber auch nicht.

Ich habe mich dieses Wochenende nun mal zum Spaß auf Part 1 
konzentriert, und wie C++ hier vielleicht für übersichtlicheren Code 
sorgen kann. Meine Zielhardware ist STM32F4 und was mich immer gestört 
hat, waren die Textwände, die die Register initialisieren. Ich habe mir 
deshalb den GPIO-Teil herausgesucht, weil der wohl das erste ist, womit 
man in Berührung kommt. Herausgekommen ist eine Template-"Bibliothek", 
die folgendes zulässt:
1
namespace fsmc {
2
3
  namespace pins {
4
    using namespace gpio;
5
6
    //                                A0 - A9                        A10 - A15           A16 - A18         A19 - A23
7
    using addr_pins = pin_list< port_f<0,1,2,3,4,5,12,13,14,15>, port_g<0,1,2,3,4,5>, port_d<11,12,13>, port_e<3,4,5,6,2> >;
8
9
    //                                D0 - D3               D4 - D12                  D13 - D15
10
    using data_pins = pin_list< port_d<14,15,0,1>, port_e<7,8,9,10,11,12,13,14,15>, port_d<8,9,10> >;
11
12
    using nbl   = pin_list< port_e< 0, 1 > >;
13
    using nl    = pb<7>;
14
    using niord = pf<6>;
15
    using nreg  = pf<7>;
16
    using niowr = pf<8>;
17
    using cd    = pf<9>;
18
    using intr  = pf<10>;
19
20
    using int2 = pg<6>;
21
    using int3 = pg<7>;
22
  }
23
24
  void init_pins() {
25
    using namespace gpio;
26
27
    rcc::enable_gpio< pins::addr_pins, pins::data_pins, port_h< 9, 10, 11, 12, 13, 14 > >();
28
29
    configure< pins::addr_pins >( alternate(AlternateMode::Fsmc, OutputType::PushPull, Speed::High) );
30
    configure< pins::data_pins >( alternate(AlternateMode::Fsmc, OutputType::OpenDrain, Speed::High) );
31
32
    // nur so als beispiel
33
    configure< ph<9>, ph<10>  >( inout(Speed::Medium) );
34
    configure< ph<11>, ph<12> >( input );
35
    configure< ph<13>, ph<14> >( output(Speed::Medium) );
36
  }
37
38
}

Ich bitte die Formatierung zu entschuldigen, für das Forum währen ein 
paar Newlines vielleicht praktischer.

Ziele waren mit absteigender Priorität:
1) Der User-Code soll nur noch das beinhalten, was von Belang ist. 
Nämlich was die Pins bedeuten, und wie sie eingestellt werden sollen.
2) Vertretbarer Template-Aufwand
3) Qualität des Ausgespuckten Assembler-Codes

Die einzelnen Configure-Aufrufe erzeugen mit -O3 so ziemlich perfekten 
Assembler-Code mit meiner Toolchain. Allerdings ist es natürlich so, 
dass sie nur innerhalb des Aufrufs Registerzugriffe zusammenlegen 
können. Wer das Optimum für seine Plattform und seine Applikation 
herausbekommen will, muss in solchen Aspekten aber sowieso von der 
Modularisierung Abstand nehmen, weil die einzelnen Teile eben Ressourcen 
auf Hardwareebene teilen. Hier wäre eine externe Applikation hilfreich, 
der man sagt was man wie verwendet und die dann (meinetwegen auch ASM-) 
Code generiert, der die optimale Initialisierung durchführt. Gibt's 
sowas schon für AVR/STM32?

Ich weiß nicht, ob ich die entstandene Bibliothek jetzt weiterpflege und 
verwende, aber ich hatte auf jeden Fall große Freude, das zu entwickeln 
;) Ich finde, der User-Code braucht den Vergleich mit dem ihm 
entsprechenden STM-Hal-Code/Direkten Registerzugriffen nicht zu 
fürchten.

Ich habe außerdem ein Amulett mit +500 Widesrtand auf Troll, also spart 
es euch gleich ;)

von Thomas W. (wagneth)


Angehängte Dateien:

Lesenswert?

Ich denke man sollte erstmal wissen man man möchte.

Wie weit will man abstrahieren ?
Die Pins von unseren Kontrollern sind doch nicht nur GPIOs sondern auch 
UART, SPI, I2C, ADC...

Das muss doch von Anfang an bedacht werden.


Für eine Tastenentprellung macht es doch Sinn wenn wenn dem 
"Entprellobjekt" ein oder mehrere, vorher initialisiertes, "GPIO 
Objekte" übergeben wird.
Über die Läuft der Zugriff.

Jetzt braucht es immer noch einen Timer bei dem eine entsprechende 
Methode registriert ist.

Für sowas kenne ich nur SIGNAL und SLOT aus QT.
IMHO werden die durch dem MOC in CallBacks umgewandelt.

Wie würde es sinnvollerweise bei UART, SPI, I2C ADC aussehen ?

Tastenentprellung ist eine reine Softwaresache sowie
Software UART, Software SPI, Software I2C.

Allerdings wird das bei den Hardware Features schon schwieriger.
Immerhin muss sich das entsprechende Objekt die GPIOs "reservieren".

Dazu muss es jemanden geben der die Eigenschaften der Pins und Ports 
kennt.
Das weiss das GPIO Objekt nicht, denn dessen Klasse ist ein allgemeines 
Objekt.
Bei der Instanziierung muss ihm das erst beigebracht werden.
1
 GPIO gpioLED (portA, Pinnummer5, Datenrichtung, ...);
2
 LED led (&gpioLED);
3
4
 led.on();
5
 led.off();
6
 led.Blink(500ms);
7
 ...

Bei meinen Programmen versuche ich stark zwischen der "Logik" und dem 
ansprechen der Hardware zu trennen.
Leider kann man das in meinem Beispiel noch nicht so gut sehen.
(sehr heiss gestrickt, eigentlich alles passiert in der ISR)
Bei anderen (fertigen) Projekten würde jetzt hal.h separat inkludiert
und nur auf die bekannten (sozusagen public) Funktionen zugegriffen 
werden können.

Somit habe ich eine starke Trennung von der Hardwareansteuerung und der 
eigentlichen Programmlogik bzw. hier beim Impulse verlängern.

Aber ich muss für jedes neue Projekt so eine hal.h schreiben.
Das nimmt einen grossteil meiner "Programmierzeit" in Anspruch.
Auch das riesen Struct kostet Zeit.

Für jedes Einspritzventile und jeden Eingang muss ich das "HAL" Struct 
händisch erweitern, initialiseren...

Bei einer OOP Umsetzung, würde ich mir erhoffen, nur die GPIOs an die 
Injektor und "Sensing" Objekte zu übergeben.
...und noch irgendwie die Sensing mit dem entsprechenden Injektor Objekt 
zu "verheiraten" und beim Timer zu registrieren.

Es wäre für mich dann ein bisschen "egaler" wieviele Zylinder ich 
befeuern muss.

Auch wird es entsprechend aufgeräumter.
Bei Änderungen muss ich, wenn ich es richtig erdenke, an weniger Stellen 
nachbessern.

Stimmt das nicht ?

von chris_ (Gast)


Angehängte Dateien:

Lesenswert?

Fuer das High-Level Design der OOP gibt es UML.
Ich habe versucht mein Programm weiter oben in ein Diagramm zu fassen. 
Bitte entschuldigt die etwas duerftige Qualitaet, es ist auf dem Pad 
handgezeichnet.

von chris_ (Gast)


Lesenswert?

Thomas W. (wagneth) schrieb

>Jetzt braucht es immer noch einen Timer bei dem eine entsprechende Methode 
registriert ist.

Den Timer habe ich oben der Einfacheit halber durch eine While-Schleife 
realisiert.
1
  while(1)
2
  {
3
    key1.update();
4
    key2.update();
5
    key3.update();
6
    
7
    delay(100);
8
  }
Man koennte natuerlich auch eine richtige Timerklasse bauen, das haette 
das Beispielprogramm unnoetig verkompliziert.

von Thomas W. (Gast)


Lesenswert?

Chris,

ja das funktioniert.
Im Prinzip ist das EventDriven.

Ich möchte aber nicht im MainLoop warten.
Zumal die Laufzeiten/Verzögerung abhängig von den Laufzeiten sind.
In meiner ISR ist wenigstens der Start der Verarbeitung zu einem fixen 
Zeitpunkt gegeben.

Irgendwie muss ein Automatismus her bei dem man einen Handler 
registrieren kann...

von Thomas W. (Gast)


Lesenswert?

Chris,
der Trick ist doch das sauber Abstrahiert wird.

Es muss doch irgendwie ein GPIO Objekt o.ä. an Dein Key Objekt übergeben 
werden, oder ?

von chris_ (Gast)


Lesenswert?

>Es muss doch irgendwie ein GPIO Objekt o.ä. an Dein Key Objekt übergeben
>werden, oder ?

Ich weiß noch nicht so recht. Der ursprüngliche Vorschlag von Peter war 
ja, dass ein Key-Object in die Informationen über den Pin schon hat.
In meiner Umsetzung oben wäre das die Zeile mit

     uint8_t keyPin;

1
#define PRESSED 0
2
#define TOOGLE 1
3
#define SHORTLONG 2
4
5
class Key{
6
  public:
7
     uint8_t keyPin;
8
     Led * oneLed;
9
     uint8_t action;
10
     uint8_t oldKey;
11
     uint8_t keyToogleState;
12
    
13
      Key(){
14
        keyToogleState=0;
15
      };
16
    
17
      Key(uint8_t p){
18
        keyPin=p;
19
        pinMode(keyPin,INPUT);
20
        
21
        keyToogleState=0;
22
      };
23
  
24
  void addKeyPressedListener(Led *led){
25
    oneLed=led;
26
    action=PRESSED;
27
  };
28
  void addKeyToogleListener(Led *led){
29
    oneLed=led;
30
    action=TOOGLE;
31
  };
32
  void addShortLongListener(Led *led){
33
    oneLed=led;
34
    action=SHORTLONG;
35
  };
36
  
37
  void update()
38
  {
39
    uint8_t k=digitalRead(keyPin);
40
    if(k!=oldKey) keyToogleState=!keyToogleState;
41
    oldKey=k;
42
    
43
    switch(action)
44
    {
45
      case PRESSED:{
46
        oneLed->set(k);  
47
      };break;
48
      
49
      case TOOGLE:{
50
        oneLed->set(keyToogleState);
51
      };break;
52
      
53
      case SHORTLONG:{
54
      
55
      };break;
56
      default:{} break;
57
    }
58
59
  }; 
60
};

Wenn ich es richtig verstehe, möchtest Du das Konzept etwas erweitern 
d.h. dem Key-Objekt verschiedene Quellen zuordnen können anstatt nur 
IO-Pins.

von chris_ (Gast)


Lesenswert?

Thomas W. schrieb
>ja das funktioniert.
>Im Prinzip ist das EventDriven.
>Ich möchte aber nicht im MainLoop warten.
>Zumal die Laufzeiten/Verzögerung abhängig von den Laufzeiten sind.
>In meiner ISR ist wenigstens der Start der Verarbeitung zu einem fixen
>Zeitpunkt gegeben.

Eine While(1)-Loop in Main war auf die Schnelle so am einfachsten zu 
realisieren. Man könnte die Key.update() Funktionen auch in die 
ISR-hängen.
1
ISR ...
2
{
3
    key1.update();
4
    key2.update();
5
    key3.update();
6
}

Ich persönlich bin ein Fan von Interrupt-freien Programmen, weil sich 
Interrupts auch gerne mal "verklemmen" wenn man beim Programmieren nicht 
aufpasst.

von chris_ (Gast)


Lesenswert?

Lange Zeit war die OOP für mich ein Buch mit sieben Siegeln, weil ich 
noch aus der Zeit der Homecomputer Ära stamme. Dass ich es nicht 
verstanden habe hat mich aber so geärgert, dass ich mich intensiver 
damit befasst habe und einige Programme damit geschrieben habe. Um so 
länger man es tut, um so klarer wird die Philosophie der OOP.

Zunächst muss ich sagen: wer es wirklich lernen will, sollte nicht mit 
C++ sondern eher mit Python anfangen. Damit verschont man sich von den 
ganzen Fehlern die C++ als zusätzliches Feature anbietet.

In der OOP gibt es das Prinzip, dass eine Klasse einer anderen eine 
Nachricht schicken kann. Ich habe mich lange Zeit gefragt: Wie muss man 
das konkret programmieren? Wie kann ein Klasse einer anderen eine 
Nachricht schicken.

Oben im Beispielprogramm kann man das sehr gut sehen. In der Klasse Key 
ist ein Leerpointer für eine LED eingebaut. Man muss einen Key 
initialisieren, in dem man dem Key den Pointer auf eine LED mitgibt. 
Danach kennt der Key die LED und kann ihr jetzt "Nachrichten" senden, 
indem er einfach eine Funktion in der LED aufruft.

von (prx) A. K. (prx)


Lesenswert?

Peter Dannegger schrieb:
> Schön wäre es noch, wenn die Klasse automatisch den passenden
> Interrupthandler aktiviert.

Auf Plattformen wie AVR und CMSIS sind die Namen der Interrupt-Handler 
in C Konvention fest vorgegeben und leiten sich von Devicenamen wie 
UART0 ab. Zudem benötigt eine Handler als Teil einer Klasseninstanz den 
Pointer auf diese Instanz.

Die Zuordnung eines vom Entwicklungsssytem vorgegebenen Handlers zur 
passenden Funktion einer Instanz muss also unweigerlich von Hand 
erfolgen, als Wrapper-Funktion um den eigentlichen Handler, mit den 
Namen des Handlers. Darin wird dann über den Pointer auf die Instanz der 
eigentliche Handler aufgerufen, beispielhaft:

extern "C" {
  void UART0_Receive_Buffer_Full(void) // offizieller Name
  {
    UART0object->Receive_Buffer_Full(); // C++ member funktion
  }
}

: Bearbeitet durch User
von A. B. (funky)


Lesenswert?

chris_ schrieb:
> Ich persönlich bin ein Fan von Interrupt-freien Programmen

Ein wesentliches Feature von MC einfach mal über Board werfen? Naja.
Wenn man auf den Stromverbrauch achten muss, kommt man um Interrupts 
nicht herum. Und auch sonst nützen und vereinfachen die mehr, als das 
man sie vermeiden müsste.

von chris_ (Gast)


Lesenswert?

>Ein wesentliches Feature von MC einfach mal über Board werfen?

Guck mal, hier gibt es einen sehr innovativen Controller ganz ohne 
Interrupts:
http://www.parallax.com/microcontrollers/propeller

von Conny G. (conny_g)


Lesenswert?

A. K. schrieb:
> Peter Dannegger schrieb:
>> Schön wäre es noch, wenn die Klasse automatisch den passenden
>> Interrupthandler aktiviert.
>
> Auf Plattformen wie AVR und CMSIS sind die Namen der Interrupt-Handler
> in C Konvention fest vorgegeben und leiten sich von Devicenamen wie
> UART0 ab. Zudem benötigt eine Handler als Teil einer Klasseninstanz den
> Pointer auf diese Instanz.
>
> Die Zuordnung eines vom Entwicklungsssytem vorgegebenen Handlers zur
> passenden Funktion einer Instanz muss also unweigerlich von Hand
> erfolgen, als Wrapper-Funktion um den eigentlichen Handler, mit den
> Namen des Handlers. Darin wird dann über den Pointer auf die Instanz der
> eigentliche Handler aufgerufen, beispielhaft:
>
> extern "C" {
>   void UART0_Receive_Buffer_Full(void) // offizieller Name
>   {
>     UART0object->Receive_Buffer_Full(); // C++ member funktion
>   }
> }

Je nachdem wie spendabel man mit Taktzyklen im Int sein möchte könnte es 
über eine Int-Handler Registry erfolgen.

Es gibt ein Objekt "IntDispatcher" das den Keys bekannt ist und sie 
registrieren sich dort mit "intDispatcher.register(&intHandler)".
Und der eigentliche Interrupt ruft grundsätzlich 
intDispatcher->Timer0OvfInt() auf, der sich dann um seine Kunden 
kümmert.

Alternativ gibt es eine IntTimer0OvfHandler-Basisklasse von der Klassen 
wie die Key-Klasse erben und automatisch einen Handler für den Int 
haben, der dann mit aufgerufen wird.

von Conny G. (conny_g)


Lesenswert?

chris_ schrieb:
>>Ein wesentliches Feature von MC einfach mal über Board werfen?
>
> Guck mal, hier gibt es einen sehr innovativen Controller ganz ohne
> Interrupts:
> http://www.parallax.com/microcontrollers/propeller

Ja, der löst den Bedarf an Ints alternativ mit 8 Cores, d.h. statt 
Interrupts die den laufenden Code unterbrechen kann ein Core gemütlich 
auf ein Ereignis warten. Finde den auch sehr cool.

von (prx) A. K. (prx)


Lesenswert?

Conny G. schrieb:
> Ja, der löst den Bedarf an Ints alternativ mit 8 Cores, d.h. statt
> Interrupts die den laufenden Code unterbrechen kann ein Core gemütlich
> auf ein Ereignis warten. Finde den auch sehr cool.

XMOS ist ähnlich, aber weit besser konstruiert.

von Ralf G. (ralg)


Lesenswert?

chris_ schrieb:
1
> ISR ...
2
> {
3
>     key1.update();
4
>     key2.update();
5
>     key3.update();
6
> }

Sowas ist aus meiner Sicht tödlich. Funktionen in einer ISR aufrufen, 
ist ja schon in C verpönt. Unter günstigen Umständen macht der Compiler 
da ein paar wenige Instruktionen draus, wenn er die Funktion kennt! Aber 
bestimmt nicht, wenn die Funktion - irgenwo weit weg - in einer anderen 
Datei als Methode einer Klasse deklariert wurde.

'key1.update();' sieht ja wenigstens noch wie ein Funktionsaufruf aus. 
Aber bei 'Beleuchtung.Led1 = an' können sich jede Menge Funktionsaufrufe 
dahinter verbergen.

von (prx) A. K. (prx)


Lesenswert?

Ralf G. schrieb:
> Funktionen in einer ISR aufrufen, ist ja schon in C verpönt.

Ach? Warum? Weil das dir vertraute Interrupt-System keine Priorisierung 
kennt, somit kein Interrupt länger als 10 Takte dauern darf?

von Ralf G. (ralg)


Lesenswert?

Das:
Conny G. schrieb:
> Alternativ gibt es eine IntTimer0OvfHandler-Basisklasse von der Klassen
> wie die Key-Klasse erben und automatisch einen Handler für den Int
> haben, der dann mit aufgerufen wird.
läuft dann auf sowas:
Conny G. schrieb:
> Es gibt ein Objekt "IntDispatcher" das den Keys bekannt ist und sie
> registrieren sich dort mit "intDispatcher.register(&intHandler)".
> Und der eigentliche Interrupt ruft grundsätzlich
> intDispatcher->Timer0OvfInt() auf, der sich dann um seine Kunden
> kümmert.
hinaus, da die ISR nicht an eine Klasse gebunden werden kann. Also wenn, 
dann nur mit Tricks (Funktionszeiger). (Vielleicht auch nur in der 
jetzigen Konstellation?)

Wenn sich jetzt eine Klasse (über Ihre Basisklasse) sozusagen bei der 
Timer-ISR anmeldet, um von ihr später mal benachrichtigt zu werden, wenn 
'was anliegt', dann muss das ja in einer Liste vermerkt werden. Die 
Liste wird in der ISR dann abgearbeitet. Wenn's gut läuft, reicht das 
vielleicht, der benachrichtigten Klasse nur einen Wert zu übergeben. 
Ohne Funktionsaufruf! Wenn nicht, ...

Billig ist's auf jeden Fall nicht (Speicher/ Rechenzeit).

von Ralf G. (ralg)


Lesenswert?

A. K. schrieb:
> Ach? Warum? Weil das dir vertraute Interrupt-System keine Priorisierung
> kennt, somit kein Interrupt länger als 10 Takte dauern darf?

Vielleicht. Kann sein.
Nur irgenwann ist trotzdem Schicht im Schacht.
Bei 'C++ auf einem MC, wie geht das?' denke ich nicht an 'Einen' (jeder 
sucht sich seinen raus und alle reden aneinander vorbei) sondern an 
'Alle' im Sinne von universell.

von Conny G. (conny_g)


Lesenswert?

Ralf G. schrieb:
> Billig ist's auf jeden Fall nicht (Speicher/ Rechenzeit).

Deshalb schrieb ich auch, je nachdem wie spendabel man für Komfort sein 
will.
Am Ende ist das manuelle Mappen von ISR nach Methode auch akzeptabel. 
Wenn man will könnte man es in einen Mechanismus packen.
Dann läuft man aber wiederum Gefahr in die 
Arduino-Vollkasko-Bequemlichkeit zu kommen, die dann richtig viel 
Resourcen kosten kann.

D.h. der Knackpunkt an einer cleveren OOP-Implementation muss sein: 
Strukturierung und Fehlervermeidung, nicht alleine Komfort.
Und da wäre diese Vorgehensweise (Automatismus für ISRs) schon an oder 
vielleicht sogar schon über der Grenze des Sinnvollen.
Denn über die genaue ISR-Architektur muss man sich als Entwickler 
unbedingt Gedanken machen, das ist gefährlich, wenn ein Framework einem 
suggeriert es sei alles easy.

von Ret (Gast)


Lesenswert?

A. K. (prx) schrieb:

Conny G. schrieb:
>> Ja, der löst den Bedarf an Ints alternativ mit 8 Cores, d.h. statt
>> Interrupts die den laufenden Code unterbrechen kann ein Core gemütlich
>> auf ein Ereignis warten. Finde den auch sehr cool.

> XMOS ist ähnlich, aber weit besser konstruiert.

Alle Jahre wieder, siehe auch Abdul's Frage: "Was wurde aus .."

Beitrag "XMOS jemand?"

;)

von Ralf G. (ralg)


Lesenswert?

Da schieb' ich mal etwas Literatur rüber:
http://www.avr-cpp.de

(aus meiner Sicht: nicht gerade ressourcen-schonend)

von Ben (Gast)


Lesenswert?

Finde die Diskussion auch interessant.
Ich würde bis hierher mal festhalten:

OOP mittels C++ auf Mikrocontrollern geht und macht Sinn. Dass es die 
Fehlerquellen reduzieren und leserlichen Code produzieren kann bei 
gleichzeitig geringerer Einarbeitungszeit sieht man am Beispiel des 
Arduino (hier blende ich mal die typischen Anfängerfragen aus, die nicht 
dem Arduino-System verschuldet sind sondern schlichtweg aus Unwissen 
oder Faulheit des Fragenstellers resultieren).
Die Probleme der Arduino-Libs (Laufzeit, Ressourcen) könnte man lösen, 
wenn man sich mal hinsetzt und gründlich Gedanken macht. Das Beispiel 
oben mit dem Portsetzen (was in einem sbi resultiert) beweist dies.

von Conny G. (conny_g)


Lesenswert?

Ret schrieb:
> A. K. (prx) schrieb:
> Conny G. schrieb:
>>> Ja, der löst den Bedarf an Ints alternativ mit 8 Cores, d.h. statt
>>> Interrupts die den laufenden Code unterbrechen kann ein Core gemütlich
>>> auf ein Ereignis warten. Finde den auch sehr cool.
>> XMOS ist ähnlich, aber weit besser konstruiert.
>
> Alle Jahre wieder, siehe auch Abdul's Frage: "Was wurde aus .."
> Beitrag "XMOS jemand?"
> ;)

Danke für die Inspiration, hab mir grad ein StartKit bestellt. Habe 
gerade eine Aufgabenstellung, wo der Propeller eigentlich gut passen 
würde, aber genau dort seine Schwäche hat (RAM-Daten-Transfer zwischen 
COGS).
Mal gespannt, ob das mit dem Xmos besser ist.

von chris_ (Gast)


Lesenswert?

>Da schieb' ich mal etwas Literatur rüber:
>http://www.avr-cpp.de

Lustig, das Bildchen entspricht genau der von mir verwendeten Struktur:
http://www.avr-cpp.de/lib/exe/fetch.php?w=500&h=&cache=cache&media=aggr_asso.jpg

von A. B. (funky)


Lesenswert?

chris_ schrieb:
> Guck mal, hier gibt es einen sehr innovativen Controller ganz ohne
> Interrupts:
> http://www.parallax.com/microcontrollers/propeller

okay, aber das ist nun nicht gerade ein Feld-Wald-und-Wiesen 
Controller...
Auf anderen Controllern habe ich überlicherweise nur einen Core, und 
entweder ich polle andauernd alle I/Os oder ich benutze IRQs.

von chris_ (Gast)


Lesenswert?

Klar, der Propeller hat eine sehr spezielle Architektur. Bei anderen 
Prozessoren gibt es viele Anwendungsfälle, in denen Interrupts 
unerlässlich sind wie z.B. Treiber mit Buffer für die serielle 
Schnittstelle.

Ich meine zu erkennen, dass das Beispiel von SisyAvr oben auch keine 
Interrupts benutzt.

Hier sieht man, dass sie in der Basisklasse vordefinierte 
Timerfunktionen haben, die sie bei Bedarf überschreiben:

http://www.avr-cpp.de/doku.php?id=timer

von Thomas W. (Gast)


Lesenswert?

@Chris:
...und wo ist in Deinem Bildchen der Controller ?
(damit meine ich seine Pins)

Abstrahiert hat man doch folgendes Szenarion :
- Entprellroutine muss regelmässig aufgerufen werden.
- Entprellroutine muss irgendwo was machen, vielleicht Dein MyObjekt...
- MyObjekt triggert das LED Object. Dieses Entscheidet was am GPIO 
passieren soll.
- GPIO Object ist das unterste Bindeglied.

Das Einzige was pollt ist die Entprellroutine.
Weil sie es muss.

Alle anderen Objecte arbeiten event getriggert.
Kürzer geht es nicht.

In Deinem Bild sehen sich auch noch irgendwie
LED und Entprellung / KEY.
Wofür ? Das wäre wieder komplexität.

von chris_ (Gast)


Lesenswert?

Ich denke es ist so wie es oben BastiDerBastler beschrieben hat:

>1) Low-Level. Also das, was man sieht wenn man in die
>Peripherie-Treiber-Schicht reinschaut. Hier ist wohl kaum mit
>OOP-Techniken irgendetwas zu gewinnen.
>
>2) High-Level. Also das, was man mit dem Low-Level-Teil macht. Hier
>hängt es doch sehr stark von der Applikation ab, wie man ihn umsetzt.
>Wenn man riesige dynamische Gebilde hat, kommt vielleicht OOP in Frage,
>C++ könnte einen hier unterstützen. Vielleicht aber auch nicht.

Ich wollte mich mit Punkt 2 beschäftigen, oben wurden ja schon ziemlich 
effiziente Methoden der Hardwareabstraktion gefunden.

Thomas W. schrieb
>...und wo ist in Deinem Bildchen der Controller ?
>(damit meine ich seine Pins)

Zugegebenermaßen ist mein Bildchen dahingehend etwas irreführend, weil 
ich ja in Wirklichkeit gar keine Klasse habe, die sich Main nennt. Es 
ist einfach die Main-Loop des Arduino:
1
void loop() {
2
  
3
  Led led1(12); // led on x
4
  Led led2(13);
5
  Led led3(11);
6
  
7
  Key key1(4);
8
  Key key2(5);
9
  Key key3(6);
10
    
11
  key1.addKeyPressedListener(&led1);
12
  key2.addKeyToogleListener(&led2);
13
  key3.addShortLongListener(&led3);
14
  
15
  
16
  while(1)
17
  {
18
    key1.update();
19
    key2.update();
20
    key3.update();
21
    
22
    delay(100);
23
    Serial.println(key2.keyToogleState);
24
  }
25
}
Wie man sieht, werden die LEDs und KEYs in der "loop" angelegt.
Deshalb verwende ich auch das die "Komposition" in meiner Handzeichnung, 
weil die LEDs und KEYs mit der Erzeugung der Main-Loop entstehen:
http://wiki.adolf-reichwein-schule.de/images/8/8f/Uml-assoziationen03.png
Für das Bekanntmachen der LEDs bei den Tastern verwende ich die 
Aggregation, weil beide unabhängig von einander existieren.

>Abstrahiert hat man doch folgendes Szenarion :
>- Entprellroutine muss regelmässig aufgerufen werden.
Das stimmt. In meinem Programm passiert das durch Key.update(), weil die 
Entprellroutinen direkt im Key implementiert sind ( bzw. nur ansatzweise 
implementiert sind, weil ich zu faul war und dachte das wäre 
ersichtlich) . Wenn Du es lieber hast, könnten wir die Entprellroutinen 
raus ziehen. Ich bin mir aber noch nicht sicher, ob das wirklich viel 
bringt. Ich gehe davon aus, dass wir die Taste entprellen wollen und 
nicht die serielle Schnittstelle.

von Thomas W. (Gast)


Lesenswert?

Chris,

genau deswegen würde ich als LowLevel einen GPIO, SPIHardware, SPI oder 
sonstwas Objekt an die nächste Instanz übergeben.

Der Trick ist doch das Du mit jedem Verschiedenen BitWackelTeil, z.b. 
die LED, jedes mal das Portgewackel neu erfinden musst.
Wiederverwertbarkeit von Code ist doch auch eine Forderung an OOP.

Man kann den Handler ja auch in der ISR manuell eintragen oder
man baut ein Wrapper Objekt.
Savr oder Dein Link von oben machen das doch eigentlich schon ganz net 
vor.
Wobei ich bei letzterem nicht so genau weiss wie es mit der Abstraktion 
aussieht.

Bei einem LC Display musst Du gleich mehrere Pins an das Objekt 
weitergeben,
man müsste das Bitgewackel wieder selbst anfassen.
Wenn es aber auf das Timing ankommt muss die nächst höhere Instanz ran.

Sehe ich das Richtig,
unbenutzte und überladene Mehtoden werden nicht mitgelinkt ?

von BastiDerBastler (Gast)


Lesenswert?

Es wird nur das mitgelinkt, was referenziert wird. Zumindest in 
optimierten Builds. Wenn eine Funktion überall ge-inlined wird, wird 
auch sie nicht ins Image übernommen.

von chris_ (Gast)


Lesenswert?

>Der Trick ist doch das Du mit jedem Verschiedenen BitWackelTeil, z.b. die LED, 
jedes mal das >Portgewackel neu erfinden musst.
>Wiederverwertbarkeit von Code ist doch auch eine Forderung an OOP.

Ok, mache doch mal 4-5 konkrete Beispiele. So in der Art
 "wird eine Taste gedrueckt, soll eine Led leuchten" .
Dann koenen wir darueber nachdenken, wie das umzusetzen waere.

von Robin E. (why_me)


Lesenswert?

Muss denn hinter jeder Aktion gleich eine Reaktion stehen? Die Aktionen 
werden ja meist in einer ISR abgerufen und dort ist meist eh keine Zeit 
um groß etwas zu rechnen, etc. Also wird in der Regel eine Flag gesetzt 
oder Daten in einen Buffer geschrieben Und das ganze dann im 
Hauptprogramm abgearbeitet. Ob die LED jetzt nach 1µS oder nach 50ms 
leuchtet ist in der Regel egal und wird sowieso nicht wahrgenommen.

Wichtiger als OOP bis auf das letzte Bit des Controllers runter zu 
bringen ist doch eine einheitliche Basis, auf der man aufbauen kann. Vom 
Programmablauf ähneln sich ja die meisten Programme ja mehr oder 
weniger:
- Hardware initialisieren, Systemtimer starten. In den ISR Daten in 
einen Buffer schreiben und eine Flag für das Hauptprogramm setzten. Im 
Hauptprogramm wird sich wenn nichts mehr zu tun ist, schlafen gelegt.

Für ein Setze_Bit braucht es noch keine Klasse, aber es wird wohl in 
sehr vielen Verwendung finden. Deswegen wäre der Erste schritt eine Art 
Betriebssystem zu schreiben, das definierte Funktionen zur Verfügung 
stellt und die Hardware entsprechend initialisiert. Also Systemtimer, 
UART, I/O,... nach defines o.ä. initialisiert. Letztenendes Alles was 
direkten Zugriff auf die Hardware hat muss bereitgestellt werden.

Auf diesen Grundfunktionen können dann alle anderen Funktionen aufbauen, 
ob eine Softuart Klasse, Tastenentprellen,... alle haben die gleichen 
Hardwareschnittstellen und greifen auf die gleichen Funktionen zu. 
Dadurch wird auch schon eine gewisse Wiederverwentbarkeit des Codes 
ermöglicht. Auf einem Neuen Controller müssen halt nur genau diese 
Grundfunktionen bereit gestellt werden. Aber eine Initialisierung der 
Hardware ist ja so oder so nötig.

Und mal nebenbei erwähnt, bei euren Beispielen mit dem Hinzufügen zu den 
ISR habt ihr einen entscheidenden Punkt vergessen, die häufigkeit des 
aufrufens, nicht jede Funktion muss in jeder Timer ISR aufgerufen 
werden. Ich würde sogar noch einen Schritt weiter gehen und einen Offset 
einplanen damit man z.B. eine funktion nur in geraden und eine andere 
Funktion nur in ungeraden Timer ISRs aufrufen kann und somit die Last 
verteilung beeinflussen.

: Bearbeitet durch User
von Bastler (Gast)


Lesenswert?

>Der Trick ist doch das Du mit jedem Verschiedenen BitWackelTeil, z.b. die LED,
>jedes mal das >Portgewackel neu erfinden musst.
>Wiederverwertbarkeit von Code ist doch auch eine Forderung an OOP.

Noch besser wird's, wenn zig verschiedene Klassen auf die Klasse 
BitWackel aufbauen. Die brauchen dann für eine neue Wackelart nur eine 
dafür vorgesehene Implementierung, d.h. statt WackelAVR ein WackelARM, 
und schon Funktionieren sie auf der neuen Kiste. Zwar nur theoretisch, 
aber das ist mehr als "gar nicht". Und wenn man sich beim Wackelart 
Design nicht zu blöd angestellt hat, dann spielt auch die Realität mit.

von Thomas W. (wagneth)


Lesenswert?

Ich behaupte wir haben hier nicht nur blinkende LEDs,
in meinem nicht OOP Beispiel wird ein Signal in 100µs Schritten 
abgetastet und auch wieder ausgegeben.

In der Interrupt.h wird doch der ISR Handler per Makro und 
Funktionszeiger gebaut.
Das müsste doch auch mit einer Methode gehen.
Ahh ein Methodenzeiger !
So könnte man vielleicht den Wrapper los werden.

...und jetzt noch die virtuelle Handler Methode der Basis Timerklasse in 
der Implementierung damit überschreiben.
Schon hat man seine eigene Timerklasse in der man in den Handler 
eintragen kann was man will.
---


Eine meiner Forderungen wäre das dass Ansprechen der Hardware gekapselt 
von der anderen Programmlogik ist.
Dabei würde es doch schon reichen wenn man
den Buchegger aufbläst 
Beitrag "Re: C++ auf einem MC, wie geht das?"
Seine pin Klasse kann auch das DDR zum auslesen setzen...

Nach dem Stil noch die Hardwarefeatures (spi,uart...) aufgesetzt und Gut 
ists.

von Daniel A. (daniel-a)


Lesenswert?

Thomas W. schrieb:
> In der Interrupt.h wird doch der ISR Handler per Makro und
> Funktionszeiger gebaut.
> Das müsste doch auch mit einer Methode gehen.
> Ahh ein Methodenzeiger !
> So könnte man vielleicht den Wrapper los werden.
Ein Memberfunktion-pointer ist aber anders als ein funktionspointer. Der 
aufruf erfordert zwingend das dazugehörige object. Man müsste also eine 
templateclasse nehmen, den pointer auf eine darin enthaltene statische 
metode holen, welche die als templateparameter übergebene funktion auf 
das ebenfalls als templateparameter übergebenen objekt aufruft.

von (prx) A. K. (prx)


Lesenswert?

Daniel A. schrieb:
> Man müsste also eine
> templateclasse nehmen, den pointer auf eine darin enthaltene statische
> metode holen, welche die als templateparameter übergebene funktion auf
> das ebenfalls als templateparameter übergebenen objekt aufruft.

Mit Pointern kommt man nicht weiter, wenn die Sprung/Vektorleiste für 
Interrupt-Handler direkt feste Namen entsprechend C Namenskonvention 
anspringt. Da müsste man schon CMSIS oder die avr-libc über Bord werfen 
und eine ziemlich eigene Methode für die Vektorleiste erfinden.

Krempelt man jedoch nicht alles um, dann müsste man bei diesem Prinzip 
der statischen Methode einen auf Linker-Ebene frei wählbaren 
Funktionsnamen verpassen können (irgendein GCC Attribut?) und den dem 
Template übergeben können. Geht das?

Bei ARM7/9 Prozessoren hat man es leichter, denn da hat man 32-Bit Werte 
beliebiger Konvention in Registern des Interrupt-Controllers und könnte 
sogar an Stelle einer Funktionsadresse einen Pointer auf einen Pointer 
auf eine Memberfunktion dort reinschreiben.

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


Lesenswert?

Ich habe mal einige ideen dazu gesammelt:
 * Vieleicht kann man da mit dem gcc alias attribut was drehen
 * Mit extern c kann das name mangeling deaktiviert werden. Vileicht 
kann man gcc ja eien ensprechend benannten funktionszeiger unterjubeln?
 * Man macht ein Makro, welches erst die memberfunktion als inline 
deklariert, dann in einem extern block das isr makro benutzt, in der isr 
die memberfunktion aufruft, und danach die memberfunktion definiert.

von chris_ (Gast)


Angehängte Dateien:

Lesenswert?

Einen konkretes Anwendungsszenario hinzuschreiben, scheint gar nicht so 
einfach ....
Seis drumm, im Anhang ein Bild zur Pinklasse. Wahrscheinlich ist es gut 
OutPin und Innpin abzuleiten, dann kann man gleich dir 
Richtungsinitialisierung einbauen.

von F. F. (foldi)


Lesenswert?

chris_ schrieb:
> Einen konkretes Anwendungsszenario hinzuschreiben, scheint gar nicht so
> einfach ....
> Seis drumm, im Anhang ein Bild zur Pinklasse. Wahrscheinlich ist es gut
> OutPin und Innpin abzuleiten, dann kann man gleich dir
> Richtungsinitialisierung einbauen.

Könnte man so noch weiter untergliedern, in die Funktion des jeweiligen 
Pins.

von Thomas W. (Gast)


Lesenswert?

Dann bräuchte man noch zusätzlich eine TriState Klasse.
Oder würde man das über den InPin machen ?

von chris_ (Gast)


Lesenswert?

Jetzt wären wir wieder beim Konkreten: wie soll eine Tri-State Klasse 
verwendet werden? Ich habe auch schon darüber nachgedacht, mir ist aber 
nichts eingefallen.

Man könnte es aber auch fast so belassen, wie es Karl-Heinz oben gemacht 
hat:

http://www.mikrocontroller.net/attachment/237464/PinTest.cpp

Dem Pin eine Funktion toOutput mitgeben, mit dem er auf Output gestellt 
wird. Meiner Meinung nach fehlt dort aber die Funktion toInput, bei der 
das DDR wieder auf Input umgestellt werden kann. Außerdem sollte das Pin 
in der Inittialisierung auf "input" gestellt werden.

von Thomas W. (Gast)


Lesenswert?

Die Idee mit dem Methodenaufruf für die ISR geht nicht weil der ISR 
Vector vom Linker aufgelöst wird ?
Dachte immer das wäre ein reinrassiger Funktionszeiger.
Die sind mehr als nur ein Jump auf Assemblerebene ?
(Kontext etc...)

---


Denke für Polling wird das so funktionieren.
Beim PinChange Interrupt kehrt sich die Richtung um.

In meinem nicht OOP Beispiel ist (wird wenn der entsprechende Solver* 
eingebaut ist) alles nebenläufig sein.


Das könnte sich am ende beissen.
Hier wird immer eine ganze Kette aufgerufen.
Oder verrenne ich mich gerade ?


* : Der Solver wird eine multiplikative und additive "korrektur" 
einbringen. Abgetastet wird mit dem ISR Takt, genauso die Ausgabe.
Gerechnet wird im Solver. (MainLoop)

PS: Hängt wohl an "meiner Betriebsart" das ich aus den vorhandenen 
Modell immer alle pins Zyklisch setze, auch wenn sich nichts verändert 
hat.

von Ben (Gast)


Lesenswert?

Diese ganzen Klassen hören sich erstmal toll an, vor allem wenn man den 
Gedanken weiterspinnt und sich UART, LCD und SPI Klassen anlegt. Eine 
LCD Klasse erhält noch die benötigten Pins mit übergeben (per Template 
oder im Konstruktor).

Aber wie würde man vorgehen, wenn es von einer internen Hardware mehrere 
Instanzen gäbe, Beispiel UART?
Ich müsste entweder das komplette Registersetup per Template/Konstruktor 
übergeben, oder ich halte für jede Instanz eine eine Liste im RAM vor.
Bei letzterem bin ich mir aber nicht sicher wie effizient der Compiler 
dann die Zugriffe umsetzen kann...

von Daniel A. (daniel-a)


Lesenswert?

Man könnte die Pins und die Hardware (z.B. lcd) auch mit einem kabel 
verbinden. Man hat dann irgendwo die classe lcd, und irgendwo die klasse 
wire. Die klasse wire wird dann in einem eigenen file mit den pins 
(welche eine eigene classe haben) und der Hardware ( classe lcd_xyz) 
instanziert. Dadurch kann man dann ganz einfach auf unterschiedlichen 
hardwareconfigurationen unterschiedliche verbindungen definieren.

von Thomas W. (Gast)


Lesenswert?

Naja,

ich denke man sollte schon die verschiedenen Schichten stark trennen.

Niedrige schicht: GPIO, SPI, TWI --- Direkte Hardwaremanipulation.

Höhe Schicht : LCD, Schiebergister --- Übersetzt Datenstrom, 
Anforderungen in LowLevel Ebene.

Höchste Schicht : Anwendung/Solver die auf die über die unteren 
schichten auf die Hardware zugreifen. --- komplette höhere Logik,


Denke darunter versteht man einen Hardware Abstration Layer.

von Thomas W. (Gast)


Lesenswert?

Zu langsam.

von chris_ (Gast)


Lesenswert?

>ich denke man sollte schon die verschiedenen Schichten stark trennen.
>Niedrige schicht: ... SPI,

Für die SPI gibt es schon mal eine Vorlage:

https://code.google.com/p/arduino/source/browse/trunk/libraries/SPI/SPI.cpp?r=1044

von Thomas W. (wagneth)


Lesenswert?


von Ralf G. (ralg)


Angehängte Dateien:

Lesenswert?

Karl Heinz schrieb:
> Ich denke ein wirklich
> nicht unwesentlicher Punkt wäre es, für diese Low-Level Sachen ein gut
> aufgebautes Framework zu haben, welches diese ganzen Port, Pin, DDR
> Sachen sauber kapselt OHNE dabei Codemässig stark aufzutragen.

Ich hab' da mal was probiert... (Siehe Anhang.)
So eine Pin-Klasse trägt überhaupt nicht auf. Das alles reduziert sich 
auf ein paar Befehle.
Jetzt habe ich aber ein Problem als Hobby-Programmierer! Wenn ich nun 
versuche von 'mcPin' eine Klasse 'mcKey' abzuleiten, dann fängt der 
Compiler an, liederlich zu arbeiten ;-) Auf einmal wird nichts mehr 
ge-inlined.
Was brauch' ich jetzt für Zaubersprüche, um dem Compiler auf die Sprünge 
zu helfen?

von tictactoe (Gast)


Lesenswert?

Probiere mal, die mcPin bzw. mcKey Variablen in main() const zu 
definieren. Dazu musst du auch alle Member-Funktionen der Klassen const 
deklarieren.

von Ralf G. (ralg)


Lesenswert?

Ralf G. schrieb:
> Was brauch' ich jetzt für Zaubersprüche

-O3

von T. Wagner (Gast)


Lesenswert?

Wer hatte eigentlich mit dem C++ angefangen ?
:)

von Karl Käfer (Gast)


Lesenswert?

Hallo Karl Heinz,

Karl Heinz schrieb:
> Das hab ich gerade eben erst erfunden :-)
> Und es ist das leidige Problem, zuverlässig aus der Portangabe die DDR
> Adresse zu ermitteln.

Warum die Information nicht einfach mitnehmen? Der Optimizier will sich 
ja auch nicht langweilen.
1
#include <avr/io.h>
2
3
class Pin {
4
public:
5
    volatile uint8_t* ddr;
6
    volatile uint8_t* port;
7
    volatile uint8_t* pin;
8
    uint8_t num;
9
10
    Pin(volatile uint8_t* ddr, volatile uint8_t* port, 
11
        volatile uint8_t* pin, uint8_t num):
12
        ddr(ddr), port(port), pin(pin), num(num)
13
    {}
14
};
15
16
class InputPin : public Pin {
17
public:
18
    InputPin(volatile uint8_t* ddr, volatile uint8_t* port, 
19
       volatile uint8_t* pin, uint8_t num):
20
        Pin(ddr, port, pin, num) {
21
        *this->ddr &= ~(1 << this->num);
22
    }
23
    int isHigh(void) { return (*this->pin & (1 << this->num)); };
24
    int isLow(void) { return !((*this->pin & (1 << this->num))); };
25
};
26
27
class OutputPin : public Pin {
28
public:
29
    OutputPin(volatile uint8_t* ddr, volatile uint8_t* port, 
30
       volatile uint8_t* pin, uint8_t num):
31
        Pin(ddr, port, pin, num) {
32
        *this->ddr |= (1 << this->num);
33
    }
34
    void setHigh(void) { *this->port |= (1 << this->num); };
35
    void setLow(void)  { *this->port &= ~(1 << this->num); };
36
    void operator=(bool const &parm) {
37
      if(parm) { this->setHigh(); }
38
      else     { this->setLow(); }
39
    }
40
};
41
42
43
int main(void) {
44
45
    OutputPin ledPin (&DDRD, &PORTD, &PIND, PD0);
46
    InputPin  btnPin (&DDRB, &PORTB, &PINB, PB0);
47
48
    while(1) {
49
        //...
50
        ledPin.setHigh();
51
        ledPin.setLow();
52
        ledPin = btnPin.isHigh();
53
    }
54
}

Da sind jetzt schon ein paar C++-Features (Klassen, Vererbung, Überladen 
von Operatoren) drin, die das (IMHO) am Ende deutlich les- und wartbarer 
machen als den entsprechenden C-Code. Und das ist meines Erachtens dann 
auch das eigentliche Ziel von C++: den Code besser zu strukturieren und 
dessen Wartbarkeit und Wiederverwendbarkeit zu erhöhen.

Nebenbei habe ich den Code mal mit avr-c++ -O3 übersetzt, mit avr-strip 
bearbytet, jeweils einmal mit überladenem "="-Operator und einmal ohne 
(und dafür [1]) in der main()-Loop. Ergebnis: in beiden Fällen ist das 
Kompilat 524 Bytes groß (-mmcu=atmega328).

Um einmal zu schauen, wie groß das Kompilat ist, wenn ich genau dieselbe 
Funktionalität in C implementiere, habe ich den Code aus [2] benutzt und 
mit denselben Compilereinstellungen mit avr-gcc übersetzt.

Wie soll ich sagen: auf meinem Kubuntu 14.04 LTS mit avr-gcc 4.8.2 ist 
auch das C-Kompilat exakt 524 Bytes groß, genau wie mit C++. Übrigens 
dasselbe, wenn ich die unnötige Zeile weglasse. Scheint ja wirklich ein 
böses Zeug mit einem riesigen Overhead zu sein, dieses C++. Oder so. ;-)

Wie dem auch sei: als jemand, der seine Brötchen als UNIX-Guru verdient 
und daher nicht täglich Codezeilen wie "DDRB &= ~(1 << PB0)" sieht, 
finde ich die C++-Variante viel les- und wartbarer als die Version in C.

Just my centz,
Karl

[1] Code:
1
        if(btnPin.isHigh()) {
2
          ledPin.setHigh();
3
        } else {
4
          ledPin.setLow();
5
        }

[2] Code:
1
#include <avr/io.h>
2
3
int main() {
4
5
    DDRD |= (1 << PD0);  // set OUTPUT
6
    DDRB &= ~(1 << PB0); // unnecessary: is already an INPUT
7
8
    while(1) {
9
10
      PORTD |= (1 << PD0);
11
      PORTD &= ~(1 << PD0);
12
13
      if(PORTB & (1 << PB0)) {
14
        PORTD |= (1 << PD0);
15
      } else {
16
        PORTD &= ~(1 << PD0);
17
      }
18
    }
19
}

von Karl Käfer (Gast)


Lesenswert?

Hallo Peter,

Peter Dannegger schrieb:
> Heiliger Bimbam schrieb:
>> Ist Euch die Programmierung in den gängigen Sprachen noch nicht
>> kompliziert genug?
>
> Karl Heinz hat das ganz richtig erkannt, es geht mir vorrangig darum,
> das Programmieren einfacher und sicherer zu machen.

Genau dafür ist C++ ein enorm leistungsfähiges Werkzeug. Leider werfen 
die meisten, die hier Beiträge verfassen, die Konzepte wild 
durcheinander und scheinen C++ nicht wirklich verstanden zu haben.

> Und C++ ist ja schon im AVR-GCC includiert, man braucht also an der
> Programmierumgebung nichts zu ändern. Einfach nur *.c nach *.C
> umbenennen.

Besser ".cpp" und ".hpp", dann kommen auch die Windowsleute besser klar.

Liebe Grüße,
Karl

PS: danke für Deine Libraries!

von Karl Käfer (Gast)


Lesenswert?

Hi Ticktacktoe,

tictactoe schrieb:
> tictactoe schrieb:
>> Dann können wir also z.B. einen Array mit Pointern auf die Clienten
>> anlegen, damit wir die virtuelle Handler-Funktione aufrufen können.
>> Vielleicht so
>> ...
>> Aber das kriegt man auch mit Template-Metaprogramming hin
>
> Ich muss hier nochmal nachhaken. Mein Post ist eine Werbung für
> Template-Metaprogramming. Man muss hier aber einen Schritt zurück machen
> und noch mal die Ausgangssituation betrachten. Gerade weil man auf einem
> µC keine dynamischen Listen von (Uhren, Tastern...) hat, könnte man
> Template-Metaprogramming verwenden (weil alle teilnehmenden Klassen zur
> Compile-Zeit bekannt sind). Aber aus dem selben Grund kann man genau so
> gut auch schreiben:
>
1
ISR(TIMER0_OVF_vect) {
2
>   uhr.OVF_vect();
3
>   taste1.OVF_vect();
4
>   taste2.OVF_vect();
5
> }
6
>
> Wozu also das ganze Gedöns um virtuelle Funktionen (braucht man bei
> dieser Form nicht) und Template-Metaprogramming? Hab' ich was an den
> Anforderungen nicht verstanden?

Nein, die Anforderungen sind nicht das Problem. Das Problem ist ein ganz 
anderes, viel tiefgreifenderes.

Du (und Karl Heinz und viele andere hier im Thread) habt den Sinn von 
Klassen, Instanzen und vor allem Templates einfach noch nicht richtig 
verstanden. Ein Template-Parameter ist ein Datentyp und wird übergeben, 
damit dieselbe Operation mit verschiedenen Datentypen ausgeführt werden 
kann.

Liebe Grüße,
Karl

von Karl Käfer (Gast)


Lesenswert?

Ret schrieb:
> F. Fo (foldi) schrieb:
>
> Dr. Sommer schrieb:
>>> Aber auch in Brainfuck. Warum verwendet niemand Brainfuck? Es ist
>>> supereinfach zu lernen, verwenden und implementieren.
>
>> Habe mir das mal angeguckt - ich hätte fast gekotzt.
>
> Da kann ich dich voll verstehen! Denn mir geht es genau nicht anders.
> Wer so einen hingekotzten Zeichensalat (ich hab extra nochmal
> nachgeschaut) wie Brainfuck ernsthaft als Programmiersprache empfiehlt,
> verarscht entweder seine Mitdiskutanten oder hat schlicht nicht alle
> Tassen im Schrank. Langsam nähren sich in mir die Anzeichen, dass ich
> solche Leute künftig an anderer Stelle nicht mehr für voll nehmen
> sollte. Und wenn mir der gleiche Herr demnächst dann seine C++
> Template-Orgien schmackhaft unter die Nase reiben will, denke ich mir
> darauf, "hab Nachsicht mit ihm! Das ist einer, der auf Brainfuck steht.
> Von dem kann einfach nichts Brauchbares oder Gescheites kommen".
>
> So kann man sich seine Glaubwürdigkeit hier auf einen Schlag verspielen.
> Aber vielleicht passt auch alles gut zusammen. Wer auf Brainfuck steht,

Man kann das hier abkürzen: wer auf Brainfuck steht, ist brainfucked.

Wer auf Mikrocontrollern C++-Templateprogrammierung einsetzen will, 
braucht aber auch gute Gründe. ;-)

Beste Grüße,
Karl

von F. F. (foldi)


Lesenswert?

Karl Käfer schrieb:
> Man kann das hier abkürzen: wer auf Brainfuck steht, ist brainfucked.

Den muss ich doch noch mal "einrahmen". :-)

von Conny G. (conny_g)


Lesenswert?

Karl Käfer schrieb:
> verstanden. Ein Template-Parameter ist ein Datentyp und wird übergeben,
> damit dieselbe Operation mit verschiedenen Datentypen ausgeführt werden
> kann.

Das ist praktisch das Konstrukt, das in Java "generics" heißt?

von Klaus W. (mfgkw)


Lesenswert?

Es ist das, was Java mit generics versucht.

von Karl Käfer (Gast)


Lesenswert?

Hallo Conny,

Conny G. schrieb:
> Karl Käfer schrieb:
>> verstanden. Ein Template-Parameter ist ein Datentyp und wird übergeben,
>> damit dieselbe Operation mit verschiedenen Datentypen ausgeführt werden
>> kann.
>
> Das ist praktisch das Konstrukt, das in Java "generics" heißt?

Ja, genau das.

Liebe Grüße,
Karl

von W.S. (Gast)


Lesenswert?

Karl Käfer schrieb:
> Du (und Karl Heinz und viele andere hier im Thread) habt den Sinn von
> Klassen, Instanzen und vor allem Templates einfach noch nicht richtig
> verstanden. Ein Template-Parameter ist ein Datentyp und wird übergeben,
> damit dieselbe Operation mit verschiedenen Datentypen ausgeführt werden
> kann.

Nananana..
Also ich sag's mal so: Wenn die Features einer Programmiersprache so 
extrem schwer zu begreifen sind, daß "Du (und Karl Heinz und viele 
andere hier im Thread)" es trotz deiner vielen Worte noch immer nicht 
begriffen haben, dann taugt diese Programmiersprache nix. Schließlich 
soll sie ja dem geneigten Programmierer das Leben erleichtern und ihn 
nicht veranlassen, sich das Hirn zermartern zu müssen.

Aber mal konkret: Klassen sind Typen und um damit was anfangen zu 
können, muß man Daten so eines Types erschaffen, auch Instanziieren 
genannt. Sowas ist ganz generell eine softwareinterne Angelegenheit.

Du kannst keinen Port instanziieren, denn der ist schlichtweg da, ganz 
einfach in der zugrundeliegenden Hardware DA. Du kannst mit all dem 
Klassengedöns lediglich die Hardware in eine zusätzliche Softwareschicht 
einwickeln - und diese Schicht kannst du dann instanziieren nach 
Belieben.

Blöd ist nur, daß du je nach Hardware innerhalb der Methoden deiner 
Klasse in ganz erheblichen Fallunterscheidungen ersticken wirst. 
Versuche z.B. mal die Pins eines gewöhnlichen ARM Controllers 
einzurichten. Ei wo ist denn nun der Satz von 2 oder 4 Bits innerhalb 
eines der 9 PINSEL Register, der für das gesuchte Pin zuständig ist? Und 
hat dieser Eintrag überhaupt was zu sagen, wenn an anderer Stelle ein 
Peripheriecore aktiviert ist, der diesen Pin von sch aus belegt?

Mit so einem simplen AVR als Beispiel sieht die Welt noch einfach aus, 
aber wollen wir hier über akademische Bleistift-Geraderückerei reden 
oder über reale Dinge aus der Praxis? Vielleicht sollte man sowas ganz 
am Anfang eines Threads mal klar festlegen.

Aber bei der o.g. Wrapperei geht der tiefere Sinn verloren, denn du hast 
in der Hardware niemals mehrere gleichartige Objekte, die aus Sicht des 
übergeordneten Programms gleichbehandelt werden können. Immerhin hat 
(eigentlich) jedes verdammte Pin am Käfer seine besondere Bedeutung und 
seine Funktionalität, die es von allen anderen unterscheidet. Das Pin 
für das Zünden der Bombe hat wirklich eine ganz andere Behandlung 
verdient als das Pin zum Einschalten der Displaybeleuchtung.

Abgesehen davon halte ich so eine Pseudo-Vereinfachung wie

if(btnPin.isHigh())
  ledPin.setHigh();
else
  ledPin.setLow();

für unzureichenden Mumpitz. Es wird in jedem Falle ein Unterprogramm 
aufgerufen und ehe man irgendwelche allumfassenden Wrapper-Klassen 
erfindet, wäre es leichter und klarer, selbiges mit ganz simplen 
gewöhnlichen Unterprogrammen zu erledigen.

Das würde dann ENDLICH die eigentlich angstrebte Abstraktion bringen:
if (StartKnopfGedrueckt())
  { BeleuchtungEin();
    if (GangEingelegt() || !BremseGedrueckt())
      beep();  //
    else
      StarteMotor();
  }
else
  { ZuendungAus();
    BeleuchtungAus();
  }

Siehste: Hier wird nicht mit popligen Bit High oder Bit Low 
herumgefummelt, denn jemand, der draufschaut, weiß nicht, welchem 
eigentlichen Systemzustand "btnPin.isHigh()" entspricht. Und was man mit 
"ledPin.setLow()" anrichtet, steht auch nicht dabei. Sowas ist also ein 
ausgesprochen mickriger Abstraktionslevel, der letztlich herzlich unnütz 
ist.

W.S.

von Thomas W. (wagneth)


Lesenswert?

@WS:

Ich vermute ich verstehe Dich recht gut.
Das was Du beschreibst setzte ich so um.

Allerdings lasse ich in einer Timer ISR alle Ausgänge immer wieder neu 
setzten.
Die eigentlichen Zustände stehen in einem grossen Struct.
Die Setter/Getter Funktionen greifen nur auf das Struct und /oder 
bereiten dinge vor.

Dabei kommt sowas heraus wie : setMotor(Motor1,Zeit_in_Timertakte);
Im Logikteil  Zustandsautomat  Hauptschleife sieht das recht gut aus.

Mein obiges Beispiel ist unfertig und hat noch keine Setter/Getter,
aber der Grundsätzliche aufbau ist sichtbar.

Du kannst Dir sicherlich vorstellen wie sich (prozeduraler Ansatz) die 
ISR aufbläht, unübersichtlich wird.
Tiefere Änderungen sind schmerzhaft !
Derzeit habe ich einen Kunden der ständig das Lastenheft ändert.
Ich glaube ich habe die ISR und Setter/Getter schon mindestens 100 mal 
angeändert.
Das Projekt verschlingt mittlerweile 6kb flash, ohne Mathmatik.


Was meinst Du wieviel schreiberei sowas in prozedural ersetzen könnte ?

{
  GPIOPWMGEN motor1PWM(GPIO::GPIO(PORTB,4));
  GPIOPWMGEN motor2PWM(GPIO::GPIO(PORTB,6));
  GPIOPWMGEN motor3PWM(GPIO::GPIO(PORTB,9));

  motor1PWM.setPWM(PẀM1);
  motor2PWM.setPWM(PWM2);
  motor3PWM.setPWM(PWM3);
  ....
}

ISR (TIMER)
{
  motor1PWM.handler();
  motor2PWM.handler();
  motor3PWM.handler();
}

Dafür braucht man aber auch eine GPIO, PWM, SPI... irgendwas Klasse.

Danach baut man die "höhere" Logik ein.
Andere Baustelle, oder ?

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


Lesenswert?

Hallo W.S.,

W.S. schrieb:
> Also ich sag's mal so: Wenn die Features einer Programmiersprache so
> extrem schwer zu begreifen sind, daß "Du (und Karl Heinz und viele
> andere hier im Thread)" es trotz deiner vielen Worte noch immer nicht
> begriffen haben, dann taugt diese Programmiersprache nix. Schließlich
> soll sie ja dem geneigten Programmierer das Leben erleichtern und ihn
> nicht veranlassen, sich das Hirn zermartern zu müssen.

Tja, das ist so eine Sache mit dem Verständnis: entweder man hat es, 
oder man hat es eben nicht. Daß schon C enorm komplex ist, siehst Du 
schon an den vielen Fragen und Fehlern, die hier ständig aufschlagen -- 
und die bisweilen sogar sehr erfahrenen C-Entwicklern passieren. Was 
hattest Du denn erwartet, was herauskommt, wenn man eine komplizierte 
Sprache wie C um ein komplexes Paradigma wie die Objektorientierung 
erweitert? Diese Komplexität ist der Preis für die Mächtigkeit, und wenn 
Du von diesen vielen neuen Features überfordert bist, spricht das nicht 
unbedingt gegen die Features. Dies gilt insbesondere, da es sich bei dem 
Besagten um eines handelt, das ausschließlich der Bequemlichkeit des 
Entwicklers dient und somit zur besseren Wart- und Wiederverwendbarkeit 
des Code beiträgt.

Du mußt das Feature ja nicht benutzen und kannst Dir gerne und jederzeit 
Deine eigenen Klassen für vector_int, vector_long, vector_float und so 
weiter schreiben. Ich hingegen bevorzuge eindeutig das Standard-Template 
std::vector<T> und kann damit genau dieselben vector-Klassen automatisch 
erzeugen lassen, die Du Dir mühselig und fehlerträchtig von Hand 
zusammen basteln müßtest.

Und wenn Du Deine Aussagen einmal zuende denkst, endest Du irgendwann 
bei Programmiersprachen, die dem intellektuellen Niveau solcher, 
hüstel, "Argumentationen", entsprechen: Niki the robot, BASIC und 
Scratch. ;-)

> Aber mal konkret: Klassen sind Typen und um damit was anfangen zu
> können, muß man Daten so eines Types erschaffen, auch Instanziieren
> genannt. Sowas ist ganz generell eine softwareinterne Angelegenheit.

Gut erkannt.

> Du kannst keinen Port instanziieren, denn der ist schlichtweg da, ganz
> einfach in der zugrundeliegenden Hardware DA.

Ja, und nein. Ja, der Port im Sinne der zugrundeliegenden Hardware ist 
bereits vorhanden, aber seine softwaretechnische Abstraktion natürlich 
nicht. Oder verzichtest Du auf <avr/io.h> und programmierst direkt gegen 
die Registeradressen? Auch die #defines sind letztlich Abstraktionen, 
welche die Lesbarkeit und die Verständlichkeit erhöhen sollen.

Aber richtig: ein Port ist jedenfalls kein Datentyp, und seine Adresse 
ist auch keiner. Genau deswegen sind, wie ich schon sagte, Templates 
hier schlicht und ergreifend deplatziert und nicht das Mittel der Wahl. 
Daß Du dieses Feature mißverstehst, spricht immer noch nicht gegen das 
Feature, sondern nur gegen Dein Verständnis davon. Nicht schlimm: wie 
wir gerade erst gesehen haben, befindest Du Dich da in sehr guter 
Gesellschaft.

> Blöd ist nur, daß du je nach Hardware innerhalb der Methoden deiner
> Klasse in ganz erheblichen Fallunterscheidungen ersticken wirst.
> Versuche z.B. mal die Pins eines gewöhnlichen ARM Controllers
> einzurichten. Ei wo ist denn nun der Satz von 2 oder 4 Bits innerhalb
> eines der 9 PINSEL Register, der für das gesuchte Pin zuständig ist? Und
> hat dieser Eintrag überhaupt was zu sagen, wenn an anderer Stelle ein
> Peripheriecore aktiviert ist, der diesen Pin von sch aus belegt?

Du benutzt die AVR-Header für ARM-Controller? Erzähl mehr davon, das 
klingt zwar wenig zielführend, dafür aber sehr interessant. Nein, im 
Ernst: wer sagt, daß man für AVRs, PICs und ARMs dieselben Klassen, 
dieselbe Abstraktion verwenden muß? Das ist nur Deine ganz persönliche, 
naive, wenn nicht sogar böswillige Unterstellung.

Tatsächlich könnte man jedoch für ARMs, AVRs und andere Controller immer 
dieselbe Schnittstelle verwenden. Was die Methode Pin::setHigh() macht, 
läßt sich hinter einer simplen Abstraktionsschicht verbergen, ähnlich 
wie avr/io.h die richtigen Adressen für DDRB, PINB und PORTB des 
betreffenden Controllers einbindet -- je nachdem, was avr-gcc als "mmcu" 
bekommt.

> Mit so einem simplen AVR als Beispiel sieht die Welt noch einfach aus,
> aber wollen wir hier über akademische Bleistift-Geraderückerei reden
> oder über reale Dinge aus der Praxis? Vielleicht sollte man sowas ganz
> am Anfang eines Threads mal klar festlegen.

In diesem Fall sind die akademische und die praktische Betrachtung 
absolut deckungsgleich: Templates übergeben Datentypen an generische 
Klassen und Funktionen, Punkt. Templates sind kein alternativer 
Mechanismus zur Übergabe von Variablen, Punkt. Ob Du das praktisch, 
theoretisch, oder akademisch betrachtest, ist egal, es kommt immer 
dasselbe dabei heraus: Templates sind hier nicht das richtige Feature, 
ganz einfach.

> Aber bei der o.g. Wrapperei geht der tiefere Sinn verloren, denn du hast
> in der Hardware niemals mehrere gleichartige Objekte, die aus Sicht des
> übergeordneten Programms gleichbehandelt werden können. Immerhin hat
> (eigentlich) jedes verdammte Pin am Käfer seine besondere Bedeutung und
> seine Funktionalität, die es von allen anderen unterscheidet. Das Pin
> für das Zünden der Bombe hat wirklich eine ganz andere Behandlung
> verdient als das Pin zum Einschalten der Displaybeleuchtung.

Letzten Endes ist ihre Funktionalität aber exakt dieselbe: High heißt 
"$Peripherie einschalten", Low bedeutet "$Periperhie ausschalten" -- und 
zwar völlig ungeachtet ob $Peripherie nun der Initialzünder der Bombe 
oder die Displaybeleuchtung ist. Und wenn ich in meinem Programm die 
Instanz des Bomben-Pins "zuender" und die Instanz des Display-Pins 
"beleuchtung" nenne, ist das am Ende schon ziemlich deutlich.

> Abgesehen davon halte ich so eine Pseudo-Vereinfachung wie
>
> if(btnPin.isHigh())
>   ledPin.setHigh();
> else
>   ledPin.setLow();
>
> für unzureichenden Mumpitz.

Nein. Der Mumpitz ist nur, was Du daraus zusammenkonstruierst, um krude 
Thesen zu belegen. Was ich hier gemacht habe, ist eine Verallgemeinerung 
der Hardware, hier die von Input- und Output-Pins. Das sagt natürlich 
noch nichts darüber, was an den Input- und Output-Pins hängt, welchen 
Zuständen der daran hängenden Peripherie-Hardware isHigh() und isLow() 
entsprechen, und was setHigh() und setLow() am ledPin bewirken. Das 
gehört aber ganz woanders hin und ist für jedes Projekt unterschiedlich: 
in einem Projekt schalte ich eine LED gegen GND, dann schaltet setHigh() 
die LED ein, und in einem anderen Projekt schalte ich die LED gegen Vcc, 
dann schaltet setHigh() die LED aus. Deswegen kann ich nicht 
allgemeingültig eine Klasse LED festlegen, weil ich nicht 
allgemeingültig sagen kann, ob on() den Pin jetzt High oder Low setzen 
muß.

> Es wird in jedem Falle ein Unterprogramm aufgerufen

Nö, der Optimizer macht genau dasselbe wie beim Direktzugriff auf die 
Register. Aber während Du eine sehr konkrete Funktionalität für ein ganz 
bestimmtes Projekt mit einer definierten Peripherie-Hardware zeigst, 
ging es bei mir um eine allgemeine Abstraktion der uC-Hardware statt um 
eine singuläre, projektspezifische Hardwarebeschreibung der Peripherie. 
Um also aus meiner Abstraktion des Mikrocontrollers ein richtiges 
Projekt mit einer bestimmten Peripherie-Hardware zu machen, braucht es 
noch einen weiteren Layer: nämlich eine Abstraktion der 
Peripherie-Hardware -- die allerdings prima auf Basis meiner 
uC-Abstraktion erfolgen kann.

> und ehe man irgendwelche allumfassenden Wrapper-Klassen
> erfindet, wäre es leichter und klarer, selbiges mit ganz simplen
> gewöhnlichen Unterprogrammen zu erledigen.

> Das würde dann ENDLICH die eigentlich angstrebte Abstraktion bringen:
> if (StartKnopfGedrueckt())
>   { BeleuchtungEin();
>     if (GangEingelegt() || !BremseGedrueckt())
>       beep();  //
>     else
>       StarteMotor();
>   }
> else
>   { ZuendungAus();
>     BeleuchtungAus();
>   }

Ja, Du kannst Dir natürlich für jeden Kram eine eigene Funktion basteln, 
genau wie ich mir meine Klasse. Von der Konsistenz ("StarteMotor()" und 
"ZuendungAus()"?) Deines Beispielcode einmal abgesehen: was hindert mich 
daran, das mit Klassen zu abstrahieren? Ach ja: nichts.
1
if (startknopf.gedrueckt()) { 
2
  beleuchtung.ein();
3
  if (gang.eingelegt() || !bremse.gedrueckt()) {
4
    beep();  //
5
  } else {
6
    motor.ein();
7
  }
8
} else { 
9
  motor.aus();
10
  beleuchtung.aus();
11
}

> Siehste: Hier wird nicht mit popligen Bit High oder Bit Low
> herumgefummelt, denn jemand, der draufschaut, weiß nicht, welchem
> eigentlichen Systemzustand "btnPin.isHigh()" entspricht.

Vergleiche meinen Code mit Deinem. Merkst Du was? Zum Beispiel für die 
Instanzen motor und beleuchtung oder für die Instanzen startknopf und 
bremse? Na? Genau:
1
#include <avr/io.h>
2
#include "Pin.hpp"
3
4
class EinAus: public OutputPin {
5
public:
6
    EinAus(volatile uint8_t* ddr, volatile uint8_t* port, 
7
           volatile uint8_t* pin, uint8_t num):
8
        OutputPin(ddr, port, pin, num) {}
9
    void ein(void) { this->setHigh(); }
10
    void aus(void) { this->setLow(); }
11
};
12
13
class Eingabe: public InputPin {
14
public:
15
    Eingabe(volatile uint8_t* ddr, volatile uint8_t* port, 
16
           volatile uint8_t* pin, uint8_t num):
17
        InputPin(ddr, port, pin, num) {}
18
    int gedrueckt(void) { return this->isHigh(); }
19
};
20
21
class Getriebe: public InputPin {
22
public:
23
    Getriebe(volatile uint8_t* ddr, volatile uint8_t* port, 
24
           volatile uint8_t* pin, uint8_t num):
25
        InputPin(ddr, port, pin, num) {}
26
    int eingelegt(void) { return this->isHigh(); }
27
};
28
29
void beep(void) {}
30
31
32
int main(void) {
33
34
    EinAus motor       = EinAus(&DDRB, &PORTB, &PINB, PB1);
35
    EinAus beleuchtung = EinAus(&DDRB, &PORTB, &PINB, PB2);
36
37
    Eingabe startknopf = Eingabe(&DDRD, &PORTD, &PIND, PD1);
38
    Eingabe bremse     = Eingabe(&DDRD, &PORTD, &PIND, PD2);
39
40
    Getriebe gang      = Getriebe(&DDRD, &PORTD, &PIND, PD3);
41
42
    while(1) {
43
        if (startknopf.gedrueckt()) { 
44
            beleuchtung.ein();
45
            if (gang.eingelegt() and !bremse.gedrueckt()) {
46
                beep();  //
47
            } else {
48
                motor.ein();
49
            }
50
        } else { 
51
            motor.aus();
52
            beleuchtung.aus();
53
        }
54
    }
55
}

So läßt sich auf der Basis meiner Klassen eine einfache, lesbare und 
trotzdem sehr flexible Abstraktion der Peripherie-Hardware durchführen. 
Der Code in der Hauptschleife ist ähnlich wie Deiner, nur konsistenter, 
lesbarer -- und ohne den logischen Fehler den Du gemacht hast. Diese 
Abstraktionsschicht ist sehr leicht, absolut projektspezifisch und trägt 
auch im Maschinencode nicht auf: das optimierte Kompilat ist nach 
avr-strip 536 Bytes groß. Und jetzt? Fällt Dir auch mal was Sachliches, 
Konstruktives und vielleicht sogar Kluges zum Thema ein?

Liebe Grüße,
Karl

von Stefan (Gast)


Lesenswert?

@Karl:
Das finde ich ein gutes Beispiel.

Werden mehrere Motoren verwendet, dann wird der Vorteil von 
Objektorientierung besonders deutlich:

  EinAus motorLinks   = EinAus(&DDRB, &PORTB, &PINB, PB1);
  EinAus motorRechts  = EinAus(&DDRB, &PORTB, &PINB, PB2);

  ...

  motorLinks.ein();
  motorRechts.ein();

Natürlich geht es auch so:

  StarteMotorlinks();
  StarteMotorRechts();
oder:
  StarteMotor(pMotorParameterLinks);
  StarteMotor(pMotorParameterRechts);

Aber dafür darf ich mein komplettes Programm ändern - überall, wo bisher 
StarteMotor() stand.

Spätestens, wenn die Motorsteuerung komplexer wird, weil z.B. der Choke 
in Abhängigkeit von der Temperatur etc. gezogen werden muß: beim 
objektorientierten Ansatz ändert sich nur die Klasse Motor, im gesamten 
restlichen Programm wird der Code nicht angefasst.

Ich habe lange "normales" C programmiert. Nachdem ich auf C++ 
umgestiegen bin, habe ich dann gemerkt, daß viele Probleme, die ich 
früher mühsam von Hand mit structs etc. gelöst habe, in C++ sehr einfach 
und logisch lösbar sind. Und in den allermeisten Fällen ohne negative 
Auswirkungen auf Performance und Codegröße.

Viele Grüße,
Stefan

von Moby (Gast)


Lesenswert?

Karl Käfer schrieb:
> was Sachliches,
> Konstruktives
1
;PortB-Eingänge:
2
.equ  startknopf  =  0
3
.equ  gang    =  1
4
.equ  bremse    =  2
5
6
;PortB-Ausgänge:
7
.equ  motor    =  3
8
.equ  beleuchtung  =  4
9
10
11
auto_control:    sbi  DDRB,motor
12
      sbi  DDRB,beleuchtung
13
14
      sbic  portb,startknopf
15
      rjmp  startknopf_gedrueckt  
16
      cbi  portb,motor
17
      cbi  portb,beleuchtung
18
      ret
19
startknopf_gedrueckt:  sbi  portb,beleuchtung
20
      sbis  portb,gang
21
      rjmp  motor_ein
22
      sbic  portb,bremse
23
      rjmp  motor_ein
24
      rcall  beep
25
      ret
26
motor_ein:    sbi  portb,motor
27
      ret
28
29
beep:

... macht nach Adam Riese maximal schnelle 32 Bytes (+ ein paar für die 
Beep-Routine) bei viel weniger Schreibaufwand, mindestens genauso 
übersichtlich und ohne sich das Hirn mit komplexen Programmausdrücken 
aufreiben zu müssen. Hier fehlt in Asm lediglich, einem Ausdruck wie 
portb,0 ein passendes Alias geben zu können. Und jetzt?

von Klaus W. (mfgkw)


Lesenswert?

Moby schrieb:
> Und jetzt?

Und jetzt bitte eine Antwort auf die Frage des TO.

Nur am Rande: er hatte nicht gefragt, ob und wie man ein Bit in 
Assembler setzen kann.

von BastiDerBastler (Gast)


Lesenswert?

Moby: Bei Deinem Quelltext muss ich dem Programmfluss folgen um zu 
sehen, wo und wie die Schleifen oder die Fallunterscheidungen liegen. 
Daher habe ich länger benötigt um zu verstehen, was geschieht.

von Stefan (Gast)


Lesenswert?

Mal abgesehen davon, daß der TO nach C++ gefragt hat, ist es einfach nur 
peinlich, Peter Danegger etwas über Assembler erzählen zu wollen.

Gruß, Stefan

von temp (Gast)


Lesenswert?

Moby schrieb:
> Und jetzt?

Der Assemblerbrei ist ja wohl auch keine Schmeicheleinheit für die 
Augen. Ob das nun 32Byte oder das 10fache ist spielt keine Rolle. Aus so 
was wird nie wartbarer Code.

von BastiDerBastler (Gast)


Lesenswert?

Moby schrieb im Beitrag #3910701:
> Ich denke, diese wenigen Zeilen werden keinen der doch so komplex denken
> könnenden C++ Programmierer überfordern ;-)

Schön provokant, aber in der Sache kein zusätzlicher Gedankeninhalt. 
Nächster Versuch!

von Stefan (Gast)


Lesenswert?

Das lieber Lässt Du mal besser weg.

Sowohl Peter Danegger, als auch Klaus, und genauso ich haben sicher 
schon wesentlich mehr in Assembler gearbeitet und sind mittlerweile ein 
paar Softwaregenerationen weiter als Du.

Stefan

von Mark B. (markbrandis)


Lesenswert?

Moby schrieb:
> Und jetzt?

Und jetzt portierst Du den Assembler-Code auf zwölf verschiedene 
Mikroprozessoren mit unterschiedlichen Befehlssätzen. Viel Spaß dabei! 
:-)

von Geert H. (geerth)


Lesenswert?


von Robin E. (why_me)


Lesenswert?

Ganz unrecht hat moby aber auch nicht. Gut bei seinem code fehlt die 
initialisierung. Also lasst es 100 zu 500 byte code sein. Das is ne 
Menge für ein und dieselbe Aufgabe.

Lesbarer wäre der Code mit kommentaren, ist also auch nicht so schlimm. 
Und offtopic ist es auch nicht, da ihr immer argumentiert, es gibt 
keinen overhead.

Also wo kommen die ca400 bytes her? Hier wäre das asm file nicht 
schlecht. Evtl fehlende optimierungen?

Vom code sieht das c++ ja nicht schlecht aus, wobei ich dem pin noch nen 
startwert mitgeben würde beim initialisieren.

Auch wurde ich dem outputpin gleich an und aus als Funktion geben und 
noch nen inveroutoutpin einbauen.

von BastiDerBastler (Gast)


Lesenswert?

Moby schrieb im Beitrag #3910841:
> Ist das etwa alles?
> Ich meine zum eigentlichen Thema?

Jetzt folgst Du vollends dem normalen Trollschema. Du trägst nichts bei 
außer einer (möglichst feinfühligen) Provokation und dann drehen wir uns 
wieder auf einer Metaebene im Kreis, also lassen wir's einfach bleiben!

Robin E. schrieb:
> Ganz unrecht hat moby aber auch nicht. Gut bei seinem code fehlt die
> initialisierung. Also lasst es 100 zu 500 byte code sein. Das is ne
> Menge für ein und dieselbe Aufgabe.

Was auch immer der Autor dort oben mit seiner Bibliothek gemacht hat, es 
war offenbar nicht gut, oder aber der entsprechende Compiler ist extrem 
schlecht, wenn es zu solchen Unterschieden kommt. Ja, Startup kostet ein 
bissl was, C-Runtime-Init vielleicht auch (aber nicht so  viel, 
wahrscheinlich hat er keine minimal-Runtime ausgewählt). So sollte aber 
nicht dermaßen viel Overhead entstehen. Wenn man im Stande ist, den 
Startup-Code zu verändern, entfällt es dann auch gänzlich. Vielleicht 
ist der AVR-gcc aber auch einfach schlecht, würde mich nicht 
überraschen.

von Mark B. (markbrandis)


Lesenswert?


von Moby (Gast)


Lesenswert?


von Neuland-Bewohner (Gast)


Lesenswert?

Moby schrieb:
> schon ausgedruckt

Oh, ein Internet-Ausdrucker. Sind die noch nicht ausgestorben?

von TriHexagon (Gast)


Lesenswert?

Moby schrieb im Beitrag #3910701:
> Tja Klaus, zur Frage C++ auf MC gehört immer auch die Frage der
> Sinnhaftigkeit.

Wie wärs wenn du mal über die Sinnhaftigkeit deiner Beiträge denkst, 
anstatt hier ständig ungefragt deine Ansichten zu C und C++ zu 
verbreiten. Im Forum müsste es jetzt jeder wissen. Gibt es dir nicht mal 
langsam zu denken, wenn kein Anderer deine Meinung teilt und dir schon 
etliche Leute (die auch noch beruflich mit µCs zutun haben) dagegen 
argumentieren? Dazu kommt dann noch dass du von C++ (und anscheinend C) 
keine Ahnung hast, es aber dennoch erbittert bekämpfst. Ganz ehrlich, du 
erinnerst mich an irgendwelche fanatischen Missionare, die meinen den 
einzig wahren Glauben verbreiten zu müssen.

von Paladium (Gast)


Lesenswert?

Moby schrieb:
> . macht nach Adam Riese maximal schnelle 32 Bytes (+ ein paar für die
> Beep-Routine) bei viel weniger Schreibaufwand, mindestens genauso
> übersichtlich und ohne sich das Hirn mit komplexen Programmausdrücken
> aufreiben zu müssen. Hier fehlt in Asm lediglich, einem Ausdruck wie
> portb,0 ein passendes Alias geben zu können. Und jetzt?

Auf den Punkt gebracht. Zumindest für diese Aufgabe. Natürlich 
programmiere auch ich keine LAN Anbindung in Assembler. Wobei man da 
schon den klassischen MC verlässt. Der Kluge Entwickler passt sein 
Sprachwerkzeug der Anforderung an und micht C u. assembler und da wo er 
komplexe nicht zeitkritische Aufgaben hat und den entsprechenden 
Speicher zur Verfügung meinetwegen auch C++.

von Moby (Gast)


Lesenswert?

TriHexagon schrieb:
> die meinen den
> einzig wahren Glauben verbreiten zu müssen

Nun, das Asm-Beispiel bezeugt keinen Glauben, sondern schlicht Fakten.
Auch wenn sie manchem OOP-Gläubigen nicht in den Kram passen ;-)

Paladium schrieb:
> Auf den Punkt gebracht. Zumindest für diese Aufgabe.

Für viele weitere genauso ;-)

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Moby schrieb im Beitrag #3910701:
> Tja Klaus, zur Frage C++ auf MC gehört immer auch die Frage der
> Sinnhaftigkeit

Sicher nicht. Peters Frage war klar - nach Assemblerlösungen wurde nicht 
gefragt.

Wenn Du diese Frage stellen willst, mach bitte Deinen eigenen Thread 
dazu auf.

Danke.

von Karl Käfer (Gast)


Lesenswert?

Hallo Moby,

Moby schrieb:
> Und jetzt?

Irgendwie habe ich trotz intensiver Bemühungen noch nicht verstanden, 
welche Relevanz das denn für eine Diskussion haben sollte, bei der es um 
C++ (und damit: um les-, wart- und wiederverwendbaren Code) auf 
Mikrocontrollern geht.

Trotzdem vielen Dank für Deinen "Beitrag".

Liebe Grüße,
Karl

von F. F. (foldi)


Lesenswert?

Ich hatte ja schon mal hier die PSoC angeführt. Da wird der Hardwareteil 
als Schaltung dargestellt und auch die Funktionen, die ein Pin ausüben 
soll wird dort in der graphischen Ansicht eingestellt.
Dahinter muss sich doch im Prinzip solch ein Konzept verbergen.

Eine einmal klar getrennte Hardware, wohl möglich noch für eine ganze 
Gruppe von µC's, die dann den eigentlich wichtigen Code wartbarer und 
austauschbar macht, das wäre doch wünschenswert.

von Johannes R. B. (Gast)


Lesenswert?

das war ein spannender und sehr detaillierter Vortrag zum Für und Wider 
C++

http://www.ese-kongress.de/paper/presentation/id/309

die für mich wichtigste Botschaft war: C und C++ Lösungen miteinander 
vergleichen, zum Beispiel bezüglich Speicherplatz und Laufzeit, läuft 
oft auf den Vergleich von Äpfeln und Birnen hinaus. Er hat sehr das 
schön an Beispielen nachgewiesen die sich eben erst auf den zweiten 
Blick erschließen. So hat er eine C-Lösung für einen Up- und 
Down-Counter einmal klassisch und einmal objektorientiert vorgestellt 
und auseinandergepflückt. Quintessenz war dabei die geschickt gebaute 
Klasse war nur 3% größer aber dafür 20% schneller :-o zudem war die 
OO-Lösung noch Typsicher und hat automatisch verhindert dass die Counter 
durch Anwendungsentwickler falsch benutzt werden konnten.

Dann hat er die C-Lösung vorgestellt die tatsächlich äquivalente 
Eigenschaften hatte und die war um einiges fetter und langsamer als die 
C++ Lösung da es eben durch den Programmierer und nicht auf 
Compilerebene gelöst wurde.


Vielleicht ist der Vortrag ja mal im Download verfügbar.

Gruß J.

von Bastler (Gast)


Lesenswert?

Aber der vergleicht ja nur C mit C++. Für eingeschworene ASM'er beides 
das gleiche Teufelszeug.  Gefundene Ironie bitte behalten!
Ansonst, die Zusammenfassung bringt's auf den Punkt: mehr Features ohne 
extra Kosten.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

Bastler schrieb:
> die Zusammenfassung bringt's auf den Punkt: mehr Features ohne
> extra Kosten.

So habe ich das bisher auch gesehen. Auf einem PC programmiere ich ja 
auch in C++ oder C#. Vor allem Templates sind genial.

Aber mit den IDEs, die ich für µCs benutze, hat das nie richtig 
funktioniert. Der Compiler für den MSP430 unterstützt keine Templates 
und bem Atmel Studio und CooCox funktioniert es bei mir (noch?) nicht, 
beim Debuggen mit Breakpoints in die Instanz-Variablen zu schauen.

Welche IDEs benutzt Ihr für C++ und klappt dort das Debugging?

: Bearbeitet durch User
von Moby (Gast)


Lesenswert?

Karl Käfer schrieb:
> habe ich trotz intensiver Bemühungen noch nicht verstanden

Das hast Du bestimmt ganz genau verstanden.
Aber was sollst Du bei diesen offensichtlichen Unterschieden nun auch 
sagen.
Also verstecke Dich ruhig weiter hinter der völlig irrealistischen 
Moderatoren-Richtlinie, stets konstant und punktgenau beim Thema zu 
bleiben.
Um es dann (wie könnte es auch anders sein) selber nicht so genau zu 
nehmen.

von W.S. (Gast)


Lesenswert?

Johannes R. B. schrieb:
> Dann hat er die C-Lösung vorgestellt die tatsächlich äquivalente
> Eigenschaften hatte und die war um einiges fetter und langsamer als die
> C++ Lösung da es eben durch den Programmierer und nicht auf
> Compilerebene gelöst wurde.

Solange man den Quelltext solcher grandiosen Beispiele nicht sehen kann, 
glaube ich solchen Leuten kein einziges Wort. Das ist ganz genau so wie 
bei Vertretern irgendeiner Ware, die sehr schön zeigen können, daß 
allein mit IHRER Ware der wahre Fortschritt einhergeht und alle anderen 
bloß Stümper sind. Frei nach Churchill.. glaube ich nur der Statistik, 
die ich eigenhändig gefälscht habe.

W.S.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

@ Moby  +  W.S.: Hört doch mal mit Eurem kleigeistigen Krieg auf. 
Folgende Frage finde ich im Zusammenhang "wie geht das?" viel 
spannender.

Torsten C. schrieb:
> Welche IDEs benutzt Ihr für C++ und klappt dort das Debugging?

Bei mir geht das eben leider noch nicht so schön, wie in der Theorie. 
Oder hat der TO Peter das Problem vielleicht gar nicht? Ich will ja 
nicht den Thread kapern.

: Bearbeitet durch User
von Moby (Gast)


Lesenswert?

Torsten C. schrieb:
> Ich will ja
> nicht den Thread kapern.

Schon passiert. Bitte bleibe beim Thema!
Sonst kommt gleich der Klaus um die Ecke ;-)

von BastiDerBastler (Gast)


Lesenswert?

@Karl Käfer: Kannst Du den Header vielleicht irgendwo hochladen, würde 
das gerne mal auf meine Plattform -> sinngemäß <- portieren.

@all: EinAus(&DDRB, &PORTB, &PINB, PB1);

Warum sind beim Atmel 4 Werte vonnöten um einen Pin zu steuern? Was ist 
DDRB und was unterscheidet es von PORTB, was bedeutet PINB und warum 
beinhaltet dann PB1 nochmal das B? Ich bin da lieder außenstehender :(

von Moby (Gast)


Lesenswert?

BastiDerBastler schrieb:
> Warum sind beim Atmel 4 Werte vonnöten um einen Pin zu steuern? Was ist
> DDRB und was unterscheidet es von PORTB, was bedeutet PINB und warum
> beinhaltet dann PB1 nochmal das B? Ich bin da lieder außenstehender :(

DDRB = Portpins I/O Definition Register
PORTB = Port-Output Register
PINB = Port-Input Register
PB1 = Port B Bit 1

von BastiDerBastler (Gast)


Lesenswert?

Also würde im Prinzip auch PB1 genügen, DDRB, PORTB, PINB ließen sich 
daraus ableiten?

von Moby (Gast)


Lesenswert?

BastiDerBastler schrieb:
> Also würde im Prinzip auch PB1 genügen, DDRB, PORTB, PINB ließen sich
> daraus ableiten?

So wie ich das verstehe enthält Px0 - Px7 nur die Bitnummer 0-7 für x= 
Port A,B,C..., woraus sich natürlich nicht die verschiedenen I/O 
Locations der drei zu einem Port x gehörigen Register DDRx,PORTx,PINx 
ableiten lassen sondern explizit anzugeben sind.

von Thomas W. (wagneth)


Angehängte Dateien:

Lesenswert?

Ich versuche gerade die Snippets aus dem Artikel (erstes Beispiel) :

http://www.mikrocontroller.net/articles/AVR_Interrupt_Routinen_mit_C%2B%2B

einzubauen.

Ich C++ Neuling, kann da mal jemand drübersehen ?

Die Timer0 Klasse wird in der Main.cpp abgeleitet.
Sollte das so funktionieren ???

Gibt es einen besseren weg ?

von Thomas W. (wagneth)


Angehängte Dateien:

Lesenswert?

Hier übrigens noch das LSS.
Was recht klein ist !?

von Nebelschwade (Gast)


Lesenswert?

BastiDerBastler schrieb:
> Also würde im Prinzip auch PB1 genügen, DDRB, PORTB, PINB ließen sich
> daraus ableiten?

Nein.

Ein Port besteht hier aus einem Byte, bzw. 8 Pit. Zu einem Port gehören 
drei Register, jeweils 1 Byte bzw. 8 Bit groß:

  DDRx   = Pins als Eingönge oder Ausgänge einstellen
  PORTx  = Für Ausgang-Pins ihr Wert, also An oder Aus
  PINx   = Für Eingangs-Pins ihr Wert, also An oder Aus

Und nun musst Du noch die Bitnummer Deines Ports wissen. Die steht in 
Pxn.

von chris (Gast)


Lesenswert?

>Ich C++ Neuling, kann da mal jemand drübersehen ?

Es ist nett, dass Du ein wenig code postest. Meiner Meinung nach wäre 
aber ein ZIP-File sinnvoller.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

chris schrieb:
> Meiner Meinung nach wäre aber ein ZIP-File sinnvoller

Meiner Meinung ist die "Codeansicht" zumindest bei den .cpp ganz nett, 
die ginge bei zip verloren.

@ Thomas W: Welche IDE benutzt Du?

von Thomas W. (wagneth)


Lesenswert?

Eclipse CDT mit dem AVR GCC Plugin.

Habe misst gebaut.
Wenn man MyTimer instanziiert passiert das hier :
Timer.cpp:(.text._ZN6Timer0C2Ev+0x2): undefined reference to 
`Timer0::timerInterrupt::ownerTimer'

Das muss jetzt aber bis morgen warten.

Im nachhinein finde ich den Anhang auch extrem unübersichtlich.

von BastiDerBastler (Gast)


Lesenswert?

@Nebelschwade:

Aber wenn PB3 nun vom fiktiven Typ static_pin<1,3> wäre, dann wäre alle 
Information im PB3 enthalten!

von Karl Käfer (Gast)


Lesenswert?

BastiDerBastler schrieb:
> @Karl Käfer: Kannst Du den Header vielleicht irgendwo hochladen, würde
> das gerne mal auf meine Plattform -> sinngemäß <- portieren.

Der Header ist der Code für die Klassen Pin, InputPin und OutputPin aus 
meinem vorherigen Beispiel.

HTH,
Karl

von BastiDerBastler (Gast)


Lesenswert?

Soo,

also ich habe den Code hier mal so hingefrickelt in meiner Bibliothek. 
Wollte jetzt kein Getriebe von einem Pin ableiten lassen usw. 
(Komposition wäre da eindeutig besser).
Vielleicht sollte man die Dinge auch nicht "pin" nennen, sondern 
"pin_ref", schließlich referenziert das ja nur einen real existierenden 
Pin.
Mit den ".configure" bin ich noch nicht zufrieden, aber input_pin, 
output_pin wären falsch, weil der Zustand des realen Pins ja nicht an 
die Referenz gebunden ist.
"pb<3>" usw. sind "statische" Pin-Referenzen, die man auch verwenden 
könnte, aber da der Beispielcode oben ja Laufzeit-Werte genommen hat, 
habe ich das hier ebenso gemacht.

[cpp]
void playground()
{
  using namespace gpio;

  pin motor       = pb<3>();
  pin beleuchtung = pb<2>();
  pin startknopf  = pd<1>();
  pin bremse      = pd<2>();
  pin gang        = pd<3>();

  motor.configure(inout());
  beleuchtung.configure(inout());
  startknopf.configure(input);
  bremse.configure(input);
  gang.configure(input);

  while(1) {
    if(startknopf.get()) {
      beleuchtung.set();

      if(gang.get() && !bremse.get()) {
        // beep
      } else {
        motor.set();
      }
    } else {
      motor.reset();
      beleuchtung.reset();
    }
  }
}
[/cpp]

Der Schleifenanteil nimmt genau 32 Byte Code ein. Die Konfiguration 
nimmt mehr ein, weil das ja ->Laufzeitabhängig<- in einigen Fällen 
maskierte read-modify-write auf mehrere Register bedeutet (in meinem 
Fall: STM32F4). Wären die Pins festverdrahtet, würden die Masken von 
meinen Templates entsprechend zusammengelegt und ausgerechnet, für 
entsprechend weniger Schreiboperationen.

von Peter L. (localhost)


Lesenswert?

Wenn du das stm32f4 discovery board benutzt, kannst du dich schonmal mit 
dem Framework 
https://github.com/roboterclubaachen/xpcc/tree/develop/examples/stm32f4_discovery 
austoben.

von W.S. (Gast)


Lesenswert?

Karl Käfer schrieb:
> Tja, das ist so eine Sache mit dem Verständnis: entweder man hat es,
> oder man hat es eben nicht. Daß schon C enorm komplex ist, siehst Du
> schon an den vielen Fragen und Fehlern, die hier ständig aufschlagen --
> und die bisweilen sogar sehr erfahrenen C-Entwicklern passieren. Was
> hattest Du denn erwartet, was herauskommt, wenn man eine komplizierte
> Sprache wie C um ein komplexes Paradigma wie die Objektorientierung
> erweitert?

Der Volksmund sagt "getroffene Hunde bellen". Da hast du in einem 
ellenlangen Beitrag versucht, deine seltsamen Ansichten zu untermauern - 
und hast doch nur geschrieben, daß du kein Verständnis für echte 
Abstraktionen hast. Du hast nicht begriffen, daß das Abstrahieren der 
Hardware auf ein bloßes Wrappen a la Port123.SetBit9.high oder so 
einfach nur ein viel zu mickriger Denkansatz ist.
Was also sollen solche billigen Dinge wie:

pin motor = pb<3>();
und
motor.configure(inout());

denn zum Verbessern der Situation innerhalb der Firmware beitragen? Es 
ist eben ganau DAS, was ich schon schrieb: ein viel zu mickriger 
Denkansatz - geschrieben eben nur zu dem Zweck, hier eine krampfhafte OO 
Anwendung mittels C++ zu posten. Von einem echten HW-Treiber bzw. einer 
echten HAL ist sowas meilenweit entfernt.


Aber mal zum oben zitierten: C ist überhaupt nicht komplex. C ist 
lediglich kompliziert, weil es zu einem ganz erheblichen Teil auf 
Konstruktionsfehlern beruht, die wiederum per Dekret a la Ordre de Mufti 
wieder ausgebügelt wurden. Ich spare mir mal die Details, das hatten wir 
schon anderweitig.

Wenn man eine komplexe UND unkomplizierte Sprache (wie Pascal) erweitert 
(um "ein komplexes Paradigma wie die Objektorientierung"), dann bleibt 
das dank Weitsicht der Leute, die das taten, auch leicht les- und 
verstehbar. Es geht also. Und man kann sogar mehrere Konstruktoren 
haben und nicht bloß einen einzigen mickrigen Konstruktor wi bei C++.

W.S.

von Bauteiltöter (Gast)


Lesenswert?

W.S. schrieb:
> Und man kann sogar mehrere Konstruktoren
> haben und nicht bloß einen einzigen mickrigen Konstruktor wi bei C++.

Und wo hast du die Weisheit her, dass C++ keine überladenen 
Konstruktoren unterstützt?

von TriHexagon (Gast)


Lesenswert?

W.S. schrieb:
> Aber mal zum oben zitierten: C ist überhaupt nicht komplex. C ist
> lediglich kompliziert, weil es zu einem ganz erheblichen Teil auf
> Konstruktionsfehlern beruht, die wiederum per Dekret a la Ordre de Mufti
> wieder ausgebügelt wurden. Ich spare mir mal die Details, das hatten wir
> schon anderweitig.

Wo soll den C kompliziert sein? Natürlich sind da ein paar grobe 
Schnitzer, aber welche Sprache ist perfekt?

C ist eine richtige pragmatische Systemprogrammiersprache, die dazu 
entwickelt wurde portable Betriebssysteme entwickeln zu können (z.B. 
Unix). Was Anfängern immer wieder aus der Bahn wirft, sind solche Dinge 
wie z.B., dass ein Stringliteral ein const char* Zeiger zurück gibt und 
dass ein String nicht durch ein = Operator kopiert werden kann. Was ist 
daran kompliziert? Da stolpert halt ein ungedultiger Anfänger drüber.

Viele Eigenschaften von C erscheinen erstmal merkwürdig und eigensinnig 
(wie z.B. die automatische Initialisierung von Variablen), wenn man aber 
die Hintergründe erforscht, dann ist es eigentlich immer logisch und 
eingängig.

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