Ein Schiedsrichtertisch für Tischtennis mit 74HC595 und 74HC165

tt.h

void bigDisplay();

/*
       0      |     7     |
    5     1   |  2     6  |
       6      |     1     |
    4     2   |  3     5  |
       3      |     4     |
*/

/*
  Digit0 Timer Hunderter  // Minute Einer
  Digit1 Timer zehner     // Sekunde Zehner
  Digit2 Timer einer      // Sekunde Einer
  Digit3 SpielerA Satz + Aufschlag D40
  Digit4 SpielerA Punkte einer + Zehner D32
  Digit5 SpielerB Satz + Aufschlag D24
  Digit6 SpielerB Punkte einer + Zehner D16
*/
constexpr bool segActiv {0};  // Die Segmente leuchten, wenn RegisterPin => 0!!!
constexpr uint8_t bigDisp {1};                        // -> Nummer des Registerzweig aus displays
constexpr uint8_t bigDispReg {7};                     // -> Anzahl der Register auf dem Zweig
bool bigDispRegOut[bigDispReg * 8] = {!segActiv};     // -> Summe aller bits auf dem Zweig
bool oldBigDisplay[bigDispReg * 8] = {!segActiv};     // Merker für Aktuallisierung



constexpr byte displays {3};
struct REGPIN
{
  uint8_t clock;
  uint8_t data;
  uint8_t latch;
};
REGPIN regpin[displays]
{{5, 7, 6}, {9, 11, 10}, {12, 14, 13}};

boolean dec[][8] =
{
  {1, 1, 0, 0, 0, 0, 0, 0}, // 0
  {1, 1, 1, 1, 1, 0, 0, 1}, // 1
  {1, 0, 1, 0, 0, 1, 0, 0}, // 2
  {1, 0, 1, 1, 0, 0, 0, 0}, // 3
  {1, 0, 0, 1, 1, 0, 0, 1}, // 4
  {1, 0, 0, 1, 0, 0, 1, 0}, // 5
  {1, 0, 0, 0, 0, 0, 1, 0}, // 6
  {1, 1, 1, 1, 1, 0, 0, 0}, // 7
  {1, 0, 0, 0, 0, 0, 0, 0}, // 8
  {1, 0, 0, 1, 0, 0, 0, 0}, // 9
  {1, 1, 1, 1, 1, 1, 1, 1}, // ALL OFF
};

constexpr uint8_t spielers {2};
struct SPIELER
{
  uint8_t points;
  uint8_t sets;
  bool aufschlag;
};
SPIELER spieler[spielers]
{{0, 0, 0}, {0, 0, 0}};

REGPIN keypad
{16, 17, 15};

struct SCOREPAD
{
  bool aufschlag;   // 21 # 22
  bool spielPlus;   //  6 # 14
  bool spielMinus;  //  5 # 13
  bool spielReset;  //  4 # 12
  bool satzPlus;    //  3 # 11
  bool satzMinus;   //  2 # 10
  bool satzReset;   //  1 #  9
};

struct KEYTABLE
{
  bool timeOutStart;   // 20 Auszeit
  bool timeOutStop;    // 19
  bool timeEinStart;   // 18 Einspielzeit
  bool timeEinStop;    // 17
  SCOREPAD sp[spielers]; // Spielertasten
};

constexpr bool gedrueckt {HIGH};

KEYTABLE kt;
KEYTABLE oldtable; // Hilfstabelle - hauptsächlich für das merken der Tastendrücke / kommt noch weg

struct ZEIT
{
  /*
    1+2 Minutenanzeige ( Anzeige 1 oder 2)( aber nur 2, 1 ist nicht in benutzung)
    3 Sekundenanzeige zehner ( Anzeige 0-5)
    4 Sekundenanzeige einer ( Anzeige 0-9)
  */
  uint8_t minute = 0;
  uint8_t sekunde = 0;
  uint32_t lastTik = 0; // Merker, wann ausgelöst
} zeit;


//
void getKeys()                                    // Funktion aus dem Ursprungspost übernommen und erweitert
{
  // Das sind Schieberegister n:1
  // Auch wenn nicht alle Pins gebraucht werden, müssen ungenutzte mitgelesen werden!
  constexpr uint8_t zaehler {24};                 // Anzahl Chips * 8 Pin
  constexpr uint32_t interval {50};               // "debounce Time"
  static uint32_t lastRead = 0;                   // Merker
  if (millis() - lastRead > interval)
  {
    digitalWrite(keypad.latch, LOW);              // Chip  aktivieren
    delay(1);
    digitalWrite(keypad.latch, HIGH);
    bool readIn = true;
    for (uint8_t b = 0; b < zaehler; b++)           // Eingänge durchzählen
    {
      readIn = digitalRead(keypad.data);
      switch (b)                                    // Zuweisung Pin => Variable
      {
//*INDENT-OFF*  
      case  1: kt.sp[0].satzReset   = readIn; break;  // bit 1 aus Schieberegister 
      case  2: kt.sp[0].satzMinus   = readIn; break;  // bit 2
      case  3: kt.sp[0].satzPlus    = readIn; break;  // bit 3
      case  4: kt.sp[0].spielReset  = readIn; break;
      case  5: kt.sp[0].spielMinus  = readIn; break;
      case  6: kt.sp[0].spielPlus   = readIn; break;
      case  9: kt.sp[1].satzReset   = readIn; break;
      case 10: kt.sp[1].satzMinus   = readIn; break;
      case 11: kt.sp[1].satzPlus    = readIn; break;
      case 12: kt.sp[1].spielReset  = readIn; break;
      case 13: kt.sp[1].spielMinus  = readIn; break;
      case 14: kt.sp[1].spielPlus   = readIn; break;
      case 17: kt.timeEinStop       = readIn; break; 
      case 18: kt.timeEinStart      = readIn; break;
      case 19: kt.timeOutStop       = readIn; break; 
      case 20: kt.timeOutStart      = readIn; break; 
      case 21: kt.sp[0].aufschlag   = readIn; break; 
      case 22: kt.sp[1].aufschlag   = readIn; break;
//*INDENT-ON*  
      }
      digitalWrite(keypad.clock, HIGH);
      digitalWrite(keypad.clock, LOW);
    }
    lastRead = millis();
  }
}
//
void printSegment(const byte &segmentReihe, const bool *array, const byte &laenge)
{
  for (uint8_t b = 0; b < laenge; b++)
  {
    digitalWrite(regpin[segmentReihe].data, array[b]);    // Array auf dem Schieberegister
    digitalWrite(regpin[segmentReihe].clock, HIGH);
    digitalWrite(regpin[segmentReihe].clock, LOW);
    digitalWrite(regpin[segmentReihe].data, LOW);
  }
  digitalWrite(regpin[segmentReihe].latch, HIGH);
  digitalWrite(regpin[segmentReihe].latch, LOW);
}
//
void displayTime()
{
  constexpr byte laenge {32};
  bool time[laenge] = {0};
  bool *timeSekundeEiner = &time[0];
  bool *timeSekundeZehner = &time[8];     // 2.tes Register
  bool *timeMinuteEiner = &time[16];      // 3.tes Register
  bool *timeMinuteZehner = &time[24];     // 4.tes Register
  memset(time, 0, laenge);
  memcpy(timeMinuteZehner, dec[zeit.minute / 10], 8);
  memcpy(timeMinuteEiner, dec[zeit.minute % 10], 8);
  memcpy(timeSekundeZehner, dec[zeit.sekunde / 10], 8);
  memcpy(timeSekundeEiner, dec[zeit.sekunde % 10], 8);
  printSegment(2, time, laenge);                        // 3.ter Kanal
}
//
void displaySpieler()
{
  /*
    1+2 Punktanzeige Spieler A (Anzeige 0-19)
    3+4 Satzanzeige Spieler A (Anzeige 0-9)( aber nur 3, 4 ist nicht in benutzung)
    5+6 Satzanzeige Spieler B (Anzeige 0-9)( aber nur 5, 6 ist nicht in benutzung)
    7+8 Punktanzeige Spieler B ( Anzeige 0-19)
  */
  constexpr byte laenge {64};
  bool spell[laenge] = {0};
  bool *spielerBzehner = &spell[8];
  bool *spielerBeiner = &spell[0];
  bool *satzBeiner = &spell[24];
  bool *satzAeiner = &spell[40];
  bool *spielerAzehner = &spell[56];
  bool *spielerAeiner = &spell[48];
  memset(spell, 0, laenge);
  memcpy(spielerAzehner, dec[spieler[0].points / 10], 8);
  memcpy(spielerAeiner, dec[spieler[0].points % 10], 8);
  memcpy(satzAeiner, dec[spieler[0].sets % 10], 8);
  memcpy(satzBeiner, dec[spieler[1].sets % 10], 8);
  memcpy(spielerBzehner, dec[spieler[1].points / 10], 8);
  memcpy(spielerBeiner, dec[spieler[1].points % 10], 8);
  printSegment(0, spell, laenge);
}
//
void bigDisplay()
{
  constexpr uint32_t printIntervall {500};
  static uint32_t lastPrint = 0;
  memset(bigDispRegOut, !segActiv, bigDispReg * 8);  // alle bits löschen
  bool *timerHunderter = &bigDispRegOut[0 * 8];      // Register 7 | DIGIT 0
  bool *timerZehner = &bigDispRegOut[1 * 8];         // Register 6 | DIGIT 1
  bool *timerEiner = &bigDispRegOut[2 * 8];          // Register 5 | DIGIT 2
  bool *spielerAsatz = &bigDispRegOut[3 * 8];
  bool *spielerAeiner = &bigDispRegOut[4 * 8];
  bool *spielerBsatz = &bigDispRegOut[5 * 8];
  bool *spielerBeiner = &bigDispRegOut[6 * 8];
  //
  memcpy(timerHunderter, dec[zeit.minute % 10], 8);
  memcpy(timerZehner, dec[zeit.sekunde / 10], 8);
  memcpy(timerEiner, dec[zeit.sekunde % 10], 8);
  memcpy(spielerAeiner, dec[spieler[0].points % 10], 8);
  memcpy(spielerAsatz, dec[spieler[0].sets % 10], 8);
  memcpy(spielerBeiner, dec[spieler[1].points % 10], 8);
  memcpy(spielerBsatz, dec[spieler[1].sets % 10], 8);
  bigDispRegOut[16] = spieler[1].points / 10 ? segActiv : !segActiv;  // spielerBZehner
  bigDispRegOut[24] = spieler[1].aufschlag   ? segActiv : !segActiv;  // spielerBAufschlag
  bigDispRegOut[32] = spieler[0].points / 10 ? segActiv : !segActiv;  // spielerAZehner
  bigDispRegOut[40] = spieler[0].aufschlag   ? segActiv : !segActiv;  // spielerAAufschlag
  for (byte b = 0; b < 56; b++)
  { bigDispRegOut[b] = !bigDispRegOut[b]; }
  if ((memcmp(bigDispRegOut, oldBigDisplay, bigDispReg * 8) != 0)  || // Neuer Inhalt ODER
      (millis() - printIntervall > lastPrint))                        // Zeit abgelaufen
  {
    for (byte b = 0; b < 56; b++)
    {
      Serial.print(bigDispRegOut[b]);
      if ((b + 1) % 8 == 0)
      { Serial.print(" | "); }
    }
    Serial.println();
    printSegment(bigDisp, bigDispRegOut, bigDispReg * 8);
    memcpy(oldBigDisplay, bigDispRegOut, bigDispReg * 8);
    lastPrint = millis();
  }
  //while (1);
};


