gesucht >> Pin-Port.Bitfolge oder Wurzelfunktion

Hallo,

an templates habe ich mich noch nicht rangetraut, wollte erstmal mit Klassen richtig warm werden. templates sehen jedoch Klassen verdammt ähnlich. Dein Code funktioniert auf Anhieb. Vielen Dank und spart so richtig satt Speicher. Unglaublich. Ein Grund mehr sich mit templates zubefassen.

Hier ist das Template eine Erzeugungsvorschrift für Klassen.

templates sehen jedoch Klassen verdammt ähnlich.

Das stimmt, in soweit, als dass es eine ist.

Hallo,

so einfach wie ich dachte ist es doch nicht.

Ich habe in meiner ausgebauten Klasse mehrere Kontrukturen um mal weniger und mal mehr Parameter zu übergeben. Vererbungen habe ich entsorgt.

In deinem Bsp. ist der Defaultkonstruktur zusammengekürzt. Zwei private Member in der Initialisierungsliste sind weg, die kommen nun vom template. Jetzt habe ich weitere Erzeugungsvorschriften templates hinzugefügt, abgeleitet von den bisheren Kontruktoren.

Jetzt weiß ich nicht weiter. Überladen geht mit templates nicht. Einerseits muss ich die template Variablen nutzen, andererseits muss ich Klassenmember mit Objektübergabeparameter initialisieren und das für verschiedene Kontruktoren. Einmal mit 0 und einmal mit Parameter. Muss ich nun für jedes ein Funktionstemplate erstellen?

Damit man sieht was ich meine, habe ich es auf 2 Kontruktoren eingekürzt und die templates übernommen und vorbereitet. Jetzt weiß ich nicht wie ich mit den bisheren Kontruktoren umgehen muss. Der Versuch die template Variablen in die Konstruktoren zu schreiben ging schief.

using Register = volatile byte *;

// UNO, Nano, Pro Mini usw.
#if defined(__AVR_ATmega328P__) | defined(__AVR_ATmega328__) | defined(__AVR_ATmega168P__) | defined(__AVR_ATmega168__)
  constexpr byte pinMaske[]     = {0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000,};
  constexpr Register portList[] = {&PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTB, &PORTB, &PORTB, &PORTB, &PORTB, &PORTB, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC,};
  constexpr Register ddrList[]  = {&DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRB, &DDRB, &DDRB, &DDRB, &DDRB, &DDRB, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC,};
  constexpr Register pinList[]  = {&PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PINB, &PINB, &PINB, &PINB, &PINB, &PINB, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC,};
//    #endif

// Mega
#elif defined(__AVR_ATmega2560__) | defined(__AVR_ATmega1280__)
  constexpr byte pinMaske[]     = {0b1, 0b10, 0b10000, 0b100000, 0b100000, 0b1000, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b10, 0b1, 0b10, 0b1, 0b1000, 0b100, 0b10, 0b1, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0b10000000, 0b100, 0b10, 0b1, 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0b1000, 0b100, 0b10, 0b1, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000,};
  constexpr Register portList[] = {&PORTE, &PORTE, &PORTE, &PORTE, &PORTG, &PORTE, &PORTH, &PORTH, &PORTH, &PORTH, &PORTB, &PORTB, &PORTB, &PORTB, &PORTJ, &PORTJ, &PORTH, &PORTH, &PORTD, &PORTD, &PORTD, &PORTD, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTD, &PORTG, &PORTG, &PORTG, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTB, &PORTB, &PORTB, &PORTB, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK,};
  constexpr Register ddrList[]  = {&DDRE, &DDRE, &DDRE, &DDRE, &DDRG, &DDRE, &DDRH, &DDRH, &DDRH, &DDRH, &DDRB, &DDRB, &DDRB, &DDRB, &DDRJ, &DDRJ, &DDRH, &DDRH, &DDRD, &DDRD, &DDRD, &DDRD, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRD, &DDRG, &DDRG, &DDRG, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRB, &DDRB, &DDRB, &DDRB, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK,};
  constexpr Register pinList[]  = {&PINE, &PINE, &PINE, &PINE, &PING, &PINE, &PINH, &PINH, &PINH, &PINH, &PINB, &PINB, &PINB, &PINB, &PINJ, &PINJ, &PINH, &PINH, &PIND, &PIND, &PIND, &PIND, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PIND, &PING, &PING, &PING, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINB, &PINB, &PINB, &PINB, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK,};
//    #endif

#else
  #error "This AVR type is currently not yet supported."
#endif

constexpr Register getOutPort(const byte pin) {
  return portList[pin];
};

constexpr Register getDdrPort(const byte pin) {
  return ddrList[pin];
};

constexpr Register getInPort(const byte pin) {
  return pinList[pin];
};

constexpr byte getMaske(const byte pin) {
  return pinMaske[pin];
};

