Von mir aus.
Oder nimmst das Komplettpaket - wir konzentrieren uns jetzt nur noch auf das Display.
tt.h
void bigDisplay();
void checkSpielerKeys();
void displaySpieler();
bool getSite(const bool site);
/*
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 serve;
};
struct SPIEL
{
SPIELER spieler[spielers];
bool serveIsChange; // Merker ob bei Satz 5 Spiel 5 gewechselt wurde
uint8_t serveStart; // Merker welcher Spieler 1. Aufschlag hat
} spiel; //{{0, 0, 0}, {0, 0, 0}, 0, 0};
constexpr byte A = 0;
constexpr byte B = 1;
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 für das merken der Tastendrücke
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 setAufschlag(const byte seite)
{
for (byte a = 0; a < spielers; a++)
{spiel.spieler[a].serve = false;} // löscht alles
spiel.spieler[seite].serve = true; // setzt aktuellen Aufschlag
}
//
void nextAufschlag() // Nächsten Aufschlag errechnen
{
byte myAufschlag = 0;
for (byte b = 0; b < spielers; b++) // gehe durch alle Spieler
{
if (spiel.spieler[b].serve) // Spieler hat aufschlag ?
{
// nächsten Spieler setzen
// Wert ist größer als Spieler, dann erster Spieler
myAufschlag = b + 1;
if (myAufschlag >= spielers)
{ myAufschlag = 0; }
}
}
setAufschlag(myAufschlag); // Aufschlag setzen
}
//
void newSet(const byte site)
{
spiel.spieler[site].sets++; // Satz gewonnen
for (byte b = 0; b < spielers; b++)
{ spiel.spieler[b].points = 0; } // Punkte-Reset
setAufschlag(spiel.serveStart); // Aufschlag setzen
}
//
void automatikAufschlag() // wird aufgerufen, wenn points-Taste gedrückt wurde
{
uint8_t allPoints = spiel.spieler[0].points + spiel.spieler[1].points;
if (allPoints < 20) // summe points für jedes 2tes Spiel
{
Serial.print(spiel.spieler[0].points);
Serial.println(spiel.spieler[1].points);
if (allPoints % 2 == 0) // jedes zweite Spiel auswerten
{ nextAufschlag(); }
}
else // Jedes Spiel
{
Serial.print(spiel.spieler[0].points);
Serial.println(spiel.spieler[1].points);
nextAufschlag();
}
}
//
bool getSite(const bool site)
{
byte allSet = 0;
for (byte b = 0; b < spielers; b++)
{ allSet += spiel.spieler[b].sets; }
if (allSet != 4) // Solange nicht 4 Säze vollständig gespielt wurden normaler Seitenwechsel
{
return allSet % 2 == 0 ? site : !site;
}
// Seitenwechsel bei Satz 5 bis Spiel 5 normal - dann Seitenwechsel!
for (byte b = 0; b < spielers; b++) // durchlaufe alle Spielerpoints
{
if (spiel.spieler[b].points >= 5) // Einer hat die Grenze erreicht?
{
return !site;
}
}
return site;
}
//
bool checkSetEnd() // Prüft ob Satz zu Ende ist
{
bool isEnd = false;
constexpr uint8_t setPoint {11}; // Satzende in Punkte
constexpr uint8_t winPoint {2}; // Unterschied Spielerpunkte zum Gewinn
//
uint8_t allPoints = 0;
for (byte b = 0; b < spielers; b++) // addiere alle Spielerpunkte als Zwischenvariable
{ allPoints += spiel.spieler[b].points; }
//
for (byte b = 0; b < spielers; b++) // Frage Spieler auf Siegpunkt ab
{
if (spiel.spieler[b].points >= setPoint) // Mindestpunktzahl zum Gewinn erreicht?
{
if ((spiel.spieler[b].points - winPoint) >= (allPoints - spiel.spieler[b].points)) // Unterschied passt?
{
newSet(b); // Satzpunkt setzen und Spielpunkte löschen
isEnd = true;
}
}
}
return isEnd;
}
//
//
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[getSite(A)].satzReset = readIn; break; // bit 1 aus Schieberegister
case 2: kt.sp[getSite(A)].satzMinus = readIn; break; // bit 2
case 3: kt.sp[getSite(A)].satzPlus = readIn; break; // bit 3
case 4: kt.sp[getSite(A)].spielReset = readIn; break;
case 5: kt.sp[getSite(A)].spielMinus = readIn; break;
case 6: kt.sp[getSite(A)].spielPlus = readIn; break;
case 9: kt.sp[getSite(B)].satzReset = readIn; break;
case 10: kt.sp[getSite(B)].satzMinus = readIn; break;
case 11: kt.sp[getSite(B)].satzPlus = readIn; break;
case 12: kt.sp[getSite(B)].spielReset = readIn; break;
case 13: kt.sp[getSite(B)].spielMinus = readIn; break;
case 14: kt.sp[getSite(B)].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[getSite(A)].aufschlag = readIn; break;
case 22: kt.sp[getSite(B)].aufschlag = readIn; break;
//*INDENT-ON*
}
digitalWrite(keypad.clock, HIGH);
digitalWrite(keypad.clock, LOW);
}
lastRead = millis();
}
}
//
void checkKeys()
{
checkSpielerKeys(); // Spielertasten ausgelagert
// 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;
}
//
}
//
void checkSpielerKeys()
{
for (byte b = 0; b < spielers; b++)
{
// aufschlag
if (kt.sp[b].aufschlag != oldtable.sp[b].aufschlag)
{
if (kt.sp[b].aufschlag == !gedrueckt)
{ setAufschlag(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.serveIsChange = 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.serveIsChange = 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)
{
if (spiel.spieler[0].points + spiel.spieler[1].points == 0) // Noch kein Punkt gespielt?
{
for (byte c = 0; c < spielers; c++) // Frage alle Aufschläge ab
if (spiel.spieler[c].serve) // Spieler hat ersten Aufschlag
{ spiel.serveStart = c; } // Merke für den Satz den ersten Aufschlag
}
spiel.spieler[b].points++; //
automatikAufschlag();
checkSetEnd(); // Wenn Satz zu Ende wird Zählerstände gesetzt
}
oldtable.sp[b].spielPlus = kt.sp[b].spielPlus;
}
}
}
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};
byte allSet = 0;
for (byte b = 0; b < spielers; b++)
{ allSet += spiel.spieler[b].sets; }
bool spell[laenge] = {0};
bool *spielerBeiner = &spell[0];
bool *spielerBzehner = &spell[8];
bool *satzBeiner = &spell[24];
bool *satzAeiner = &spell[40];
bool *spielerAeiner = &spell[48];
bool *spielerAzehner = &spell[56];
memset(spell, 0, laenge);
// Das ist tricky:
// die Ausgabe der Spielerdaten ist abhängig von der Anzahl gespielter Sätze
// Mit jedem gespieltem Satz wechselt der Spieler die Seite
// Und bei Satz 5 nochmal mitten im Spiel... getSite() soll das ermitteln
memcpy(spielerAzehner, dec[spiel.spieler[getSite(A)].points / 10], 8);
memcpy(spielerAeiner, dec[spiel.spieler[getSite(A)].points % 10], 8);
memcpy(satzAeiner, dec[spiel.spieler[getSite(A)].sets % 10], 8);
memcpy(satzBeiner, dec[spiel.spieler[getSite(B)].sets % 10], 8);
memcpy(spielerBzehner, dec[spiel.spieler[getSite(B)].points / 10], 8);
memcpy(spielerBeiner, dec[spiel.spieler[getSite(B)].points % 10], 8);
printSegment(0, spell, laenge);
}
//
//
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].serve = spiel.spieler[s].serve ? 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].serve = false;
}
spiel.serveIsChange = false;
spiel.serveStart = 0;
zeit.minute = 0;
zeit.sekunde = 0;
}
#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 wird noch umgebaut - dann erst neu coden
bigDisplay(); // große Anzeige
displayTime(); // countDownDisplay
}
//
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].serve ? segActiv : !segActiv; // spielerBAufschlag
bigDispRegOut[32] = spiel.spieler[0].points / 10 ? segActiv : !segActiv; // spielerAZehner
bigDispRegOut[40] = spiel.spieler[0].serve ? 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();
}
};
//