void myTimer()
{
  constexpr uint32_t _oneSecond {1000};
  if (zeit.minute || zeit.sekunde)   // Solange Zeit aktiv
  {
    if (millis() - zeit.lastTik >= _oneSecond)
    {
      if (zeit.sekunde > 0)
      { zeit.sekunde--; }
      else if (zeit.minute > 0)
      {
        zeit.minute--;
        zeit.sekunde = 59;
      }
      zeit.lastTik += _oneSecond;
    }
  }
}

//
void segmentTest()
{
  for (byte b = 0; b < 10; b++)
  {
    for (byte s = 0; s < spielers; s++)
    {
      spieler[s].points = spieler[s].points ? 0 : 88;
      spieler[s].sets = spieler[s].sets ? 0 : 88;
      spieler[s].aufschlag = spieler[s].aufschlag ? 0 : 1;
    }
    zeit.minute = zeit.minute ? 0 : 88;
    zeit.sekunde = zeit.sekunde ? 0 : 88;
    displaySpieler();
    displayTime();
    bigDisplay();
    delay(500);
  }
}
//
void allNew()
{
  for (byte s = 0; s < spielers; s++)
  {
    spieler[s].points = 0;
    spieler[s].sets = 0;
    spieler[s].aufschlag = false;
  }
  zeit.minute = 0;
  zeit.sekunde = 0;
}

neuer Code:

#include "tt.h"

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Tischtennis Display"));
  //display init
  for (byte b = 0; b < displays; b++)
  {
    pinMode(regpin[b].clock, OUTPUT);
    pinMode(regpin[b].data, OUTPUT);
    pinMode(regpin[b].latch, OUTPUT);
    digitalWrite(regpin[b].clock, LOW);
    digitalWrite(regpin[b].data, LOW);
    digitalWrite(regpin[b].latch, LOW);
  };
  pinMode(keypad.clock, OUTPUT);
  pinMode(keypad.data, INPUT);
  pinMode(keypad.latch, OUTPUT);
  digitalWrite(keypad.clock, LOW);
  digitalWrite(keypad.latch, LOW);
  segmentTest();
}

void loop()
{
  getKeys();        // Holt Tastenzustände
  checkKeys();      // wertet Tasten aus
  myTimer();        // CountDownTimer
  displaySpieler(); // kleine Anzeige
  bigDisplay();     // große Anzeige
  displayTime();    // countDownDisplay
}



/*
   Angabe

  Wird bei Start vergeben und danach alle 2 Punkte gewechselt....

  Jetzt die Besonderheiten....

  Ab Punktestand 10:10 wird nach jedem Punkt gewechselt

  Im 5ten Satz werden die Seiten gewechselt wenn jemmand 5 Punkte erreicht hat das heißt
  Angabe wechselt natürlich mit wie die Sätze und die Punkte auch

  Nach beendeten Satz wird die Angabe gewechselt(Reset Punkte), d.h. SpielerA hat Angabe gewonnen, im 2ten Satz fängt SpielerB mit Angabe an, im dritten wieder SpielerA usw

  So

  Ist da was von Realisierbar?
*/

void nextAufschlag()
{
  byte myAufschlag = 0;
  for (byte b = 0; b < spielers; b++)                 // gehe durch alle Spieler
  {
    if (spieler[b].aufschlag)                        // Spieler hat aufschlag ?
    {
      myAufschlag = b + 1;                           // nächsten Spieler setzen
      if (myAufschlag > spielers - 1)                 // Wert ist grßer als Spieler
      { myAufschlag = 0; }                           // Dann erster Spieler
    }
  }
  newAufschlag(myAufschlag);                       // Aufschlag setzen
}
//
void automatikAufschlag()                                 // wird aufgerufen, wenn points-Taste gedrückt wurde
{
  if (spieler[0].points + spieler[0].points < 20)         // summe points für jedes 2tes Spiel
  {
    if ((spieler[0].points + spieler[1].points) % 2 == 0) // jedes zweite Spiel auswerten
    {
      nextAufschlag();                                    //
    }
  }
  else                                                     // Jedes Spiel
  {
    nextAufschlag();
  }
}

void newAufschlag(const byte x)
{
  for (byte a = 0; a < spielers; a++)
  {spieler[x].aufschlag = false;}                      // löscht alles
  spieler[x].aufschlag = true;                         // setzt aktuellen
}