template <byte pin_a, byte pin_b>  
template <byte pin_a, byte pin_b, byte raster>


class GrayEncoder               // Klassenname "GrayEncoder"
{
  protected:                    // private wird zu protected, nur intern zugängliche Elemente ...
    byte const PinA;            // Pin Port.Bit erste Phase
    byte const PinB;            // Pin Port.Bit zweite Phase
    int relMin;
    int relMax;
    byte shift;                 // Zählkorrektur wegen Steps pro Rastung
    volatile long rel_counter;  // relativer Zähler, veränderbar
    volatile long abs_counter;  // absoluter Zähler, nicht veränderbar
    int direction;              // Richtungsanzeige  +1 / -1
    int enc_delta;
    int last;
    byte phase_a;               // erste Phase
    byte phase_b;               // zweite Phase
    long abs_old;
    
    int getPhases() {
      int n = 0;                      // new
      
      phase_a = bool(*getInPort(pin_a) & getMaske(pin_a));
      phase_b = bool(*getInPort(pin_b) & getMaske(pin_b));

      if ( phase_a ) n = 3;
      if ( phase_b ) n ^= 1;          // convert gray to binary
      return n;
    }
    
    void update_RelativCounter () {
      long new_abs = abs_counter >> shift;          // halbiert, geviertelt oder ... Steps pro Rastung
      
      if ( new_abs != abs_old) {
        if (new_abs > abs_old) {
          rel_counter++;
        }
        else {
          rel_counter--;
        }
        
        abs_old = new_abs;
      
        if (rel_counter < relMin) {
          rel_counter = relMax;
        }
        else if (rel_counter > relMax) {
          rel_counter = relMin;
        }
      }
    }
    
  public:              // von außen zugängliche Elemente ...
    GrayEncoder ():
      // Initialisierungsliste
      relMin(0),
      relMax(0),
      shift(0),
      rel_counter(0),   // Zähler
      abs_counter(0),   // Zähler
      direction(0),     // Richtungsanzeige  +1 / -1     
      abs_old(0)
    { }

    // vorher GrayEncoder (byte _A, byte _B, byte _raster):
    GrayEncoder (byte raster):
      // Initialisierungsliste
      relMin(0),
      relMax(0),
      shift(_raster/2),
      rel_counter(0),     // Zähler
      abs_counter(0),     // Zähler
      direction(0),       // Richtungsanzeige  +1 / -1
      abs_old(0)
    { }
        
    void init()
    {
      *getDdrPort(pin_a) &= ~getMaske(pin_a);
      *getOutPort(pin_a) |=  getMaske(pin_a);
      
      *getDdrPort(pin_b) &= ~getMaske(pin_b);
      *getOutPort(pin_b) |=  getMaske(pin_b);
      
      last = getPhases();   // power on state
      enc_delta = 0;
    }

    void encode()
    {
      int n = getPhases();
      int diff = last - n;
      
      if ( diff & 1 )  {                        // bit 0 = value (1)
        last = n;                               // store new as next last
        enc_delta = (diff & 2) - 1;             // bit 1 = direction (+/-), Zähler
        abs_counter += (long) enc_delta;
        direction = enc_delta;                  // Richtungsanzeige (+1 / -1)
      }
      update_RelativCounter();
    }

    int getDirection()  {return direction; }      // Richtungsabfrage
    
    long getRelCounter()  {return rel_counter; }  // relativen Zählwert abfragen, ist schon angepasst
                  
    long getAbsCounter()  {return abs_counter >> shift; } // absoluten Zählwert abfragen
                                                          // x Steps pro Rastung
           
    int getA()  { return phase_a; }
    
    int getB()  { return phase_a; }
        
};


// ---------------------------------------------------------------------------------------

GrayEncoder <37, 36>      Encoder1;   // Phase A/B
GrayEncoder <40, 41,  2>  ALPS;       // Phase A/B / Raster

setup und loop musste ich weglassen wegen max. 9000 Zeichen

Ich glaube ich habe was verständliches im Buch von Stroustrup gefunden im Bezug auf Klassen und templates
Ich melde mich wieder ...

using Register = volatile byte *;

// UNO, Nano, Pro Mini usw.
#if defined(__AVR_ATmega328P__) | defined(__AVR_ATmega328__) | defined(__AVR_ATmega168P__) | defined(__AVR_ATmega168__)
  constexpr byte pinMaske[]     = {0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000,};
  constexpr Register portList[] = {&PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTB, &PORTB, &PORTB, &PORTB, &PORTB, &PORTB, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC,};
  constexpr Register ddrList[]  = {&DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRB, &DDRB, &DDRB, &DDRB, &DDRB, &DDRB, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC,};
  constexpr Register pinList[]  = {&PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PINB, &PINB, &PINB, &PINB, &PINB, &PINB, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC,};
