andere Lib - darf Klassenname gleich bleiben?

Hallo,

ich hatte eine Library erstellt, wovon ich nun ein Kopie angelegt habe um intern spezielle Änderungen vorzunehmen.
Im Grunde bleibt sie gleich, aber nur fast.

Meine originale Lib heißt "GrayEncoder" auch die .h und .cpp Datei heißen so.
Der Konstruktor und Klassenname ebenso.

Kann ich nun bei der "Kopie" den Konstruktor und Klassenname beibehalten und nur den Namen der Lib ändern?
Ich würde jetzt nur den Namen auf "ALPS_EC11_Encoder" ändern und die .h und .cpp auch.

Ich bin mir nicht sicher wie weit die Kapselung der Library geht.

Hallo,

Antwort, ja, Konstruktor und Klassenname können beibehalten werden. Compiler meckert erstmal nicht.
Aber man muss ja den Klassennamen im Sketch bei Initialisierung verwenden.

Bsp.:

#include <ALPS_EC11_Encoder.h>

ALPS_EC11_Encoder Encoder1 (&PINE, 4, 5);  // Pin 2,3 > Port.E "Bit 4","Bit 5"

Wenn ich jetzt beide fast gleiche Lib verwende, weiß ja der Compiler nicht, welche Klasse ich aus welcher Lib initialisieren möchte.

ich könnte auch den alten Klassenname behalten und das dann schreiben.

#include <ALPS_EC11_Encoder.h>

GrayEncoder Encoder1 (&PINE, 4, 5);  // Pin 2,3 > Port.E "Bit 4","Bit 5"

Aber spätestens wenn ich beide Libs einbinde müßte es ein Problem geben.

#include <GrayEncoder.h>
#include <ALPS_EC11_Encoder.h>

GrayEncoder Encoder1 (&PINE, 4, 5);  // Pin 2,3 > Port.E "Bit 4","Bit 5"

Weil woher will der Compiler wissen welche GrayEncoder Klasse ich meine? Die existiert in beiden Libs.

Worin unterscheiden sich die Klassen? Sind es nur Methoden die sich ändern? Dann kannst du die neue Klasse einfach von der ersten Klasse ableiten und die Methoden überschreiben. Dann minimiert sich auch der duplizierte Code. Aber du brauchst natürlich einen anderen Namen.

Hallo,

der Unterschied ist nur, dass ich für den ALPS EC11 Drehencoder den ausgelesenen Zählerstand vorher durch 2 teile und beim übergeben zurück in die Klasse vorher mal 2 nehmen muss. Damit die wieder gleich sind und alles intern richtig weiter zählt, wenn sich nichts ändert.

Der Drehencoder gibt mir sonst pro Drehklick ein Vielfaches von 2 aus, weil er 2 Impulse dabei erzeugt.
Ich möchte jedoch pro Drehklick genau einen "Tick" haben. Deswegen das *2 und /2.
Das erspart mir im Sketch viele Fehler, falls ich *2 oder /2 vergessen sollte.

Klar, ich könnte auch einfach 2 neue Funktionen, du nennst es Methoden, einbauen und es alles bleibt beim alten.
Nur wie benenne ich diese sinnvoll zur Unterscheidung. Zudem dann die Lib Kuttelmuttel wird.
Wobei ich das noch verkraften würde wenn es dabei bleibt, wonach es aussieht.

Neugierig bin ich jetzt wie das ableiten und überschreiben funktionieren würde?
Soll ich die .h. und .cpp veröffentlichen?

2 neue Funktionen, du nennst es Methoden

Ich nenne es Methode weil Funktionen und Klassenfunktionen/Methoden nicht das gleiche sind

Neugierig bin ich jetzt wie das ableiten und überschreiben funktionieren würde?

Einfach die neue Klasse als Unterklasse der alten machen. Und der neuen Methode den gleichen Namen wie die Methode in der Basisklasse geben.