void checkKeys()
{
  for (byte b = 0; b < spielers; b++)
  {
    // aufschlag
    if (kt.sp[b].aufschlag != oldtable.sp[b].aufschlag)
    {
      if (kt.sp[b].aufschlag == gedrueckt)
      {
        newAufschlag(b);
      }
      oldtable.sp[b].aufschlag = kt.sp[b].aufschlag;
    }
    // satz Reset
    if (kt.sp[b].satzReset != oldtable.sp[b].satzReset)
    {
      if (kt.sp[b].satzReset == gedrueckt)
      { spieler[b].sets = 0;}
      oldtable.sp[b].satzReset = kt.sp[b].satzReset;
    }
    // satz Minus
    if (kt.sp[b].satzMinus != oldtable.sp[b].satzMinus)        // Vergleich auf deaktiven Merker
    {
      if (kt.sp[b].satzMinus == gedrueckt)
      {
        if (spieler[b].sets > 0)                                  // Nur wenn was auf dem Zähler steht
        { spieler[b].sets--; }
      }
      oldtable.sp[b].satzMinus = kt.sp[b].satzMinus;            // Merker damit nicht weiter gezählt wird
    }
    // satz Plus
    if (kt.sp[b].satzPlus != oldtable.sp[b].satzPlus)           // mit Merker
    {
      if (kt.sp[b].satzPlus == gedrueckt)
      { spieler[b].sets++; }
      oldtable.sp[b].satzPlus = kt.sp[b].satzPlus;
    }
    // spiel Reset
    if (kt.sp[b].spielReset != oldtable.sp[b].spielReset)
    {
      if (kt.sp[b].spielReset == gedrueckt)
      { spieler[b].points = 0; }
      oldtable.sp[b].spielReset = kt.sp[b].spielReset;
    }
    // spiel Minus
    if (kt.sp[b].spielMinus != oldtable.sp[b].spielMinus)        // mit Merker für Spielstand
    {
      if (spieler[b].points > 0 && kt.sp[b].spielMinus == gedrueckt)
      { spieler[b].points--; }
      oldtable.sp[b].spielMinus = kt.sp[b].spielMinus;
    }
    // spiel Plus
    if (kt.sp[b].spielPlus != oldtable.sp[b].spielPlus)           // Siehe Satz
    {
      if (kt.sp[b].spielPlus == gedrueckt)
      {
        spieler[b].points++;
        automatikAufschlag();
      }
      oldtable.sp[b].spielPlus = kt.sp[b].spielPlus;
    }
  }
  // EinspielZeit
  if (kt.timeEinStart != oldtable.timeEinStart)                   // Tastenstatus geändert?
  {
    if (kt.timeEinStart == gedrueckt)                             //
    {
      zeit.minute = 2;                                            // Vorbelegung
      zeit.sekunde = 0;
      zeit.lastTik = millis();                                    // aktuelle Zeit für timer
    }
    oldtable.timeEinStart = kt.timeEinStart;                      // Tastenstatus merken
  }
  if (kt.timeEinStop != oldtable.timeEinStop)
  {
    if (kt.timeEinStop == gedrueckt)
    {
      zeit.minute = 0;
      zeit.sekunde = 0;;
    }
    oldtable.timeEinStop = kt.timeEinStop;
  }
  // TimeOutZeit
  if (kt.timeOutStart != oldtable.timeOutStart)
  {
    if (kt.timeOutStart == gedrueckt)
    {
      zeit.minute = 1;
      zeit.sekunde = 0;
      zeit.lastTik = millis();
    }
    oldtable.timeOutStart = kt.timeOutStart;
  }
  if (kt.timeOutStop != oldtable.timeOutStop)
  {
    if (kt.timeOutStop == gedrueckt)
    {
      zeit.minute = 0;
      zeit.sekunde = 0;;
    }
    oldtable.timeOutStop = kt.timeOutStop;
  }
  //
}

Bei jedem point+ wird jetzt geprüft, ob der Aufschlag getauscht wird - mit entsprechender Aktion.
So der Plan.

Hinweis noch am Rande: Den Spielerwechsel hab ich noch nicht. Ich will erstmal, dass der richtig mitzählt.

Guten Morgen,

Diese Codezeilen füge Ich hinzu, kein Austausch richtig?

doch tt.h austauschen und dann den Code.

Er wechselt nicht die angabe sondern fügt hinzu...

in newAufschlag() ändern in:

  {spieler[a].aufschlag = false;}                      // löscht alles

Das wechseln funktioniert jetzt.
Respekt, das du das hinbekommst war mir eigentlich klar, aber das das so schnell geht respekt!

Was anderes....

Wäre es ein großer Aufwand die zuordnung der einzelnen 595er zu ändern?

Ich überlege alles auf eine Platine zu setzen inklusive mein 64 PCB anschluss zu den Panellen.
Und da weiß Ich noch nicht wie Ich die Platine anordnen kann!

Nö.

  bool *timerHunderter = &bigDispRegOut[0 * 8];      // Register 7 | DIGIT 0
  bool *timerZehner = &bigDispRegOut[1 * 8];         // Register 6 | DIGIT 1
  bool *timerEiner = &bigDispRegOut[2 * 8];          // Register 5 | DIGIT 2
  bool *spielerAsatz = &bigDispRegOut[3 * 8];
  bool *spielerAeiner = &bigDispRegOut[4 * 8];
  bool *spielerBsatz = &bigDispRegOut[5 * 8];
  bool *spielerBeiner = &bigDispRegOut[6 * 8];

Die erste Zahl ist die Position.
Tauscht Du die Position, oder änderst das so, dass alle an einem neuen Strang hänge, ist nur die Zuordnung neu zu vergeben.
Ändert sich auch die Zuordnung der Pins zum Segment (7=>a, 6=>b ...) sind das 20 Minuten mehr, wenn das nicht wieder falsch auf dem Papier ist...

Okay dann weiß Ich bescheid, danke

Bin grade am Layout der Platine dran...

Melde mich dann wenn Ich weiter bin.

Wenn du noch muse und lust hast kannst ja noch den Spielerwechsel einbauen.

Der kommt mit jedem 5-ten gespielten Satz?

Es ist ein 3 Satz sieg Spiel,
d.h. der der als erstes 3 Sätze gewonnen hat gewinnt.
also im 5ten Satz wird gewechselt wenn ein Spieler 5 Punkte erreicht hat, es geht dann aber ganz normal weiter.
Ich hoffe es war verständlich

Das wirst Du merken, wenn ich das gebaut habe.
Ich baue immer nur das, wie ich es verstehe :slight_smile:

Aber ums mal abzukürzen:
Bedingung ist, dass wir im 5.ten Satz sind und das der erste Spieler den 5ten Punkt gemacht hat.

Genau

neu tt.h

void bigDisplay();

/*
       0      |     7     |
    5     1   |  2     6  |
       6      |     1     |
    4     2   |  3     5  |
       3      |     4     |
*/

/*
  Digit0 Timer Hunderter  // Minute Einer
  Digit1 Timer zehner     // Sekunde Zehner
  Digit2 Timer einer      // Sekunde Einer
  Digit3 SpielerA Satz + Aufschlag D40
  Digit4 SpielerA Punkte einer + Zehner D32
  Digit5 SpielerB Satz + Aufschlag D24
  Digit6 SpielerB Punkte einer + Zehner D16
*/
constexpr bool segActiv {0};  // Die Segmente leuchten, wenn RegisterPin => 0!!!
constexpr uint8_t bigDisp {1};                        // -> Nummer des Registerzweig aus displays
constexpr uint8_t bigDispReg {7};                     // -> Anzahl der Register auf dem Zweig
bool bigDispRegOut[bigDispReg * 8] = {!segActiv};     // -> Summe aller bits auf dem Zweig
bool oldBigDisplay[bigDispReg * 8] = {!segActiv};     // Merker für Aktuallisierung



constexpr byte displays {3};
struct REGPIN
{
  uint8_t clock;
  uint8_t data;
  uint8_t latch;
};
REGPIN regpin[displays]
{{5, 7, 6}, {9, 11, 10}, {12, 14, 13}};

boolean dec[][8] =
{
  {1, 1, 0, 0, 0, 0, 0, 0}, // 0
  {1, 1, 1, 1, 1, 0, 0, 1}, // 1
  {1, 0, 1, 0, 0, 1, 0, 0}, // 2
  {1, 0, 1, 1, 0, 0, 0, 0}, // 3
  {1, 0, 0, 1, 1, 0, 0, 1}, // 4
  {1, 0, 0, 1, 0, 0, 1, 0}, // 5
  {1, 0, 0, 0, 0, 0, 1, 0}, // 6
  {1, 1, 1, 1, 1, 0, 0, 0}, // 7
  {1, 0, 0, 0, 0, 0, 0, 0}, // 8
  {1, 0, 0, 1, 0, 0, 0, 0}, // 9
  {1, 1, 1, 1, 1, 1, 1, 1}, // ALL OFF
};

constexpr uint8_t spielers {2};
struct SPIELER
{
  uint8_t points;
  uint8_t sets;
  bool aufschlag;
};
struct SPIEL
{
  SPIELER spieler[spielers];
  bool isChange;
} spiel; //{{0, 0, 0}, {0, 0, 0}, 0};

REGPIN keypad
{16, 17, 15};

struct SCOREPAD
{
  bool aufschlag;   // 21 # 22
  bool spielPlus;   //  6 # 14
  bool spielMinus;  //  5 # 13
  bool spielReset;  //  4 # 12
  bool satzPlus;    //  3 # 11
  bool satzMinus;   //  2 # 10
  bool satzReset;   //  1 #  9
};