//    #endif

// Mega
#elif defined(__AVR_ATmega2560__) | defined(__AVR_ATmega1280__)
  constexpr byte pinMaske[]     = {0b1, 0b10, 0b10000, 0b100000, 0b100000, 0b1000, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b10, 0b1, 0b10, 0b1, 0b1000, 0b100, 0b10, 0b1, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0b10000000, 0b100, 0b10, 0b1, 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0b1000, 0b100, 0b10, 0b1, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000,};
  constexpr Register portList[] = {&PORTE, &PORTE, &PORTE, &PORTE, &PORTG, &PORTE, &PORTH, &PORTH, &PORTH, &PORTH, &PORTB, &PORTB, &PORTB, &PORTB, &PORTJ, &PORTJ, &PORTH, &PORTH, &PORTD, &PORTD, &PORTD, &PORTD, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTD, &PORTG, &PORTG, &PORTG, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTB, &PORTB, &PORTB, &PORTB, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK,};
  constexpr Register ddrList[]  = {&DDRE, &DDRE, &DDRE, &DDRE, &DDRG, &DDRE, &DDRH, &DDRH, &DDRH, &DDRH, &DDRB, &DDRB, &DDRB, &DDRB, &DDRJ, &DDRJ, &DDRH, &DDRH, &DDRD, &DDRD, &DDRD, &DDRD, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRD, &DDRG, &DDRG, &DDRG, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRB, &DDRB, &DDRB, &DDRB, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK,};
  constexpr Register pinList[]  = {&PINE, &PINE, &PINE, &PINE, &PING, &PINE, &PINH, &PINH, &PINH, &PINH, &PINB, &PINB, &PINB, &PINB, &PINJ, &PINJ, &PINH, &PINH, &PIND, &PIND, &PIND, &PIND, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PIND, &PING, &PING, &PING, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINB, &PINB, &PINB, &PINB, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK,};
//    #endif

#else
  #error "This AVR type is currently not yet supported."
#endif

constexpr Register getOutPort(const byte pin) {
  return portList[pin];
};

constexpr Register getDdrPort(const byte pin) {
  return ddrList[pin];
};

constexpr Register getInPort(const byte pin) {
  return pinList[pin];
};

constexpr byte getMaske(const byte pin) {
  return pinMaske[pin];
};

//template <byte pin_a, byte pin_b,>

template <byte pin_a, byte pin_b, byte raster = 0>  
class GrayEncoder               // Klassenname "GrayEncoder"
{
  protected:                    // private wird zu protected, nur intern zugängliche Elemente ...
 //   byte const PinA;            // Pin Port.Bit erste Phase
 //   byte const PinB;            // Pin Port.Bit zweite Phase
    int relMin;
    int relMax;
    byte shift;                 // Zählkorrektur wegen Steps pro Rastung
    volatile long rel_counter;  // relativer Zähler, veränderbar
    volatile long abs_counter;  // absoluter Zähler, nicht veränderbar
    int direction;              // Richtungsanzeige  +1 / -1
    int enc_delta;
    int last;
    byte phase_a;               // erste Phase
    byte phase_b;               // zweite Phase
    long abs_old;
    
    int getPhases() {
      int n = 0;                      // new
      
      phase_a = bool(*getInPort(pin_a) & getMaske(pin_a));
      phase_b = bool(*getInPort(pin_b) & getMaske(pin_b));

      if ( phase_a ) n = 3;
      if ( phase_b ) n ^= 1;          // convert gray to binary
      return n;
    }
    
    void update_RelativCounter () {
      long new_abs = abs_counter >> shift;          // halbiert, geviertelt oder ... Steps pro Rastung
      
      if ( new_abs != abs_old) {
        if (new_abs > abs_old) {
          rel_counter++;
        }
        else {
          rel_counter--;
        }
        
        abs_old = new_abs;
      
        if (rel_counter < relMin) {
          rel_counter = relMax;
        }
        else if (rel_counter > relMax) {
          rel_counter = relMin;
        }
      }
    }
    
  public:              // von außen zugängliche Elemente ...

    // vorher GrayEncoder (byte _A, byte _B, byte _raster):
    GrayEncoder():
      // Initialisierungsliste
      relMin(0),
      relMax(0),
      shift(raster/2),
      rel_counter(0),     // Zähler
      abs_counter(0),     // Zähler
      direction(0),       // Richtungsanzeige  +1 / -1
      abs_old(0)
    { }
        
    void init()
    {
      *getDdrPort(pin_a) &= ~getMaske(pin_a);
      *getOutPort(pin_a) |=  getMaske(pin_a);
      
      *getDdrPort(pin_b) &= ~getMaske(pin_b);
      *getOutPort(pin_b) |=  getMaske(pin_b);
      
      last = getPhases();   // power on state
      enc_delta = 0;
    }