class EncoderBase
{
public:
  EncoderBase(int pinA, int pinB) : pinA(pinA), pinB(pinB)
  {
  }

  void doSomething()
  {
    Serial.println("Base");
    Serial.println(pinA);
    Serial.println(pinB);
  }
  
  const int pinA, pinB;
};

class EncoderDerived : EncoderBase
{
public:
  EncoderDerived(int pinA, int pinB) : EncoderBase(pinA, pinB)
  {
  }

  void doSomething()
  {
    Serial.println("Derived");
    Serial.println(pinA);
    Serial.println(pinB);
  }
};

void setup()
{
  Serial.begin(9600);

  EncoderBase encoder1(1, 2);
  EncoderDerived encoder2(3, 4);
  
  encoder1.doSomething();
  Serial.println();
  encoder2.doSomething();
}

void loop() 
{
}

Die Pin Definition sind nur der Einfachheit halber public. Im Konstruktor reicht man die Parameter an den Konstruktor der Basisklasse durch

Solange man dann nicht die abgeleitete Klasse über einen Zeiger auf die Basisklasse ansprechen will geht das wie erwartet. Dafür bräuchte man dann virtuelle Methoden. Ist hier aber nicht nötig.

Hallo,

okay, Funktionen != Methoden ... wegen dem vorangestellten Bereichsoperator? :slight_smile:

Ich glaube ich erkenne den roten Faden dabei, probiere ich morgen ...

Vielen Dank erstmal.

Als ich das las wolte ich schon das Handtuch werfen, undurchsichtig. http://www.cpp-tutor.de/cpp/le14/ableitung.html

okay, Funktionen != Methoden ... wegen dem vorangestellten Bereichsoperator?

Weil nicht-statische Methoden einen versteckten Parameter haben. Den this Zeiger auf ihr Objekt. Was zur Folge hat dass man einem Zeiger auf eine Funktion keine Methode zuweisen kann.

http://www.cpp-tutor.de/cpp/le14/ableitung.html

Um was es hier geht wird da auch erwähnt:

Memberfunktionen einer abgeleiteten Klasse verdecken die gleichnamigen Memberfunktionen ihrer Basisklasse

Memberfunktion ist eine direktere Version des englischen "member function". Andere Bezeichnungen sind Klassenfunktion oder Methode

Was noch sehr wichtig ist und ich ganz vergessen (dadurch dass ich es vereinfacht hatte), aber jetzt auf deiner Seite gesehen habe:
Variablen der Basisklasse die du in der Unterklasse verwenden willst müssen in der Basisklasse als "protected" deklariert werden! Nicht als "private". Dann sind sie nach außen nicht sichtbar, aber in der Unterklasse sichtbar

Weil nicht-statische Methoden einen versteckten Parameter haben. Den this Zeiger auf ihr Objekt. Was zur Folge hat dass man einem Zeiger auf eine Funktion keine Methode zuweisen kann.

Huh, jetzt hast du ihn wohl wieder verschreckt :wink:

class X {
int member;  // eine Member-Variable, jede Instanz (Objekt) von X hat eigene member Variablen

public:
 X(int p): member(p) {} // Ein Konstruktor mit Parameter
 int method(int param) { return (param+member); } // Eine Methode
};


X instance(100);  // ein spezielles Objekt der Klasse X
X x(200);            // ein anderes Objekt der gleichen Klasse
void setup() {
 Serial.begin(9600);
 Serial.println(instance.method(500));  // Aufruf der Methode: Ergebnis 600

 Serial.println(x.method(500));  // Aufruf der Methode für das andere Objekt: Ergebnis 700
}
void loop() {}

Methoden haben nicht nur den this Zeiger, sondern auch Zugriff auf alle Member-Variablen.