struct KEYTABLE
{
  bool timeOutStart;   // 20 Auszeit
  bool timeOutStop;    // 19
  bool timeEinStart;   // 18 Einspielzeit
  bool timeEinStop;    // 17
  SCOREPAD sp[spielers]; // Spielertasten
};

constexpr bool gedrueckt {HIGH};

KEYTABLE kt;
KEYTABLE oldtable; // Hilfstabelle - hauptsächlich für das merken der Tastendrücke / kommt noch weg

struct ZEIT
{
  /*
    1+2 Minutenanzeige ( Anzeige 1 oder 2)( aber nur 2, 1 ist nicht in benutzung)
    3 Sekundenanzeige zehner ( Anzeige 0-5)
    4 Sekundenanzeige einer ( Anzeige 0-9)
  */
  uint8_t minute = 0;
  uint8_t sekunde = 0;
  uint32_t lastTik = 0; // Merker, wann ausgelöst
} zeit;


//
void newAufschlag(const byte x)
{
  for (byte a = 0; a < spielers; a++)
  {spiel.spieler[a].aufschlag = false;}                      // löscht alles
  spiel.spieler[x].aufschlag = true;                         // setzt aktuellen
}
//
void nextAufschlag()
{
  byte myAufschlag = 0;
  for (byte b = 0; b < spielers; b++)                 // gehe durch alle Spieler
  {
    if (spiel.spieler[b].aufschlag)                        // Spieler hat aufschlag ?
    {
      // nächsten Spieler setzen
      // Wert ist größer als Spieler, dann erster Spieler
      myAufschlag = b + 1 > spielers ? 0 : b + 1;
    }
  }
  newAufschlag(myAufschlag);                       // Aufschlag setzen
}
//
void automatikAufschlag()                                 // wird aufgerufen, wenn points-Taste gedrückt wurde
{
  if (spiel.spieler[0].points + spiel.spieler[0].points < 20)         // summe points für jedes 2tes Spiel
  {
    if ((spiel.spieler[0].points + spiel.spieler[1].points) % 2 == 0) // jedes zweite Spiel auswerten
    {
      nextAufschlag();                                    //
    }
  }
  else                                                     // Jedes Spiel
  {
    nextAufschlag();
  }
}

//
void getKeys()                                    // Funktion aus dem Ursprungspost übernommen und erweitert
{
  // Das sind Schieberegister n:1
  // Auch wenn nicht alle Pins gebraucht werden, müssen ungenutzte mitgelesen werden!
  constexpr uint8_t zaehler {24};                 // Anzahl Chips * 8 Pin
  constexpr uint32_t interval {50};               // "debounce Time"
  static uint32_t lastRead = 0;                   // Merker
  if (millis() - lastRead > interval)
  {
    digitalWrite(keypad.latch, LOW);              // Chip  aktivieren
    delay(1);
    digitalWrite(keypad.latch, HIGH);
    bool readIn = true;
    for (uint8_t b = 0; b < zaehler; b++)           // Eingänge durchzählen
    {
      readIn = digitalRead(keypad.data);
      switch (b)                                    // Zuweisung Pin => Variable
      {
//*INDENT-OFF*  
      case  1: kt.sp[0].satzReset   = readIn; break;  // bit 1 aus Schieberegister 
      case  2: kt.sp[0].satzMinus   = readIn; break;  // bit 2
      case  3: kt.sp[0].satzPlus    = readIn; break;  // bit 3
      case  4: kt.sp[0].spielReset  = readIn; break;
      case  5: kt.sp[0].spielMinus  = readIn; break;
      case  6: kt.sp[0].spielPlus   = readIn; break;
      case  9: kt.sp[1].satzReset   = readIn; break;
      case 10: kt.sp[1].satzMinus   = readIn; break;
      case 11: kt.sp[1].satzPlus    = readIn; break;
      case 12: kt.sp[1].spielReset  = readIn; break;
      case 13: kt.sp[1].spielMinus  = readIn; break;
      case 14: kt.sp[1].spielPlus   = readIn; break;
      case 17: kt.timeEinStop       = readIn; break; 
      case 18: kt.timeEinStart      = readIn; break;
      case 19: kt.timeOutStop       = readIn; break; 
      case 20: kt.timeOutStart      = readIn; break; 
      case 21: kt.sp[0].aufschlag   = readIn; break; 
      case 22: kt.sp[1].aufschlag   = readIn; break;
//*INDENT-ON*  
      }
      digitalWrite(keypad.clock, HIGH);
      digitalWrite(keypad.clock, LOW);
    }
    lastRead = millis();
  }
}
//
void printSegment(const byte &segmentReihe, const bool *array, const byte &laenge)
{
  for (uint8_t b = 0; b < laenge; b++)
  {
    digitalWrite(regpin[segmentReihe].data, array[b]);    // Array auf dem Schieberegister
    digitalWrite(regpin[segmentReihe].clock, HIGH);
    digitalWrite(regpin[segmentReihe].clock, LOW);
    digitalWrite(regpin[segmentReihe].data, LOW);
  }
  digitalWrite(regpin[segmentReihe].latch, HIGH);
  digitalWrite(regpin[segmentReihe].latch, LOW);
}
//
void displayTime()
{
  constexpr byte laenge {32};
  bool time[laenge] = {0};
  bool *timeSekundeEiner = &time[0];
  bool *timeSekundeZehner = &time[8];     // 2.tes Register
  bool *timeMinuteEiner = &time[16];      // 3.tes Register
  bool *timeMinuteZehner = &time[24];     // 4.tes Register
  memset(time, 0, laenge);
  memcpy(timeMinuteZehner, dec[zeit.minute / 10], 8);
  memcpy(timeMinuteEiner, dec[zeit.minute % 10], 8);
  memcpy(timeSekundeZehner, dec[zeit.sekunde / 10], 8);
  memcpy(timeSekundeEiner, dec[zeit.sekunde % 10], 8);
  printSegment(2, time, laenge);                        // 3.ter Kanal
}
//
void displaySpieler()
{
  /*
    1+2 Punktanzeige Spieler A (Anzeige 0-19)
    3+4 Satzanzeige Spieler A (Anzeige 0-9)( aber nur 3, 4 ist nicht in benutzung)
    5+6 Satzanzeige Spieler B (Anzeige 0-9)( aber nur 5, 6 ist nicht in benutzung)
    7+8 Punktanzeige Spieler B ( Anzeige 0-19)
  */
  constexpr byte laenge {64};
  bool spell[laenge] = {0};
  bool *spielerBzehner = &spell[8];
  bool *spielerBeiner = &spell[0];
  bool *satzBeiner = &spell[24];
  bool *satzAeiner = &spell[40];
  bool *spielerAzehner = &spell[56];
  bool *spielerAeiner = &spell[48];
  memset(spell, 0, laenge);
  memcpy(spielerAzehner, dec[spiel.spieler[0].points / 10], 8);
  memcpy(spielerAeiner, dec[spiel.spieler[0].points % 10], 8);
  memcpy(satzAeiner, dec[spiel.spieler[0].sets % 10], 8);
  memcpy(satzBeiner, dec[spiel.spieler[1].sets % 10], 8);
  memcpy(spielerBzehner, dec[spiel.spieler[1].points / 10], 8);
  memcpy(spielerBeiner, dec[spiel.spieler[1].points % 10], 8);
  printSegment(0, spell, laenge);
}
//
void bigDisplay()
{
  constexpr uint32_t printIntervall {500};
  static uint32_t lastPrint = 0;
  memset(bigDispRegOut, !segActiv, bigDispReg * 8);  // alle bits löschen
  bool *timerHunderter = &bigDispRegOut[0 * 8];      // Register 7 | DIGIT 0
  bool *timerZehner = &bigDispRegOut[1 * 8];         // Register 6 | DIGIT 1
  bool *timerEiner = &bigDispRegOut[2 * 8];          // Register 5 | DIGIT 2
  bool *spielerAsatz = &bigDispRegOut[3 * 8];
  bool *spielerAeiner = &bigDispRegOut[4 * 8];
  bool *spielerBsatz = &bigDispRegOut[5 * 8];
  bool *spielerBeiner = &bigDispRegOut[6 * 8];
  //
  memcpy(timerHunderter, dec[zeit.minute % 10], 8);
  memcpy(timerZehner, dec[zeit.sekunde / 10], 8);
  memcpy(timerEiner, dec[zeit.sekunde % 10], 8);
  memcpy(spielerAeiner, dec[spiel.spieler[0].points % 10], 8);
  memcpy(spielerAsatz, dec[spiel.spieler[0].sets % 10], 8);
  memcpy(spielerBeiner, dec[spiel.spieler[1].points % 10], 8);
  memcpy(spielerBsatz, dec[spiel.spieler[1].sets % 10], 8);
  bigDispRegOut[16] = spiel.spieler[1].points / 10 ? segActiv : !segActiv;  // spielerBZehner
  bigDispRegOut[24] = spiel.spieler[1].aufschlag   ? segActiv : !segActiv;  // spielerBAufschlag
  bigDispRegOut[32] = spiel.spieler[0].points / 10 ? segActiv : !segActiv;  // spielerAZehner
  bigDispRegOut[40] = spiel.spieler[0].aufschlag   ? segActiv : !segActiv;  // spielerAAufschlag
  for (byte b = 0; b < 56; b++)
  { bigDispRegOut[b] = !bigDispRegOut[b]; }
  if ((memcmp(bigDispRegOut, oldBigDisplay, bigDispReg * 8) != 0)  || // Neuer Inhalt ODER
      (millis() - printIntervall > lastPrint))                        // Zeit abgelaufen
  {
    for (byte b = 0; b < 56; b++)
    {
      Serial.print(bigDispRegOut[b]);
      if ((b + 1) % 8 == 0)
      { Serial.print(" | "); }
    }
    Serial.println();
    printSegment(bigDisp, bigDispRegOut, bigDispReg * 8);
    memcpy(oldBigDisplay, bigDispRegOut, bigDispReg * 8);
    lastPrint = millis();
  }
  //while (1);
};