    void encode()
    {
      int n = getPhases();
      int diff = last - n;
      
      if ( diff & 1 )  {                        // bit 0 = value (1)
        last = n;                               // store new as next last
        enc_delta = (diff & 2) - 1;             // bit 1 = direction (+/-), Zähler
        abs_counter += (long) enc_delta;
        direction = enc_delta;                  // Richtungsanzeige (+1 / -1)
      }
      update_RelativCounter();
    }

    int getDirection()  {return direction; }      // Richtungsabfrage
    
    long getRelCounter()  {return rel_counter; }  // relativen Zählwert abfragen, ist schon angepasst
                  
    long getAbsCounter()  {return abs_counter >> shift; } // absoluten Zählwert abfragen
                                                          // x Steps pro Rastung
           
    int getA()  { return phase_a; }
    
    int getB()  { return phase_a; }
        
};


// ---------------------------------------------------------------------------------------

GrayEncoder <37, 36>      Encoder1;   // Phase A/B
GrayEncoder <40, 41, 2>   ALPS;       // Phase A/B / Raster

void setup() 
{
  Serial.begin(9600);
  Encoder1.init();
  ALPS.init();
}

void loop() 
{
   Encoder1.encode();
   ALPS.encode();
  // Ausgabe();
}

? ? ?

Hallo,

das ist ja nett. Während ich andere verarzte werde ich verarztet. :slight_smile:

Das sieht ja schon wieder zu einfach aus um wahr zu sein. Entweder lese ich die falschen Seiten im Buch oder ich denke zu kompliziert. Ich dachte ich muss "hunderte" Klassen-Templates" bauen die er dann unterscheiden kann.

Dein Code funktioniert. Jetzt sollte ich das auf meine anderen herkömmlichen Konstruktoren anwenden können ...
Vielen Dank.

Das sieht ja schon wieder zu einfach aus um wahr zu sein.

Über die Wahrheit müssen wir nicht streiten.
Und die naive Schlichtheit solcher Dinge, hat nicht unbedingt was mit mit dem anderen "Einfach" zu tun.
Ja, mit dem einen Einfach schon, aber nicht mit dem anderen "Einfach".

oder ich denke zu kompliziert

Das befürchte ich...
Zumindest, sehen die Ideen, auf die du kommst etwas danach aus.

Ich glaube, ich denke bei sowas nur ganz wenig.
Ist eher, wie "Malen nach Zahlen".

Ich stelle mir ein "Muster" vor, so wie es werden soll, welche Schnittstelle, und dann wird nur noch ausgemalt.
Wenn dann die Compiler Meldungen ausgeräumt sind, ...

Manchmal gibt es eine ganze Menge "Muster" oder mögliche Implementierungen.
Wenn möglich, werden die dann alle ausprobiert.

Ein solches "Muster" habe ich letztens durchgespielt...
Auch mit tief ins Buch schauen usw...

Eigentlich ein billiges Problem.
Die Anzahl Zellen eines Arrays herausfinden!
Erstaunlich vielfältig, so ein "einfaches" Problem.
(und etwas spannend, was man da alles über sich und C++ herausfindet)

EDIT:
Zusammenfassung der Entwicklung des arrayCount()
(habs mal aus der Wühlkiste raus gesucht)

// keinerlei Typeprüfung, recht kryptische Meldung bei falschem Parameter
// Und macros sind so C, so igitt, C++ kann das besser
#define arrayCount_A(array) (sizeof(array)/sizeof(array[0]))



/*
// nur ab C++14, wegen dem auto
// keinerlei Typeprüfung, recht kryptische Meldung bei falschem Parameter
constexpr size_t arrayCount_B(const auto &array)
{
  return sizeof(array)/sizeof(array[0]);
}
*/

// ersetzen des auto durch ein template Konstrukt
// weiterhin krypische Meldung, aber tut jetzt unter C++11
template<typename Array> constexpr size_t arrayCount_C(const Array &array)
{
     return sizeof(array)/sizeof(array[0]);
}


// jetzt Prüfung, ob wirklich ein Array übergeben wird
// Meldung sieht etwas besser/klarer aus
template<typename Array, size_t n> constexpr size_t arrayCount_D(const Array (&array)[n])
{
     return sizeof(array)/sizeof(array[0]);
}



// achja... wir haben die Groesse ja schon im Datentype des Arrays...
// einfach nutzen, was schon da ist, muss nicht berechet werden
// jetzt warnt der Kompiler allerdings wegen einer ungenutzten Variable (recht hat er)
template<typename Array, size_t n> constexpr size_t arrayCount_E(const Array (&array)[n])
{
     return n;
}