Bei Arduino gerät der Unterschied zwischen einer Klasse (z.B. HardwareSerial) und Objekten dieser Klasse (z.B. Serial) schonmal aus dem Blickfeld. Weil es oft nur ein Objekt der jeweiligen Klasse gibt.

Natürlich war Sereniflys Antwort komplett richtig

Hallo,

ich mußte erst nochmal nachlegen für morgen ... siehe Anhang :slight_smile:

Ich habe nun versucht das gesagte umzusetzen. Jedoch hakelt es tausende Fehlermeldungen.
Ich habe die Basisklasse "GrayEncoder_v2" an die neue "ALSP_Encoder" vererbt. Zeile 35.
Habe die 2 geänderten Methoden hinzugefügt. Der Rest soll ja vererbt werden und bleibt gleich.
Zeile 11 habe ich von private zu protected geändert.
In der .cpp habe ich die eine neue Funktion am Ende eingefügt mit ihrem neuen Klassenname. Zeile 57.

Header Datei

// Deklaration der Klasse
// GrayEncoder_v2.h

#ifndef GrayEncoder_v2_h
#define GrayEncoder_v2_h

#include <Arduino.h>

class GrayEncoder_v2       // Klassenname "GrayEncoder_v2"
{
  protected:                    // nur intern zugängliche Elemente ...   
    volatile byte const* PORT;  // verwendeter Port
    byte const PinA;            // Pin Port.Bit erste Phase
    byte const PinB;            // Pin Port.Bit zweite Phase
    int enc_last;               // Startwert 2, weil TLE4905 low aktiv
    volatile long enc_counter;  // Zähler
    int direction;              // Richtungsanzeige  +1 / -1
    byte Phase_A;				// erste Phase
    byte Phase_B;				// zweite Phase
	
	
  public:	             // von außen zugängliche Elemente ...
    GrayEncoder_v2 (volatile byte*, byte, byte);   // Port, Port.Bit, Port.Bit
    void encode();
    void delCounter();				// nullt den Counter
    void setCounter( long value );	        // Counter-Wert ändern
    long getCounter()  { return enc_counter; }	// Getter-Methode, Zählerrückgabe
    int getDirection()  { return direction; }	// Getter-Methode, Richtungsanzeige    	
    int getA()  { return Phase_A; }    		// Getter-Methode, Signal von Phase A
    int getB()  { return Phase_B; }    		// Getter-Methode, Signal von Phase B
		
}; 


class ALPS_Encoder : GrayEncoder_v2   // Klasse "ALPS_Encoder" erbt von "GrayEncoder_v2"
{
  public:	             // von außen zugängliche Elemente ...
    ALPS_Encoder (volatile byte*, byte, byte);     // Port, Port.Bit, Port.Bit
    void setCounter( long value );		   // Counter-Wert ändern
    long getCounter()  { return enc_counter/2; }   // Getter-Methode, Zählerrückgabe
    	
}; 

#endif

.cpp

// Definition der Klasse
// GrayEncoder_v2.cpp

#include "GrayEncoder_v2.h"

// Konstruktor ::
GrayEncoder_v2::GrayEncoder_v2 (volatile byte *p_Port, byte _A, byte _B):
  // Initialisierungsliste
  PORT(p_Port),	
  PinA(_A),
  PinB(_B),
  enc_last(0x02),	// Startwert B11, weil TLE4905 low aktiv
  enc_counter(0),	// Zähler
  direction(0),		// Richtungsanzeige  +1 / -1
  Phase_A(1),     	// default 1, weil TLE4905 low aktiv
  Phase_B(1)		// default 1, weil TLE4905 low aktiv
{ }	