void myTimer()
{
  constexpr uint32_t _oneSecond {1000};
  if (zeit.minute || zeit.sekunde)   // Solange Zeit aktiv
  {
    if (millis() - zeit.lastTik >= _oneSecond)
    {
      if (zeit.sekunde > 0)
      { zeit.sekunde--; }
      else if (zeit.minute > 0)
      {
        zeit.minute--;
        zeit.sekunde = 59;
      }
      zeit.lastTik += _oneSecond;
    }
  }
}

//
void segmentTest()
{
  for (byte b = 0; b < 10; b++)
  {
    for (byte s = 0; s < spielers; s++)
    {
      spiel.spieler[s].points = spiel.spieler[s].points ? 0 : 88;
      spiel.spieler[s].sets = spiel.spieler[s].sets ? 0 : 88;
      spiel.spieler[s].aufschlag = spiel.spieler[s].aufschlag ? 0 : 1;
    }
    zeit.minute = zeit.minute ? 0 : 88;
    zeit.sekunde = zeit.sekunde ? 0 : 88;
    displaySpieler();
    displayTime();
    bigDisplay();
    delay(500);
  }
}
//
void allNew()
{
  for (byte s = 0; s < spielers; s++)
  {
    spiel.spieler[s].points = 0;
    spiel.spieler[s].sets = 0;
    spiel.spieler[s].aufschlag = false;
  }
  spiel.isChange = false;
  zeit.minute = 0;
  zeit.sekunde = 0;
}

Neuer Code:

#include "tt.h"

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Tischtennis Display"));
  //display init
  for (byte b = 0; b < displays; b++)
  {
    pinMode(regpin[b].clock, OUTPUT);
    pinMode(regpin[b].data, OUTPUT);
    pinMode(regpin[b].latch, OUTPUT);
    digitalWrite(regpin[b].clock, LOW);
    digitalWrite(regpin[b].data, LOW);
    digitalWrite(regpin[b].latch, LOW);
  };
  pinMode(keypad.clock, OUTPUT);
  pinMode(keypad.data, INPUT);
  pinMode(keypad.latch, OUTPUT);
  digitalWrite(keypad.clock, LOW);
  digitalWrite(keypad.latch, LOW);
  segmentTest();
}

void loop()
{
  getKeys();        // Holt Tastenzustände
  checkKeys();      // wertet Tasten aus
  myTimer();        // CountDownTimer
  displaySpieler(); // kleine Anzeige
  bigDisplay();     // große Anzeige
  displayTime();    // countDownDisplay
}



/*
   Angabe

  Wird bei Start vergeben und danach alle 2 Punkte gewechselt....

  Jetzt die Besonderheiten....

  Ab Punktestand 10:10 wird nach jedem Punkt gewechselt

  Im 5ten Satz werden die Seiten gewechselt wenn jemmand 5 Punkte erreicht hat das heißt
  Angabe wechselt natürlich mit wie die Sätze und die Punkte auch

  Nach beendeten Satz wird die Angabe gewechselt(Reset Punkte), d.h. SpielerA hat Angabe gewonnen, im 2ten Satz fängt SpielerB mit Angabe an, im dritten wieder SpielerA usw

  So

  Ist da was von Realisierbar?
*/

void changeSpieler()     // Tauscht die Spieler auf dem Display
{
  if (spiel.isChange == true) // Ist bereits getauscht worden?
  { return; }                     // Dann gehts nicht weiter ;)
  SPIELER tempspieler{0, 0, 0};
  tempspieler.points = spiel.spieler[0].points;
  tempspieler.sets = spiel.spieler[0].sets;
  tempspieler.aufschlag = spiel.spieler[0].aufschlag;
  for (byte b = 0; b < spielers - 1; b++)
  {
    spiel.spieler[b].points = spiel.spieler[b + 1].points;
    spiel.spieler[b].sets = spiel.spieler[b + 1].sets;
    spiel.spieler[b].aufschlag = spiel.spieler[b + 1].aufschlag;
  }
  spiel.spieler[spielers - 1].points = tempspieler.points;
  spiel.spieler[spielers - 1].sets = tempspieler.sets;
  spiel.spieler[spielers - 1].aufschlag = tempspieler.aufschlag;
  spiel.isChange = true;
}
//
void positionChoice()
{
  uint8_t myValue = 0;
  for (byte b = 0; b < spielers; b++)      // addiere alle gespielten Sets
  {
    myValue += spiel.spieler[b].sets;
  }
  if (myValue == 5)                        // Wenn aktuell Wert erreicht
  {
    for (byte b = 0; b < spielers; b++)    // addiere alle Spielerpoints
    {
      if (spiel.spieler[b].points == 5)    //
      {
        changeSpieler();
        break;
      }
    }
  }
}
//
void checkKeys()
{
  for (byte b = 0; b < spielers; b++)
  {
    // aufschlag
    if (kt.sp[b].aufschlag != oldtable.sp[b].aufschlag)
    {
      if (kt.sp[b].aufschlag == gedrueckt)
      {
        newAufschlag(b);
      }
      oldtable.sp[b].aufschlag = kt.sp[b].aufschlag;
    }
    // satz Reset
    if (kt.sp[b].satzReset != oldtable.sp[b].satzReset)
    {
      if (kt.sp[b].satzReset == gedrueckt)
      {
        spiel.spieler[b].sets = 0;
        spiel.isChange = false;                                   // Wenn Zähler gelöscht wird, wird Merker gelöscht;
      }
      oldtable.sp[b].satzReset = kt.sp[b].satzReset;
    }
    // satz Minus
    if (kt.sp[b].satzMinus != oldtable.sp[b].satzMinus)        // Vergleich auf deaktiven Merker
    {
      if (kt.sp[b].satzMinus == gedrueckt)
      {
        if (spiel.spieler[b].sets > 0)                                  // Nur wenn was auf dem Zähler steht
        { spiel.spieler[b].sets--; }
      }
      oldtable.sp[b].satzMinus = kt.sp[b].satzMinus;            // Merker damit nicht weiter gezählt wird
    }
    // satz Plus
    if (kt.sp[b].satzPlus != oldtable.sp[b].satzPlus)           // mit Merker
    {
      if (kt.sp[b].satzPlus == gedrueckt)
      {
        spiel.spieler[b].sets++;
      }
      oldtable.sp[b].satzPlus = kt.sp[b].satzPlus;
    }
    // spiel Reset
    if (kt.sp[b].spielReset != oldtable.sp[b].spielReset)
    {
      if (kt.sp[b].spielReset == gedrueckt)
      {
        spiel.spieler[b].points = 0;
        spiel.isChange = false;                                   // Wenn Zähler gelöscht wird, wird Merker gelöscht;
      }
      oldtable.sp[b].spielReset = kt.sp[b].spielReset;
    }
    // spiel Minus
    if (kt.sp[b].spielMinus != oldtable.sp[b].spielMinus)        // mit Merker für Spielstand
    {
      if (spiel.spieler[b].points > 0 && kt.sp[b].spielMinus == gedrueckt)
      { spiel.spieler[b].points--; }
      oldtable.sp[b].spielMinus = kt.sp[b].spielMinus;
    }
    // spiel Plus
    if (kt.sp[b].spielPlus != oldtable.sp[b].spielPlus)           // Siehe Satz
    {
      if (kt.sp[b].spielPlus == gedrueckt)
      {
        spiel.spieler[b].points++;
        automatikAufschlag();
        positionChoice(); // spieler wechseln Position am Tisch bei Satz 5 und Spiel 5
      }
      oldtable.sp[b].spielPlus = kt.sp[b].spielPlus;
    }
  }
  // EinspielZeit
  if (kt.timeEinStart != oldtable.timeEinStart)                   // Tastenstatus geändert?
  {
    if (kt.timeEinStart == gedrueckt)                             //
    {
      zeit.minute = 2;                                            // Vorbelegung
      zeit.sekunde = 0;
      zeit.lastTik = millis();                                    // aktuelle Zeit für timer
    }
    oldtable.timeEinStart = kt.timeEinStart;                      // Tastenstatus merken
  }
  if (kt.timeEinStop != oldtable.timeEinStop)
  {
    if (kt.timeEinStop == gedrueckt)
    {
      zeit.minute = 0;
      zeit.sekunde = 0;;
    }
    oldtable.timeEinStop = kt.timeEinStop;
  }
  // TimeOutZeit
  if (kt.timeOutStart != oldtable.timeOutStart)
  {
    if (kt.timeOutStart == gedrueckt)
    {
      zeit.minute = 1;
      zeit.sekunde = 0;
      zeit.lastTik = millis();
    }
    oldtable.timeOutStart = kt.timeOutStart;
  }
  if (kt.timeOutStop != oldtable.timeOutStop)
  {
    if (kt.timeOutStop == gedrueckt)
    {
      zeit.minute = 0;
      zeit.sekunde = 0;;
    }
    oldtable.timeOutStop = kt.timeOutStop;
  }
  //
}