// den Parameter anonymisiert, und der Kompiler ist gluecklich.
// die endgueltige(?) Variante
template<typename Array, size_t n> constexpr size_t arrayCount_F(const Array (&)[n])
{
     return n;
}




byte testArray[3][4][5];  // deliquent




void setup() 
{
  Serial.begin(9600);    
    
  Serial.print("arrayCount_A 1: ");  Serial.println(arrayCount_A(testArray));
  Serial.print("arrayCount_A 2: ");  Serial.println(arrayCount_A(testArray[0]));
  Serial.print("arrayCount_A 3: ");  Serial.println(arrayCount_A(testArray[0][0]));
 // Serial.print("arrayCount_A 4: ");  Serial.println(arrayCount_A(testArray[0][0][0]));
  Serial.println();
  
/*
// nur ab C++14
  Serial.print("arrayCount_B 1: ");  Serial.println(arrayCount_B(testArray));
  Serial.print("arrayCount_B 2: ");  Serial.println(arrayCount_B(testArray[0]));
  Serial.print("arrayCount_B 3: ");  Serial.println(arrayCount_B(testArray[0][0]));
 // Serial.print("arrayCount_B 4: ");  Serial.println(arrayCount_B(testArray[0][0][0]));
  Serial.println();
*/

  Serial.print("arrayCount_C 1: ");  Serial.println(arrayCount_C(testArray));
  Serial.print("arrayCount_C 2: ");  Serial.println(arrayCount_C(testArray[0]));
  Serial.print("arrayCount_C 3: ");  Serial.println(arrayCount_C(testArray[0][0]));
 // Serial.print("arrayCount_C 4: ");  Serial.println(arrayCount_C(testArray[0][0][0]));
  Serial.println();

  Serial.print("arrayCount_D 1: ");  Serial.println(arrayCount_D(testArray));
  Serial.print("arrayCount_D 2: ");  Serial.println(arrayCount_D(testArray[0]));
  Serial.print("arrayCount_D 3: ");  Serial.println(arrayCount_D(testArray[0][0]));
 // Serial.print("arrayCount_D 4: ");  Serial.println(arrayCount_D(testArray[0][0][0]));
  Serial.println();

  Serial.print("arrayCount_E 1: ");  Serial.println(arrayCount_E(testArray));
  Serial.print("arrayCount_E 2: ");  Serial.println(arrayCount_E(testArray[0]));
  Serial.print("arrayCount_E 3: ");  Serial.println(arrayCount_E(testArray[0][0]));
 // Serial.print("arrayCount_E 4: ");  Serial.println(arrayCount_E(testArray[0][0][0]));
  Serial.println();

  Serial.print("arrayCount_F 1: ");  Serial.println(arrayCount_F(testArray));
  Serial.print("arrayCount_F 2: ");  Serial.println(arrayCount_F(testArray[0]));
  Serial.print("arrayCount_F 3: ");  Serial.println(arrayCount_F(testArray[0][0]));
 // Serial.print("arrayCount_F 4: ");  Serial.println(arrayCount_F(testArray[0][0][0]));
  Serial.println();

}

void loop() 
{


}

Hallo,

ja, mit Schnittstellenformulierung muss ich mich auch tiefer befassen.

So, ich konnte das adaptieren. Mit "herkömmlichen" Konstrukturen und 4 Instanzen es waren Flash 4380 Byte, RAM 824 Byte. Mit template und davon die direkte "pin_x" Nutzung sind es Flash 3808 Byte, RAM 326 Byte. Nutze ich noch die templates Variablen relMin und relMax direkt ohne Default-Konstruktur Umweg, steigt der Flash sagenhaft an. Flash 4692 Byte, RAM 310 Byte.

Was mir noch etwas unklar ist, die Zeile template< ... steht irgendwie verloren einfach so da. Woher weiß der Compiler das es zur Klasse gehört? Keine geschweifte Klammer, nix. Die sonstigen Konstruktoren stehen doch innnerhalb der Klasse. Da ist mir der Zusammenhang klar.

using Register = volatile byte *;

// UNO, Nano, Pro Mini usw.
#if defined(__AVR_ATmega328P__) | defined(__AVR_ATmega328__) | defined(__AVR_ATmega168P__) | defined(__AVR_ATmega168__)
  constexpr byte pinMaske[]     = {0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000,};
  constexpr Register portList[] = {&PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTD, &PORTB, &PORTB, &PORTB, &PORTB, &PORTB, &PORTB, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC,};
  constexpr Register ddrList[]  = {&DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD, &DDRB, &DDRB, &DDRB, &DDRB, &DDRB, &DDRB, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC,};
  constexpr Register pinList[]  = {&PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PIND, &PINB, &PINB, &PINB, &PINB, &PINB, &PINB, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC,};
//    #endif