// ab hier Scopes/Bereichsoperator ::
void GrayEncoder_v2::encode()  
{
  int i = 0;
  
  Phase_A = (*PORT >> PinA & 1);  // einzelnes Bit heraus fischen
  Phase_B = (*PORT >> PinB & 1);
    
  if( Phase_A )  {
    i = 1;
  }
  
  if( Phase_B )  {
    i ^= 3;           // convert gray to binary
  }
  
  i -= enc_last;      // difference new - last

  if( i & 1 )  {                        // bit 0 = value (1)
    enc_last += i;                      // store new as next last
    enc_counter += (long) (i & 2) - 1;  // bit 1 = direction (+/-), Zähler
    direction = (i & 2) - 1;            // Richtungsanzeige (+1 / -1)
  }
  
}

void GrayEncoder_v2::delCounter()
{
  enc_counter = 0;
}


void GrayEncoder_v2::setCounter( long value )
{
  enc_counter = value;
}		


void ALPS_Encoder::setCounter( long value )
{
  enc_counter = value * 2;
}

Sketch

/*
  Doc Arduino - german Arduino Forum
  IDE 1.6.13
  Arduino Mega2560
  30.12.2016
*/


#include <GrayEncoder_v2.h>

ALPS_Encoder   Encoder2 (&PINE, 4, 5);  // Pin 2,3 > Port.E "Bit 4","Bit 5"
int Encoder_Direction;
int Encoder_Counter;

const byte Taster_Pin = 4;    // Taster am Drehencoder
const byte LED_Pin = 13;

void setup() {
  Serial.begin(9600);
  
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(Taster_Pin, INPUT_PULLUP);
  pinMode(LED_Pin, OUTPUT);
  
}

void loop() {

  update_Encoder();

  update_Taster();
  
  serieller_Monitor();

}


void update_Encoder ()
{  
  Encoder2.encode(); 
  Encoder_Direction = Encoder2.getDirection(); 
  Encoder_Counter = Encoder2.getCounter();      
}


void update_Taster ()
{
  if ( digitalRead(Taster_Pin) == LOW ) {
    Encoder2.delCounter();
  }  
}


void serieller_Monitor ()
{
  static unsigned int intervall = 500;
  static unsigned long prevMillis = 0;

  if (millis()-prevMillis<intervall) return; // Zeit noch nicht erreicht, Funktion abbrechen
  
  prevMillis+=intervall; 
  Ueberschriftszeile();
  Serial.print(Encoder_Direction); Serial.print('\t');
  Serial.print(Encoder_Counter); Serial.print('\t');
  Serial.println();
}


void Ueberschriftszeile ()
{
  static int counter = 33;

  counter++;
  
  if (counter<20) return; // Zeit noch nicht erreicht, Funktion abbrechen
  
  counter = 0; 
  Serial.print(F("DIR")); Serial.print('\t');
  Serial.print(F("COUNT")); Serial.print('\t');
  Serial.println();
}

In file included from C:\Users\Worker\AppData\Local\Temp\arduino_modified_sketch_470514\GrayEncoder_v2.ino:9:0:

C:\Users\Worker\Documents\Arduino\libraries\GrayEncoder_v2/GrayEncoder_v2.h: In function 'void update_Encoder()':

C:\Users\Worker\Documents\Arduino\libraries\GrayEncoder_v2/GrayEncoder_v2.h:24:10: error: 'void GrayEncoder_v2::encode()' is inaccessible

und tausend andere ....

Kipferl_a.jpg

Kipferl_b.jpg

Was schon mal auffällt ist dass deine neue Encoder Klasse keinen Konstruktor hat. Im Header ja, aber die Implementierung fehlt.
Du kannst in Konstruktoren übrigens dem Parameter und der Variable den gleichen Namen geben. Das ist wesentlich einfacher zu lesen

Der Fehler ist dass bei der Vererbung die Zugriffsrechte nicht angegeben sind. Korrekt ist es so:

class ALPS_Encoder : public GrayEncoder_v2

Mein Fehler. Da war das Test Beispiel auch wieder zu sehr vereinfacht

Das bedeutet dass alles was in der Basisklasse public ist auch in der Unterklasse public ist (protected bleibt auch protected). Wenn man nichts angibt hat man private Vererbung (public und protected werden zu private) und das geht schief.