erster Angaben wechsel funktioniert, aber der Zweite setzt aus wieder der dritte wechsel geht der 4 wechsel setzt wieder aus

aeh...???
Wenn der Spielstand unter 10:10 ist wird doch blos alle 2 Spiele der Aufschlag gewechselt.
Danach mit jedem Spiel.

Irgendwas versteh ich jetzt nicht.

Ja richtig,
aktuel ist

  1. Punkt wechsel 4. Punkt kein wechsel 6. Punkt wechsel 8. Punkt kein wechsel 10. Punkt wechsel.....

weiß nicht wie ich es anders beschreiben soll

es wird ein Wechsel ausgelassen.
ab 10 wieder richtig

Hm... hat denn der erste Versuch oben funktioniert?

tausche mal tt.h

void bigDisplay();

/*
       0      |     7     |
    5     1   |  2     6  |
       6      |     1     |
    4     2   |  3     5  |
       3      |     4     |
*/

/*
  Digit0 Timer Hunderter  // Minute Einer
  Digit1 Timer zehner     // Sekunde Zehner
  Digit2 Timer einer      // Sekunde Einer
  Digit3 SpielerA Satz + Aufschlag D40
  Digit4 SpielerA Punkte einer + Zehner D32
  Digit5 SpielerB Satz + Aufschlag D24
  Digit6 SpielerB Punkte einer + Zehner D16
*/
constexpr bool segActiv {0};  // Die Segmente leuchten, wenn RegisterPin => 0!!!
constexpr uint8_t bigDisp {1};                        // -> Nummer des Registerzweig aus displays
constexpr uint8_t bigDispReg {7};                     // -> Anzahl der Register auf dem Zweig
bool bigDispRegOut[bigDispReg * 8] = {!segActiv};     // -> Summe aller bits auf dem Zweig
bool oldBigDisplay[bigDispReg * 8] = {!segActiv};     // Merker für Aktuallisierung



constexpr byte displays {3};
struct REGPIN
{
  uint8_t clock;
  uint8_t data;
  uint8_t latch;
};
REGPIN regpin[displays]
{{5, 7, 6}, {9, 11, 10}, {12, 14, 13}};

boolean dec[][8] =
{
  {1, 1, 0, 0, 0, 0, 0, 0}, // 0
  {1, 1, 1, 1, 1, 0, 0, 1}, // 1
  {1, 0, 1, 0, 0, 1, 0, 0}, // 2
  {1, 0, 1, 1, 0, 0, 0, 0}, // 3
  {1, 0, 0, 1, 1, 0, 0, 1}, // 4
  {1, 0, 0, 1, 0, 0, 1, 0}, // 5
  {1, 0, 0, 0, 0, 0, 1, 0}, // 6
  {1, 1, 1, 1, 1, 0, 0, 0}, // 7
  {1, 0, 0, 0, 0, 0, 0, 0}, // 8
  {1, 0, 0, 1, 0, 0, 0, 0}, // 9
  {1, 1, 1, 1, 1, 1, 1, 1}, // ALL OFF
};

constexpr uint8_t spielers {2};
struct SPIELER
{
  uint8_t points;
  uint8_t sets;
  bool aufschlag;
};
struct SPIEL
{
  SPIELER spieler[spielers];
  bool isChange;
} spiel; //{{0, 0, 0}, {0, 0, 0}, 0};

REGPIN keypad
{16, 17, 15};

struct SCOREPAD
{
  bool aufschlag;   // 21 # 22
  bool spielPlus;   //  6 # 14
  bool spielMinus;  //  5 # 13
  bool spielReset;  //  4 # 12
  bool satzPlus;    //  3 # 11
  bool satzMinus;   //  2 # 10
  bool satzReset;   //  1 #  9
};

struct KEYTABLE
{
  bool timeOutStart;   // 20 Auszeit
  bool timeOutStop;    // 19
  bool timeEinStart;   // 18 Einspielzeit
  bool timeEinStop;    // 17
  SCOREPAD sp[spielers]; // Spielertasten
};

constexpr bool gedrueckt {HIGH};

KEYTABLE kt;
KEYTABLE oldtable; // Hilfstabelle - hauptsächlich für das merken der Tastendrücke / kommt noch weg

struct ZEIT
{
  /*
    1+2 Minutenanzeige ( Anzeige 1 oder 2)( aber nur 2, 1 ist nicht in benutzung)
    3 Sekundenanzeige zehner ( Anzeige 0-5)
    4 Sekundenanzeige einer ( Anzeige 0-9)
  */
  uint8_t minute = 0;
  uint8_t sekunde = 0;
  uint32_t lastTik = 0; // Merker, wann ausgelöst
} zeit;


//
void newAufschlag(const byte x)
{
  for (byte a = 0; a < spielers; a++)
  {spiel.spieler[a].aufschlag = false;}                      // löscht alles
  spiel.spieler[x].aufschlag = true;                         // setzt aktuellen
}
//
void nextAufschlag()
{
  byte myAufschlag = 0;
  for (byte b = 0; b < spielers; b++)                      // gehe durch alle Spieler
  {
    if (spiel.spieler[b].aufschlag)                        // Spieler hat aufschlag ?
    {
      // nächsten Spieler setzen
      // Wert ist größer als Spieler, dann erster Spieler
      myAufschlag = b + 1;
      if (myAufschlag >= spielers)
      { myAufschlag = 0; }
    }
  }
  newAufschlag(myAufschlag);                              // Aufschlag setzen
}
//
void automatikAufschlag()                                 // wird aufgerufen, wenn points-Taste gedrückt wurde
{
  if (spiel.spieler[0].points + spiel.spieler[0].points < 20)         // summe points für jedes 2tes Spiel
  {
    if ((spiel.spieler[0].points + spiel.spieler[1].points) % 2 == 0) // jedes zweite Spiel auswerten
    {
      nextAufschlag();                                    //
    }
  }
  else                                                     // Jedes Spiel
  {
    nextAufschlag();
  }
}