// Mega
#elif defined(__AVR_ATmega2560__) | defined(__AVR_ATmega1280__)
  constexpr byte pinMaske[]     = {0b1, 0b10, 0b10000, 0b100000, 0b100000, 0b1000, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b10, 0b1, 0b10, 0b1, 0b1000, 0b100, 0b10, 0b1, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0b10000000, 0b100, 0b10, 0b1, 0b10000000, 0b1000000, 0b100000, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0b1000, 0b100, 0b10, 0b1, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000,};
  constexpr Register portList[] = {&PORTE, &PORTE, &PORTE, &PORTE, &PORTG, &PORTE, &PORTH, &PORTH, &PORTH, &PORTH, &PORTB, &PORTB, &PORTB, &PORTB, &PORTJ, &PORTJ, &PORTH, &PORTH, &PORTD, &PORTD, &PORTD, &PORTD, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTA, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTC, &PORTD, &PORTG, &PORTG, &PORTG, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTL, &PORTB, &PORTB, &PORTB, &PORTB, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTF, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK, &PORTK,};
  constexpr Register ddrList[]  = {&DDRE, &DDRE, &DDRE, &DDRE, &DDRG, &DDRE, &DDRH, &DDRH, &DDRH, &DDRH, &DDRB, &DDRB, &DDRB, &DDRB, &DDRJ, &DDRJ, &DDRH, &DDRH, &DDRD, &DDRD, &DDRD, &DDRD, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRA, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRC, &DDRD, &DDRG, &DDRG, &DDRG, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRL, &DDRB, &DDRB, &DDRB, &DDRB, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRF, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK, &DDRK,};
  constexpr Register pinList[]  = {&PINE, &PINE, &PINE, &PINE, &PING, &PINE, &PINH, &PINH, &PINH, &PINH, &PINB, &PINB, &PINB, &PINB, &PINJ, &PINJ, &PINH, &PINH, &PIND, &PIND, &PIND, &PIND, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINA, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PINC, &PIND, &PING, &PING, &PING, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINL, &PINB, &PINB, &PINB, &PINB, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINF, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK, &PINK,};
//    #endif

#else
  #error "This AVR type is currently not yet supported."
#endif

constexpr Register getOutPort(const byte pin) {
  return portList[pin];
};

constexpr Register getDdrPort(const byte pin) {
  return ddrList[pin];
};

constexpr Register getInPort(const byte pin) {
  return pinList[pin];
};

constexpr byte getMaske(const byte pin) {
  return pinMaske[pin];
};

template <byte pin_a, byte pin_b, byte raster = 0, int _min = 0, int _max = 0>
//template <byte pin_a, byte pin_b, byte raster = 0, int relMin = 0, int relMax = 0> // erhöht den Flashbedarf

class GrayEncoder               // Klassenname "GrayEncoder"
{
  protected:                    // private wird zu protected, nur intern zugängliche Elemente ...
    int relMin;
    int relMax;
    byte shift;                 // Zählkorrektur wegen Steps pro Rastung
    volatile long rel_counter;  // relativer Zähler, veränderbar
    volatile long abs_counter;  // absoluter Zähler, nicht veränderbar
    int direction;              // Richtungsanzeige  +1 / -1
    int enc_delta;
    int last;
    byte phase_a;               // erste Phase
    byte phase_b;               // zweite Phase
    long abs_old;
    
    int getPhases() {
      int n = 0;                      // new
      
      phase_a = bool(*getInPort(pin_a)  &  getMaske(pin_a));
      phase_b = bool(*getInPort(pin_b)  &  getMaske(pin_b));

      if ( phase_a ) n = 3;
      if ( phase_b ) n ^= 1;          // convert gray to binary
      return n;
    }
    
    void update_RelativCounter () {
      long new_abs = abs_counter >> shift;          // halbiert, geviertelt oder ... Steps pro Rastung
      
      if ( new_abs != abs_old) {
        if (new_abs > abs_old) {
          rel_counter++;
        }
        else {
          rel_counter--;
        }
        
        abs_old = new_abs;
      
        if (rel_counter < relMin) {
          rel_counter = relMax;
        }
        else if (rel_counter > relMax) {
          rel_counter = relMin;
        }
      }
    }
    
  public:              // von außen zugängliche Elemente ...    
    GrayEncoder ():
      // Initialisierungsliste
      relMin(_min),
      relMax(_max),
      shift(raster/2),
      rel_counter(_min),   // Zähler
      abs_counter(0),   // Zähler
      direction(0),     // Richtungsanzeige  +1 / -1     
      abs_old(0)
    { }
    
    void init()
    {
      *getDdrPort(pin_a) &= ~getMaske(pin_a);
      *getOutPort(pin_a) |=  getMaske(pin_a);
      
      *getDdrPort(pin_b) &= ~getMaske(pin_b);
      *getOutPort(pin_b) |=  getMaske(pin_b);
      
      last = getPhases();   // power on state
      enc_delta = 0;
    }