Hallo,

hmm, einfacher wird es damit wohl nicht. :wink:
Muss neben dem Konstruktor auch die Initialisierungsliste nochmal mit angegeben werden?
Ich dachte die ganze Klasse wird einfach vererbt, neue Methoden dazu, fertig. :slight_smile:
Am Ende habe ich quasi 2 Libs in einer statt zwei sauber getrennte Librarys.

hmm, einfacher wird es damit wohl nicht.

Doch es ist einfacher :stuck_out_tongue: Wenn man nicht die Hälfte vergisst :wink:

Am Ende habe ich quasi 2 Libs in einer statt zwei sauber getrennte Librarys.

Du hast aber nicht unnötig Code kopiert und dupliziert. Änderungen in der Basisklasse beeinflussen so auch die Unterklasse. Das ist nicht immer gut, aber in diesem Fall schon.

Oder angenommen man hat ein Szenario wo man 20 verschiedene Varianten implementieren muss. Dann legt man eine allgemeine Oberklasse an und macht die entsprechende Methode abstrakt/rein virtuell (d.h. sie enthält keine Implementierung) und legt einfach für jede Variante eine Unterklasse an. Dann kann man kein Objekt der Oberklasse anlegen und jede Unterklasse wird gezwungen die Methode zu implementieren (so wie es jetzt ist ist es optional)

Muss neben dem Konstruktor auch die Initialisierungsliste nochmal mit angegeben werden?

Siehe mein Beispiel:

  EncoderDerived(int pinA, int pinB) : EncoderBase(pinA, pinB)
  {
  }

Einfach die Parameter (bei dir sind es 3) an den anderen Konstruktor weiterreichen.

Hallo,

gut, man muss damit nur eine Lib pflegen statt mehrere, auch wenn diese dann komplizierter aussieht, für mich jedenfalls. :wink:

Ich hoffe jetzt alles richtig umgesetzt zu haben. Bekomme nur noch eine Fehlermeldung, aber ich sehe den Klammerfehler nicht.

Die beiden leicht verschiedenen Methoden mit gleichen Namen "getCounter" und setCounter" sind dann intern getrennt.?
Also das eine ist nur für "GrayEncoder_v2" sichtbar und die andere Methode gleichen Namens nur für "ALSP_Encoder"?
Weil "ALPS_Encoder" erbt doch von der anderen. Hätte damit 2x "getCounter" und setCounter" zur Verfügung?

// Deklaration der Klasse
// GrayEncoder_v2.h

#ifndef GrayEncoder_v2_h
#define GrayEncoder_v2_h

#include <Arduino.h>

class GrayEncoder_v2       // Klassenname "GrayEncoder_v2"
{
  protected:                    // private wird zu protected, nur intern zugängliche Elemente ...   
    volatile byte const* PORT;  // verwendeter Port
    byte const PinA;            // Pin Port.Bit erste Phase
    byte const PinB;            // Pin Port.Bit zweite Phase
    int enc_last;               // Startwert 2, weil TLE4905 low aktiv
    volatile long enc_counter;  // Zähler
    int direction;              // Richtungsanzeige  +1 / -1
    byte Phase_A;		// erste Phase
    byte Phase_B;		// zweite Phase
	
	
  public:	             // von außen zugängliche Elemente ...
    GrayEncoder_v2 (volatile byte*, byte, byte);   // Port, Port.Bit, Port.Bit
    void encode();
    void delCounter();			        // nullt den Counter
    void setCounter( long value );		// Counter-Wert ändern
    long getCounter()  { return enc_counter; }	// Getter-Methode, Zählerrückgabe
    int getDirection()  { return direction; }	// Getter-Methode, Richtungsanzeige    	
    int getA()  { return Phase_A; }    		// Getter-Methode, Signal von Phase A
    int getB()  { return Phase_B; }    		// Getter-Methode, Signal von Phase B
		
}; 