//
void getKeys()                                    // Funktion aus dem Ursprungspost übernommen und erweitert
{
  // Das sind Schieberegister n:1
  // Auch wenn nicht alle Pins gebraucht werden, müssen ungenutzte mitgelesen werden!
  constexpr uint8_t zaehler {24};                 // Anzahl Chips * 8 Pin
  constexpr uint32_t interval {50};               // "debounce Time"
  static uint32_t lastRead = 0;                   // Merker
  if (millis() - lastRead > interval)
  {
    digitalWrite(keypad.latch, LOW);              // Chip  aktivieren
    delay(1);
    digitalWrite(keypad.latch, HIGH);
    bool readIn = true;
    for (uint8_t b = 0; b < zaehler; b++)           // Eingänge durchzählen
    {
      readIn = digitalRead(keypad.data);
      switch (b)                                    // Zuweisung Pin => Variable
      {
//*INDENT-OFF*  
      case  1: kt.sp[0].satzReset   = readIn; break;  // bit 1 aus Schieberegister 
      case  2: kt.sp[0].satzMinus   = readIn; break;  // bit 2
      case  3: kt.sp[0].satzPlus    = readIn; break;  // bit 3
      case  4: kt.sp[0].spielReset  = readIn; break;
      case  5: kt.sp[0].spielMinus  = readIn; break;
      case  6: kt.sp[0].spielPlus   = readIn; break;
      case  9: kt.sp[1].satzReset   = readIn; break;
      case 10: kt.sp[1].satzMinus   = readIn; break;
      case 11: kt.sp[1].satzPlus    = readIn; break;
      case 12: kt.sp[1].spielReset  = readIn; break;
      case 13: kt.sp[1].spielMinus  = readIn; break;
      case 14: kt.sp[1].spielPlus   = readIn; break;
      case 17: kt.timeEinStop       = readIn; break; 
      case 18: kt.timeEinStart      = readIn; break;
      case 19: kt.timeOutStop       = readIn; break; 
      case 20: kt.timeOutStart      = readIn; break; 
      case 21: kt.sp[0].aufschlag   = readIn; break; 
      case 22: kt.sp[1].aufschlag   = readIn; break;
//*INDENT-ON*  
      }
      digitalWrite(keypad.clock, HIGH);
      digitalWrite(keypad.clock, LOW);
    }
    lastRead = millis();
  }
}
//
void printSegment(const byte &segmentReihe, const bool *array, const byte &laenge)
{
  for (uint8_t b = 0; b < laenge; b++)
  {
    digitalWrite(regpin[segmentReihe].data, array[b]);    // Array auf dem Schieberegister
    digitalWrite(regpin[segmentReihe].clock, HIGH);
    digitalWrite(regpin[segmentReihe].clock, LOW);
    digitalWrite(regpin[segmentReihe].data, LOW);
  }
  digitalWrite(regpin[segmentReihe].latch, HIGH);
  digitalWrite(regpin[segmentReihe].latch, LOW);
}
//
void displayTime()
{
  constexpr byte laenge {32};
  bool time[laenge] = {0};
  bool *timeSekundeEiner = &time[0];
  bool *timeSekundeZehner = &time[8];     // 2.tes Register
  bool *timeMinuteEiner = &time[16];      // 3.tes Register
  bool *timeMinuteZehner = &time[24];     // 4.tes Register
  memset(time, 0, laenge);
  memcpy(timeMinuteZehner, dec[zeit.minute / 10], 8);
  memcpy(timeMinuteEiner, dec[zeit.minute % 10], 8);
  memcpy(timeSekundeZehner, dec[zeit.sekunde / 10], 8);
  memcpy(timeSekundeEiner, dec[zeit.sekunde % 10], 8);
  printSegment(2, time, laenge);                        // 3.ter Kanal
}
//
void displaySpieler()
{
  /*
    1+2 Punktanzeige Spieler A (Anzeige 0-19)
    3+4 Satzanzeige Spieler A (Anzeige 0-9)( aber nur 3, 4 ist nicht in benutzung)
    5+6 Satzanzeige Spieler B (Anzeige 0-9)( aber nur 5, 6 ist nicht in benutzung)
    7+8 Punktanzeige Spieler B ( Anzeige 0-19)
  */
  constexpr byte laenge {64};
  bool spell[laenge] = {0};
  bool *spielerBzehner = &spell[8];
  bool *spielerBeiner = &spell[0];
  bool *satzBeiner = &spell[24];
  bool *satzAeiner = &spell[40];
  bool *spielerAzehner = &spell[56];
  bool *spielerAeiner = &spell[48];
  memset(spell, 0, laenge);
  memcpy(spielerAzehner, dec[spiel.spieler[0].points / 10], 8);
  memcpy(spielerAeiner, dec[spiel.spieler[0].points % 10], 8);
  memcpy(satzAeiner, dec[spiel.spieler[0].sets % 10], 8);
  memcpy(satzBeiner, dec[spiel.spieler[1].sets % 10], 8);
  memcpy(spielerBzehner, dec[spiel.spieler[1].points / 10], 8);
  memcpy(spielerBeiner, dec[spiel.spieler[1].points % 10], 8);
  printSegment(0, spell, laenge);
}
//
void bigDisplay()
{
  constexpr uint32_t printIntervall {500};
  static uint32_t lastPrint = 0;
  memset(bigDispRegOut, !segActiv, bigDispReg * 8);  // alle bits löschen
  bool *timerHunderter = &bigDispRegOut[0 * 8];      // Register 7 | DIGIT 0
  bool *timerZehner = &bigDispRegOut[1 * 8];         // Register 6 | DIGIT 1
  bool *timerEiner = &bigDispRegOut[2 * 8];          // Register 5 | DIGIT 2
  bool *spielerAsatz = &bigDispRegOut[3 * 8];
  bool *spielerAeiner = &bigDispRegOut[4 * 8];
  bool *spielerBsatz = &bigDispRegOut[5 * 8];
  bool *spielerBeiner = &bigDispRegOut[6 * 8];
  //
  memcpy(timerHunderter, dec[zeit.minute % 10], 8);
  memcpy(timerZehner, dec[zeit.sekunde / 10], 8);
  memcpy(timerEiner, dec[zeit.sekunde % 10], 8);
  memcpy(spielerAeiner, dec[spiel.spieler[0].points % 10], 8);
  memcpy(spielerAsatz, dec[spiel.spieler[0].sets % 10], 8);
  memcpy(spielerBeiner, dec[spiel.spieler[1].points % 10], 8);
  memcpy(spielerBsatz, dec[spiel.spieler[1].sets % 10], 8);
  bigDispRegOut[16] = spiel.spieler[1].points / 10 ? segActiv : !segActiv;  // spielerBZehner
  bigDispRegOut[24] = spiel.spieler[1].aufschlag   ? segActiv : !segActiv;  // spielerBAufschlag
  bigDispRegOut[32] = spiel.spieler[0].points / 10 ? segActiv : !segActiv;  // spielerAZehner
  bigDispRegOut[40] = spiel.spieler[0].aufschlag   ? segActiv : !segActiv;  // spielerAAufschlag
  for (byte b = 0; b < 56; b++)
  { bigDispRegOut[b] = !bigDispRegOut[b]; }
  if ((memcmp(bigDispRegOut, oldBigDisplay, bigDispReg * 8) != 0)  || // Neuer Inhalt ODER
      (millis() - printIntervall > lastPrint))                        // Zeit abgelaufen
  {
    for (byte b = 0; b < 56; b++)
    {
      Serial.print(bigDispRegOut[b]);
      if ((b + 1) % 8 == 0)
      { Serial.print(" | "); }
    }
    Serial.println();
    printSegment(bigDisp, bigDispRegOut, bigDispReg * 8);
    memcpy(oldBigDisplay, bigDispRegOut, bigDispReg * 8);
    lastPrint = millis();
  }
  //while (1);
};


void myTimer()
{
  constexpr uint32_t _oneSecond {1000};
  if (zeit.minute || zeit.sekunde)   // Solange Zeit aktiv
  {
    if (millis() - zeit.lastTik >= _oneSecond)
    {
      if (zeit.sekunde > 0)
      { zeit.sekunde--; }
      else if (zeit.minute > 0)
      {
        zeit.minute--;
        zeit.sekunde = 59;
      }
      zeit.lastTik += _oneSecond;
    }
  }
}

//
void segmentTest()
{
  for (byte b = 0; b < 10; b++)
  {
    for (byte s = 0; s < spielers; s++)
    {
      spiel.spieler[s].points = spiel.spieler[s].points ? 0 : 88;
      spiel.spieler[s].sets = spiel.spieler[s].sets ? 0 : 88;
      spiel.spieler[s].aufschlag = spiel.spieler[s].aufschlag ? 0 : 1;
    }
    zeit.minute = zeit.minute ? 0 : 88;
    zeit.sekunde = zeit.sekunde ? 0 : 88;
    displaySpieler();
    displayTime();
    bigDisplay();
    delay(500);
  }
}
//
void allNew()
{
  for (byte s = 0; s < spielers; s++)
  {
    spiel.spieler[s].points = 0;
    spiel.spieler[s].sets = 0;
    spiel.spieler[s].aufschlag = false;
  }
  spiel.isChange = false;
  zeit.minute = 0;
  zeit.sekunde = 0;
}