    void encode()
    {
      int n = getPhases();
      int diff = last - n;
      
      if ( diff & 1 )  {                        // bit 0 = value (1)
        last = n;                               // store new as next last
        enc_delta = (diff & 2) - 1;             // bit 1 = direction (+/-), Zähler
        abs_counter += (long) enc_delta;
        direction = enc_delta;                  // Richtungsanzeige (+1 / -1)
      }
      update_RelativCounter();
    }

    int getDirection()  {return direction; }      // Richtungsabfrage
    
    long getRelCounter()  {return rel_counter; }  // relativen Zählwert abfragen, ist schon angepasst
                  
    long getAbsCounter()  {return abs_counter >> shift; } // absoluten Zählwert abfragen
                                                          
    int getA()  { return phase_a; }
    
    int getB()  { return phase_a; }   
};

// ---------------------------------------------------------------------------------------

GrayEncoder <37, 36, 4, 10, 20> Encoder1;   // Phase A/B
GrayEncoder <37, 36, 4,  0, 10> ALPS_EM20B; // Phase A/B / Raster/ RelMin / RelMax 
GrayEncoder <40, 41, 2,  0, 20> ALPS_EC11;  // Phase A/B / Raster/ RelMin / RelMax
GrayEncoder <40, 41, 2>         ALPS;       // Phase A/B / Raster

Genau was zu erwarten zu ist. Templates sind Code Generatoren. Die erzeugen dir schnell eine neue Klasse für jede Variante mit anderen Parametern/Datentypen

Was mir noch etwas unklar ist, die Zeile template< ... steht irgendwie verloren einfach so da. Woher weiß der Compiler das es zur Klasse gehört?

Er erwartet eine Klasse oder Funktion nach der schließenden Klammer

ja, mit Schnittstellenformulierung muss ich mich auch tiefer befassen.

Für mich ist das der Dreh und Angelpunkt.
Schnittstellen/Interfaces

Was mir noch etwas unklar ist, die Zeile template< ... steht irgendwie verloren einfach so da.

Nee...
Du klebst sie da mitten rein.

Du schreibst: (ein hinkendes Beispiel)

void loop() 
{
  int

  a
  =


  42

  ;
}

Und sagst dann: "Das sieht aber komisch aus!"

Ich klemme die Zeile direkt vor das class, oder das template<...> direkt vor die Funktion, in der gleichen Zeile.
Bei Funktionen geht es nicht anders.

Bei Klassen kannst du dir mehr Platz lassen, das dient dann hauptsächlich dazu, um sich selber zu verwirren.

Templates kann man nur auf Funktionen, Strukturen und Klassen anwenden.
Es gibt keine Ausnahme, oder anderweitigen Einsatz.
Und damit ist die Zuordnung eindeutig.
Auch wenn du im Code 2 Meter Abstand einhältst.

Nutze ich noch die templates Variablen relMin und relMax direkt ohne Default-Konstruktur Umweg, steigt der Flash sagenhaft an. Flash 4692 Byte, RAM 310 Byte.

Ja, sowas kann dir passieren!

Templates sind kein Wundermittel, um Code zu schrumpfen.
Es kann auch das genaue Gegenteil eintreten.

Bedenke:
Ein Template ist eine Erzeugungsvorschrift für Klassen und Methoden.

Das Zauberwort ist Erzeugungsvorschrift!
Es wird was gebaut.

Wenn sich die Templateparameter unterscheiden, wird Code dupliziert!
Immer!

Dieser duplizierte Code "kann" schlanker sein, als Code welcher ohne Templates geschrieben wurde, muss aber nicht.

Etwas üben, dann siehst du von vornherein, wo es sich aufbläht.

Wie immer eine Gradwanderung.
Augen auf!


Templates sind also ein wunderbares Werkzeug, mit Ecken und Kanten.

Hallo,

muss ich verinnerlichen und verstehen lernen.

Die Speichereinsparung kommt jedoch aktuell von den constexpr Funktionen, nicht durch das template?

Wenn template Code Generatoren sind, muss doch das template genauso eine Instanz erzeugen wie die Klasse es mit ihrem Konstruktor machen würde.

Die Zeile ist nicht anders machbar? Ich musste nämlich hier die Reihenfolge der Parameter ändern, anders wie mit Klassen Konstruktoren die sich an Hand der Signatur unterschieden haben.

template <byte pin_a, byte pin_b, byte raster = 0, int _min = 0, int _max = 0>

Die Speichereinsparung kommt jedoch aktuell von den constexpr Funktionen, nicht durch das template?

Richtig!
Die Arbeit wird in den Kompiler verlagert, darum weniger Code auf dem µC.

Wenn template Code Generatoren sind, muss doch das template genauso eine Instanz erzeugen wie die Klasse es mit ihrem Konstruktor machen würde.