class ALPS_Encoder : public GrayEncoder_v2   // Klasse "ALPS_Encoder" erbt von "GrayEncoder_v2"
{
  public:	             // von außen zugängliche Elemente ...
    ALPS_Encoder (volatile byte*, byte, byte);     // Port, Port.Bit, Port.Bit
    void setCounter( long value );		   // Counter-Wert ändern
    long getCounter()  { return enc_counter/2; }   // Getter-Methode, Zählerrückgabe
    	
}; 

#endif
// Definition der Klasse
// GrayEncoder_v2.cpp

#include "GrayEncoder_v2.h"

// Konstruktor ::
GrayEncoder_v2::GrayEncoder_v2 (volatile byte *p_Port, byte _A, byte _B):
  // Initialisierungsliste
  PORT(p_Port),	
  PinA(_A),
  PinB(_B),
  enc_last(0x02),	// Startwert B11, weil TLE4905 low aktiv
  enc_counter(0),	// Zähler
  direction(0),		// Richtungsanzeige  +1 / -1
  Phase_A(1),     	// default 1, weil TLE4905 low aktiv
  Phase_B(1)		// default 1, weil TLE4905 low aktiv
{ }	

// ab hier Scopes/Bereichsoperator ::
void GrayEncoder_v2::encode()  
{
  int i = 0;
  
  Phase_A = (*PORT >> PinA & 1);  // einzelnes Bit heraus fischen
  Phase_B = (*PORT >> PinB & 1);
    
  if( Phase_A )  {
    i = 1;
  }
  
  if( Phase_B )  {
    i ^= 3;           // convert gray to binary
  }
  
  i -= enc_last;      // difference new - last

  if( i & 1 )  {                        // bit 0 = value (1)
    enc_last += i;                      // store new as next last
    enc_counter += (long) (i & 2) - 1;  // bit 1 = direction (+/-), Zähler
    direction = (i & 2) - 1;            // Richtungsanzeige (+1 / -1)
  }
  
}

void GrayEncoder_v2::delCounter()
{
  enc_counter = 0;
}


void GrayEncoder_v2::setCounter( long value )
{
  enc_counter = value;
}		

// neue vererbte Klasse //
// Konstruktor Vererbung
ALPS_Encoder(byte *p_Port, byte _A, byte _B) : GrayEncoder_v2 (*p_Port, _A, _B)
{ }

void ALPS_Encoder::setCounter( long value )
{
  enc_counter = value * 2;
}

ich weiß nicht was ihm stört, nehme ich den hinteren Zeiger weg, meckert er trotzdem

C:\Users\Worker\Documents\Arduino\libraries\GrayEncoder_v2\GrayEncoder_v2.cpp:58:19: error: expected ')' before '*' token

ALPS_Encoder(byte *p_Port, byte _A, byte _B) : GrayEncoder_v2 (p_Port, _A, _B)

Also das eine ist nur für "GrayEncoder_v2" sichtbar und die andere Methode gleichen Namens nur für "ALSP_Encoder"?
Weil "ALPS_Encoder" erbt doch von der anderen. Hätte damit 2x "getCounter" und setCounter" zur Verfügung?

Du kannst die Methode der Oberklasse explizit mit dem Scope Operator ansprechen, aber ansonsten überdeckt die Methode der Unterklasse die der Oberklasse.

ich weiß nicht was ihm stört, nehme ich den hinteren Zeiger weg, meckert er trotzdem

Wieso dereferenzierst du den Zeiger?

ALPS_Encoder(byte *p_Port, byte _A, byte _B) : GrayEncoder_v2 (p_Port, _A, _B)
{ }

Außerdem sollte p_Port wahrscheinlich auch hier volatile sein!