Angaben Wechsel funktioniert wieder.
Aber nicht die funktion im 5ten Satz(Seitenwechsel)

Tausch mal und sag mal, was der SerMon ausgibt.
Der muss áb dem 5ten Satz sich melden

#include "tt.h"

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Tischtennis Display"));
  //display init
  for (byte b = 0; b < displays; b++)
  {
    pinMode(regpin[b].clock, OUTPUT);
    pinMode(regpin[b].data, OUTPUT);
    pinMode(regpin[b].latch, OUTPUT);
    digitalWrite(regpin[b].clock, LOW);
    digitalWrite(regpin[b].data, LOW);
    digitalWrite(regpin[b].latch, LOW);
  };
  pinMode(keypad.clock, OUTPUT);
  pinMode(keypad.data, INPUT);
  pinMode(keypad.latch, OUTPUT);
  digitalWrite(keypad.clock, LOW);
  digitalWrite(keypad.latch, LOW);
  segmentTest();
}

void loop()
{
  getKeys();        // Holt Tastenzustände
  checkKeys();      // wertet Tasten aus
  myTimer();        // CountDownTimer
  displaySpieler(); // kleine Anzeige
  bigDisplay();     // große Anzeige
  displayTime();    // countDownDisplay
}



/*
   Angabe

  Wird bei Start vergeben und danach alle 2 Punkte gewechselt....

  Jetzt die Besonderheiten....

  Ab Punktestand 10:10 wird nach jedem Punkt gewechselt

  Im 5ten Satz werden die Seiten gewechselt wenn jemmand 5 Punkte erreicht hat das heißt
  Angabe wechselt natürlich mit wie die Sätze und die Punkte auch

  Nach beendeten Satz wird die Angabe gewechselt(Reset Punkte), d.h. SpielerA hat Angabe gewonnen, im 2ten Satz fängt SpielerB mit Angabe an, im dritten wieder SpielerA usw

  So

  Ist da was von Realisierbar?
*/

void changeSpieler()     // Tauscht die Spieler auf dem Display
{
  if (spiel.isChange == true) // Ist bereits getauscht worden?
  { 
    Serial.println(F("Wurde bereits getauscht..."));
    return; }                     // Dann gehts nicht weiter ;)
  SPIELER tempspieler{0, 0, 0};
  tempspieler.points = spiel.spieler[0].points;
  tempspieler.sets = spiel.spieler[0].sets;
  tempspieler.aufschlag = spiel.spieler[0].aufschlag;
  for (byte b = 0; b < spielers - 1; b++)
  {
    Serial.print(F("Fülle "));
    Serial.print(b);
    Serial.print(F(" mit "));
    Serial.println(b+1);
    spiel.spieler[b].points = spiel.spieler[b + 1].points;
    spiel.spieler[b].sets = spiel.spieler[b + 1].sets;
    spiel.spieler[b].aufschlag = spiel.spieler[b + 1].aufschlag;
  }
  spiel.spieler[spielers - 1].points = tempspieler.points;
  spiel.spieler[spielers - 1].sets = tempspieler.sets;
  spiel.spieler[spielers - 1].aufschlag = tempspieler.aufschlag;
  spiel.isChange = true;
}
//
void positionChoice()
{
  uint8_t myValue = 0;
  for (byte b = 0; b < spielers; b++)      // addiere alle gespielten Sets
  {
    myValue += spiel.spieler[b].sets;
  }
  if (myValue == 5)                        // Wenn aktuell Wert erreicht
  {
    Serial.println(F("Spielsatz 5 erkannt"));
    for (byte b = 0; b < spielers; b++)    // addiere alle Spielerpoints
    {
      if (spiel.spieler[b].points == 5)    //
      {
        Serial.println(F("Spielpunkt 5 erkannt"));
        changeSpieler();
        break;
      }
    }
  }
}
//
void checkKeys()
{
  for (byte b = 0; b < spielers; b++)
  {
    // aufschlag
    if (kt.sp[b].aufschlag != oldtable.sp[b].aufschlag)
    {
      if (kt.sp[b].aufschlag == gedrueckt)
      {
        newAufschlag(b);
      }
      oldtable.sp[b].aufschlag = kt.sp[b].aufschlag;
    }
    // satz Reset
    if (kt.sp[b].satzReset != oldtable.sp[b].satzReset)
    {
      if (kt.sp[b].satzReset == gedrueckt)
      {
        spiel.spieler[b].sets = 0;
        spiel.isChange = false;                                   // Wenn Zähler gelöscht wird, wird Merker gelöscht;
      }
      oldtable.sp[b].satzReset = kt.sp[b].satzReset;
    }
    // satz Minus
    if (kt.sp[b].satzMinus != oldtable.sp[b].satzMinus)        // Vergleich auf deaktiven Merker
    {
      if (kt.sp[b].satzMinus == gedrueckt)
      {
        if (spiel.spieler[b].sets > 0)                                  // Nur wenn was auf dem Zähler steht
        { spiel.spieler[b].sets--; }
      }
      oldtable.sp[b].satzMinus = kt.sp[b].satzMinus;            // Merker damit nicht weiter gezählt wird
    }
    // satz Plus
    if (kt.sp[b].satzPlus != oldtable.sp[b].satzPlus)           // mit Merker
    {
      if (kt.sp[b].satzPlus == gedrueckt)
      {
        spiel.spieler[b].sets++;
      }
      oldtable.sp[b].satzPlus = kt.sp[b].satzPlus;
    }
    // spiel Reset
    if (kt.sp[b].spielReset != oldtable.sp[b].spielReset)
    {
      if (kt.sp[b].spielReset == gedrueckt)
      {
        spiel.spieler[b].points = 0;
        spiel.isChange = false;                                   // Wenn Zähler gelöscht wird, wird Merker gelöscht;
      }
      oldtable.sp[b].spielReset = kt.sp[b].spielReset;
    }
    // spiel Minus
    if (kt.sp[b].spielMinus != oldtable.sp[b].spielMinus)        // mit Merker für Spielstand
    {
      if (spiel.spieler[b].points > 0 && kt.sp[b].spielMinus == gedrueckt)
      { spiel.spieler[b].points--; }
      oldtable.sp[b].spielMinus = kt.sp[b].spielMinus;
    }
    // spiel Plus
    if (kt.sp[b].spielPlus != oldtable.sp[b].spielPlus)           // Siehe Satz
    {
      if (kt.sp[b].spielPlus == gedrueckt)
      {
        spiel.spieler[b].points++;
        automatikAufschlag();
        positionChoice(); // spieler wechseln Position am Tisch bei Satz 5 und Spiel 5
      }
      oldtable.sp[b].spielPlus = kt.sp[b].spielPlus;
    }
  }
  // EinspielZeit
  if (kt.timeEinStart != oldtable.timeEinStart)                   // Tastenstatus geändert?
  {
    if (kt.timeEinStart == gedrueckt)                             //
    {
      zeit.minute = 2;                                            // Vorbelegung
      zeit.sekunde = 0;
      zeit.lastTik = millis();                                    // aktuelle Zeit für timer
    }
    oldtable.timeEinStart = kt.timeEinStart;                      // Tastenstatus merken
  }
  if (kt.timeEinStop != oldtable.timeEinStop)
  {
    if (kt.timeEinStop == gedrueckt)
    {
      zeit.minute = 0;
      zeit.sekunde = 0;;
    }
    oldtable.timeEinStop = kt.timeEinStop;
  }
  // TimeOutZeit
  if (kt.timeOutStart != oldtable.timeOutStart)
  {
    if (kt.timeOutStart == gedrueckt)
    {
      zeit.minute = 1;
      zeit.sekunde = 0;
      zeit.lastTik = millis();
    }
    oldtable.timeOutStart = kt.timeOutStart;
  }
  if (kt.timeOutStop != oldtable.timeOutStop)
  {
    if (kt.timeOutStop == gedrueckt)
    {
      zeit.minute = 0;
      zeit.sekunde = 0;;
    }
    oldtable.timeOutStop = kt.timeOutStop;
  }
  //
}

Ein gesetzter Tausch geht momentan nur mit einem satz-Reset zu löschen.

Tauschen tut er nicht
Die Sätze brauchen ja auch nicht getausche werden, da ja 2:2 steht!

Es müssen ja "nur" die Punkte und die Angabe getauscht werden.
Sorry mein Fehler hab zu kompliziert gedacht.