Wieso?

Aus Templates werden Klassen und Funktionen generiert, keine Instanzen.
Und diese generierten Klassen sind dann die Vorlage/Erzeugungsvorschrift für die konkrete Instanz

Die Zeile ist nicht anders machbar? Ich musste nämlich hier die Reihenfolge der Parameter ändern, anders wie mit Klassen Konstruktoren die sich an Hand der Signatur unterschieden haben.

Das verstehe ich nicht.

Hallo,

jetzt wird mir einiges klarer. Danke erstmal.

Eine Bitte noch. In welcher Reihenfolge hast du die Pins beim Leonardo festgelegt? 31 Pins? Da sind paar doppelt?
Pin 0 bis 13 ist klar. Danach wie weiter? Danach müßte wie beim Uno und A0 bis A5 kommen?
Der original Uno und Leonardo müßten die gleiche Anzahl Pins haben.
Ich frage deshalb weil ich eine kompatible Portbit Liste erstellen möchte. Für den Fall der Fälle das man das einmal benötigt.

Eine Bitte noch. In welcher Reihenfolge hast du die Pins beim Leonardo festgelegt?

Die Arrays habe ich per Arduino Programm aus den Original Daten gebildet.
Selber keinerlei Hand angelegt.

Wenn da bei mir ein Fehler ist, dann auch im Original.

Hallo,

waren die Grundlage die pins_arduino.h Dateien?

Hallo,

ich nehme mal an ja, was sonst. Bleiben wir beim Leonardo.
C:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\leonardo
Vom Arduino Pin D0 bis D23 gehe ich mit.
Was aber der Gedanke hinter D24 bis D30 ist erschließt sich mir nicht.
Weil bis auf D30 sind alle anderen davon damit doppelt vorhanden.
Steht sogar in den Kommentaren extra so drin. ?
Das Problem beginnt doch jetzt wenn man D4 und D24 im Programm verwendet, damit unterschiedliche Dinge tun möchte, Eingang/Ausgang und sich dann wundert wenn es knallt.
SDA und SCL sind schon gefährlich doppelt auf den Pinheader rausgeführt.
Für mich hat er nur 23 nutzbare Pins. Mehr Kabel kann ich nicht anstecken.
SPI-SS (PB0) ist nicht rausgeführt. Wird jedoch mit gezählt?

Okay, der Sinn dahinter mag sein das man einige digitale Pins zusätzlich noch als analogen Eingang verwenden kann. Weil der 32u4 das kann. Wird nur niemand nutzen wenn die Boards so nicht beschriftet sind. Das sind Internas die man sich erarbeiten muss.

Das Problem sehe ich nun leider in deinen Array Listen. Die werden ja nur digital genutzt. Nicht das es hier mal "knallt". Ich würde es beschränken auf 23 Pins. Ich meins ja nur gut.

Ich meins ja nur gut.

Ist es in den original Dateien anders?

Ich möchte dazu kompatibel bleiben.
Keine Extrawurst.

Hallo,

nein, ist in den original Dateien wie bei dir.
Meine Sorgen fangen an, weil der Leonardo nur D0 bis D23 echte Pins hat, keine 31 und deine Listen sind nur digital nutzbar. Nicht analog. D30 ist auch kein Pin, nur die Tx Led. Ich wüßte nicht ob irgendjemand einen Pin D24 bis D30 auf dem Board findet?

Wenn man mehr analoge Eingänge benötigt, dann wird man D4, D6, D8, D9, D10, D12 mit A6 ... A11 ansprechen.
Sodass diese dann automatisch als analoge Eingänge konfiguriert werden mögen.

Wollte nur auf ein mögliches Problem hinweisen. Sodass jemand der deine Listen nutzt erst gar nicht mit Pinnummern 24 bis 30 in Versuchung kommt. Weil eh sinnfrei für digital. Wenn ich "Arduino" wäre, ich hätte die digitalen Nummern D24 bis D30 erst gar nicht eingeführt. Nur A6 bis A11. Mehr wollte ich gar nicht sagen. :wink:

Arduino
Leonardo
D0
D1
D2
D3
D4
D5
D6
D7
D8
D9
D10
D11
D12
D13
D14 MISO
D15 SCK
D16 MOSI
D17 SS
D18 A0
D19 A1
D20 A2
D21 A3
D22 A4
D23 A5
D24 A6/D4
D25 A7/D6
D26 A8/D8
D27 A9/D9
D28 A10/D10
D29 A11/D12
D30 tx Led

Nunja..

Das Problem habe ich schon verstanden....
Sehe nur keine Lösung.

Falls die Arduino Gemeinde das irgendwann ändert, wird auch mein Generator das ändern, und dann wird die Änderung in meine Lib einfließen.
(vermutlich)