Ist aber eine seltsame Fehlermeldung. Da kann C++ manchmal sehr kryptisch sein wenn der Parser nicht merkt was eigentlich falsch ist

Hallo,

mit volatile meckert er volatile an.
In der Fehlermeldung nimmt er den Zeiger entweder weg oder schreibt ihn hin, er macht in der Fehlermeldung immer das Gegenteil mit dem Zeiger was ich im Code schreibe.

mit volatile erscheint das ...
Code:

// neue vererbte Klasse //
// Konstruktor Vererbung
ALPS_Encoder(volatile byte *p_Port, byte _A, byte _B) : GrayEncoder_v2 (*p_Port, _A, _B)
{ }

void ALPS_Encoder::setCounter( long value )
{
  enc_counter = value * 2;
}

Fehlermeldung

\libraries\GrayEncoder_v2\GrayEncoder_v2.cpp:58:14: error: expected unqualified-id before 'volatile'

ALPS_Encoder(volatile byte *p_Port, byte _A, byte _B) : GrayEncoder_v2 (*p_Port, _A, _B)

^

\libraries\GrayEncoder_v2\GrayEncoder_v2.cpp:58:14: error: expected ')' before 'volatile'

ARG! Ewig herumprobiert bis ich gemerkt habe dass der Klassenname + Scope Operator fehlen. Bitte daran denken dass ich meinen Test-Code ohne Header geschrieben haben. Da muss man das nicht unbedingt trennen

ALPS_Encoder::ALPS_Encoder(volatile byte* port, byte pinA, byte pinB) : GrayEncoder_v2 (port, pinA, pinB)
{ }

Hier mal vollständig und so dass Variablen konsequent mit Kleinbuchstaben anfangen:

class GrayEncoder_v2       // Klassenname "GrayEncoder_v2"
{
  protected:                    // private wird zu protected, nur intern zugängliche Elemente ...
    volatile byte const* PORT;  // verwendeter PORT
    const byte pinA;            // Pin PORT.Bit erste Phase
    const byte pinB;            // Pin PORT.Bit zweite Phase
    int enc_last;               // Startwert 2, weil TLE4905 low aktiv
    volatile long enc_counter;  // Zähler
    int direction;              // Richtungsanzeige  +1 / -1
    byte phase_A;    // erste Phase
    byte phase_B;   // zweite Phase


  public:             // von außen zugängliche Elemente ...
    GrayEncoder_v2 (volatile byte* PORT, byte pinA, byte pinB);   // PORT, PORT.Bit, PORT.Bit
    void encode();
    void delCounter();              // nullt den Counter
    void setCounter( long value );    // Counter-Wert ändern
    long getCounter()  {
      return enc_counter;  // Getter-Methode, Zählerrückgabe
    }
    int getDirection()  {
      return direction;  // Getter-Methode, Richtungsanzeige
    }
    int getA()  {
      return phase_A;  // Getter-Methode, Signal von Phase A
    }
    int getB()  {
      return phase_B;  // Getter-Methode, Signal von Phase B
    }

};


class ALPS_Encoder : public GrayEncoder_v2   // Klasse "ALPS_Encoder" erbt von "GrayEncoder_v2"
{
  public:             // von außen zugängliche Elemente ...
    ALPS_Encoder (volatile byte* port, byte pinA, byte pinB);     // PORT, PORT.Bit, PORT.Bit
    void setCounter( long value );      // Counter-Wert ändern
    long getCounter()  {
      return enc_counter / 2;  // Getter-Methode, Zählerrückgabe
    }

};

GrayEncoder_v2::GrayEncoder_v2 (volatile byte* port, byte pinA, byte pinB):
  // Initialisierungsliste
  PORT(port),
  pinA(pinA),
  pinB(pinB),
  enc_last(0x02), // Startwert B11, weil TLE4905 low aktiv
  enc_counter(0), // Zähler
  direction(0),   // Richtungsanzeige  +1 / -1
  phase_A(1),       // default 1, weil TLE4905 low aktiv
  phase_B(1)    // default 1, weil TLE4905 low aktiv
{ 
}

// ab hier Scopes/Bereichsoperator ::
void GrayEncoder_v2::encode()
{
  int i = 0;

  phase_A = (*PORT >> pinA & 1);  // einzelnes Bit heraus fischen
  phase_B = (*PORT >> pinB & 1);

  if ( phase_A )  {
    i = 1;
  }

  if ( phase_B )  {
    i ^= 3;           // convert gray to binary
  }

  i -= enc_last;      // difference new - last

  if ( i & 1 )  {                       // bit 0 = value (1)
    enc_last += i;                      // store new as next last
    enc_counter += (long) (i & 2) - 1;  // bit 1 = direction (+/-), Zähler
    direction = (i & 2) - 1;            // Richtungsanzeige (+1 / -1)
  }

}

void GrayEncoder_v2::delCounter()
{
  enc_counter = 0;
}


void GrayEncoder_v2::setCounter( long value )
{
  enc_counter = value;
}

// neue vererbte Klasse //
// Konstruktor Vererbung
ALPS_Encoder::ALPS_Encoder(volatile byte* port, byte pinA, byte pinB) : GrayEncoder_v2 (port, pinA, pinB)
{ }

void ALPS_Encoder::setCounter( long value )
{
  enc_counter = value * 2;
}

void setup() {
  ALPS_Encoder encoder2 (&PINC, 4, 5);
  encoder2.setCounter(1);
}

void loop() {
  // put your main code here, to run repeatedly:
}

Bei const Zeiger musst du übrigens aufpassen wo du das const hinschreibst. Es gibt konstante Zeiger auf variable Daten, variable Zeiger auf konstante Daten und konstante Zeiger auf konstante Daten

Hallo Serenifly,

du hast es wirklich drauf. Funktioniert. Vielen Dank. :slight_smile:

Meine nächste Frage wäre sowieso in die Richtung gegangen, warum ich das schön fein sauber getrennt habe in .h und .cpp wenn alle Welt alles in eine Datei wirft. Weil die Trennung war bisher mein ganzer Stolz. Auf den online Seiten wurde auch alles in eine Datei geschrieben und machte mich wuschig. :o Diese Vielfältigkeit macht es für mich nicht einfacher.

Mit const möchte ich sicherstellen, dann die übergebenen Bits mit Port unangetastet bleiben. Obwohl es als private auch sicher vor Manipulation ist. Und ich wollte den Compiler unterstützen.

Schreibst du auch globale Variablen mit _ am Anfang? _global :slight_smile:

Aber gut, hast ja recht, man sollte eine klare Schreibweise durchziehen.

Schreibst du auch globale Variablen mit _ am Anfang? _global

Manchmal. Aber eher in C# weil man sie dann mit Intellisense leicht findet. Ohne Autovervollständigung ist es nicht unbedingt sinnvoll und in C++ fangen sehr viele System-Sachen schon mit einem Unterstrich an. Weshalb oft davon abgeraten wird.

Hallo,

aha.

Eine Sache viel mir noch auf. Hat es einen Grund warum du aus der Zeile

Code: [Select]

public:	             // von außen zugängliche Elemente ...
    GrayEncoder_v2 (volatile byte*, byte, byte);   // Port, Port.Bit, Port.Bit

die machst?

Code: [Select]

public:             // von außen zugängliche Elemente ...
    GrayEncoder_v2 (volatile byte* PORT, byte pinA, byte pinB);   // PORT, PORT.Bit, PORT.Bit

Oder durch copy / paste passiert? :wink:

Das war Absicht. Man muss die Variablen-Namen in der Deklaration nicht unbedingt hinschreiben, aber ich finde es besser verständlich. Ist aber Geschmackssache. Bei setCounter() hast du es auch gemacht