Sonntag, 26. Juni 2016

4 Gewinnt auf LED-Matrix mit Raspberry Pi


Was ist schöner als Spiele entwickeln?

Im Zuge einer Studienarbeit habe ich mit meinem Freund Christian ein 4-Gewinnt entwickelt. Es läuft auf Raspberry Pi mit einem selbst gebauten Hardwaremodul auf Lochrasterplatine.

Der Raspberry Pi wird mit Raspbian betrieben und der Quellcode ist in Python geschrieben. Auf dem Hardwaremodul sind eine 7x6 LED-Matrix und 3 Taster zum steuern des Spiels aufgebaut. In den folgenden Kapiteln erkläre ich das Projekt etwas genauer.

Falls ihr jedoch einfach nur schnell loslegen wollt:
Schaltplan, Quellcode und die offizielle Doku findet ihr in den Projektdaten bei den folgenden Hostern:

Dropbox                                                          Github

Schreibt in die Kommentare falls ihr Fragen oder Anregungen habt, ich freu mich auf euer Feedback - Nun viel Spaß beim selber machen!

Hans

1. Hardware


1.1 Teile der Platine

Schauen wir erstmal kurz das Hardwaremodul an.


        1. Schieberegister (74HC595)
        2. High-Side-Treiber (UDN2981)
        3. Low-Side-Treiber (ULN2803)
        4. Vorwiderstände der LEDs
        5. Duo-LEDs (grün-rot)
        6. Taster (Left, Enter, Right)
        7. GPIO-Schnittstelle

1.2 LED-Matrix

Die LED-Matrix besteht aus 7x6 Duo-LEDs mit gemeinsamer Kathode. Sie haben die Farben Rot und Grün. Die Matrix wird durch zwei 8-Bit High-Side-Treiber an den 14 Anoden und einem 8-Bit Low-Side-Treiber an den 6 Kathoden mit Strom versorgt. Die Ansteuerung der Matrix übernehmen drei in Serie geschaltete 8-Bit Schieberegister.

Die folgende Abbildung zeigt den Aufbau und die Ansteuerung der LED-Matrix. Dabei beginnt der Datenvektor stets an der oberen rechten Seite der Matrix und zieht sich durch die Spalten von rechts nach links und anschließend durch die Zeilen von oben nach unten bis zum unteren linken Ende der Matrix.



Um für eine richtige Anzeige zu sorgen, muss der Datenvektor korrekt zusammengesetzt sein. Die Vektorpositionen 0, 1, 16, 17 sind nicht beschaltet. Welche Werte hier eingetragen werden ist grundsätzlich irrelevant. Bei den Positionen 2–15 handelt es sich um die Matrix-Spalten. Dabei sprechen die geraden Zahlen die grüne Anode der jeweiligen LED an, während die ungeraden Zahlen die rote Anode der jeweiligen LED ansprechen. Die Positionen 18–23 entsprechen den Zeilen, hier werden die Kathoden der jeweiligen LEDs angesprochen.

Um eine LED in der Matrix anzusteuern, wird sowohl in den Spalten, als auch in den Zeilen mit High-Pegel adressiert. Das bedeutet, wenn man beispielsweise die LED in der 5. Spalte und der 3. Reihe mit grün ansprechen möchte, müssen an den Datenpositionen 6 und 20 High-Pegel anliegen. Alle anderen Datenpositionen müssten Low-Pegel halten. In diesem Fall würde nur die ausgewählte LED grün leuchten und der Rest der Matrix wäre dunkel. (Datenvektor: ← 00010000 00000000 01000000 ←)

Die Schieberegister beziehen ihre Daten hauptsächlich von der Datenleitung SER und den zwei Taktleitungen SCK und RCK. Des Weiteren existieren die VektorlöschenLeitung SCL und die Freigabeleitung G. Diese 5 Datenleitungen sind mit dem Raspberry Pi verbunden und für die Steuerung der LED-Matrix verantwortlich. Weitere Informationen zu den verschiedenen Steuerleitungen sind in Tabelle 1 zusammengefasst.


1.3 Taster

Die Taster sind unterhalb der LED-Matrix angeordnet. Mit ihnen wird das Spiel gesteuert. Dabei gibt es zwei Taster, um den Coin nach rechts(Right) und links(Left) zu bewegen und einen Taster um den Coin fallen zu lassen(Enter). Der Enter-Taster hat eine Zusatzfunktion. Hält man ihn für längere Zeit gedrückt, so wird ein Reset ausgeführt und das Spiel neu gestartet.

Jeder Taster ist durch eine kleine Schaltung Hardware-Entprellt und führt eine eigene Datenleitung zum Raspberry Pi. Allerdings ist in der Software noch ein Algorithmus zum Entprellen der Taster vorhanden. Dies ist vorangegangen Entwicklungsphasen geschuldet und völlig unkritisch. In einer späteren Entwicklungsstufe sollte dieser Programmabschnitt angepasst und optimiert werden.

1.4 GPIO-Schnittstelle

An der GPIO-Schnittstelle werden alle Ein- und Ausgänge sowie die Betriebsspannung der Platine in einem Sockel zusammengefasst. An dieser Stelle muss der Raspberry Pi angeschlossen werden. Tabelle 1 gibt eine detaillierte Übersicht über alle Pins und zeigt, wie das Board mit dem Raspberry Pi zu verbinden ist.

1.5 Schaltplan

 

1.6 Board


2 Software

Die Software zur Ansteuerung des 4-Gewinnt-Spiels wurde für Raspberry Pi auf dem Betriebssystem Raspian geschrieben. Die dazu verwendete Programmiersprache ist Python. Da Python eine Interpretersprache ist, besteht keine Notwendigkeit, den Quellcode zu kompilieren, er wird direkt im Betriebssystem interpretiert.

Die gesamte entwickelte Software des 4-Gewinnt Spiels befindet sich in der Python-Datei VierGewinnt v1.4.py. Nachfolgend werden die einzelnen Komponenten dieser Software und der Ablauf des Hauptprogramms näher beschrieben.

2.1 Softwaremodule

Die Software ist in 3 Abschnitte aufgeteilt. Dabei handelt es sich um die Definition der Variablen, die Definition der Funktionen und den Ablauf des Hauptprogramms. Nachfolgend werden die Definition der Variablen und die Definition der Funktionen näher betrachtet und erläutert.

2.1.1 Definition der Variablen

Die Software beginnt mit der Definition der GPIO-Pins. Dazu wird für jeden Pin eine globale Variable definiert. Die Pin-Variablen enthalten dabei eine Zahl entsprechend des zugewiesenen Pins. Beim Aufrufen von Funktionen mit diesen Variablen wird die enthaltene Zahl verwendet, um auf den entsprechenden GPIO-Pin zu verlinken. Eine genaue Übersicht zu den GPIO-Pin-Variablen liefert Tabelle 2.

Im zweiten Programmschritt werden einige Programm-Variablen definiert. Sie werden vielfältig in den einzelnen Funktionen und im Hauptprogramm verwendet. Tabelle 3 zeigt die Programm-Variablen übersichtlich dargestellt und erklärt.


2.1.2 Definiton der Funktionen

Um ein Programm schlank und übersichtlich zu programmieren, wurden wiederkehrende Programmbestandteile in Funktionen integriert. Nachfolgend werden alle im Programm definierten Funktionen aufgelistet und beschrieben. In Abbildung 6 werden unter den Funktionen bestehende Abhängigkeiten veranschaulicht.




Button(button nr)
Eingabevariable:
button nr . . . kann eine der 3 Button-Variablen enthalten (2/3/4)Beschreibung:
Gibt an, ob ein Taster gedrückt wurde. Welcher Taster geprüft werden soll, wird
durch die Eingabevariable ausgewählt.
Rückgabewert:
0. . . Taster ist nicht gedrückt, 1. . . Taster ist gedrückt
global button state. . . gibt an, ob Taster gedrückt wurde
global button old . . . speichert, welcher Taster gedrückt wurde. Erst wenn dieser los-
gelassen wird, kann er erneut betätigt werden.

Output Enable()
Eingabevariable:

Beschreibung:
Aktiviert die Tri-State-Ausgänge der Schieberegister und erlaubt das Durchschalten
der Speicherregister auf die Ausgänge. Damit können die Daten der Speicherregister
auf der LED-Matrix angezeigt werden. Siehe hierzu auch Anhang D Datenblatt
Schieberegister 74HC595 (Auszug).
Rückgabewert:


Output Disable()
Eingabevariable:

Beschreibung:
Schaltet die Tri-State-Ausgänge der Schieberegister hochohmig. Dadurch können die Speicherregister keinen Wert auf die Ausgänge schreiben.
D Datenblatt Schieberegister 74HC595 (Auszug)
Rückgabewert:


Clear Shift Register()
Eingabevariable:

Beschreibung:
Löscht den gesamten in den Schieberegistern gespeicherten Datenvektor. Der neue
Vektor enthält ausschließlich Nullen.
Rückgabewert:


Set Shift Register(data)
Eingabevariablen:
data. . . zu schreibender LED-Matrix Datenvektor
Beschreibung:
Schiebt einen Datenvektor beliebiger Länge in die Schieberegister.
Rückgabewert:


Set Storage Register()
Eingabevariable:

Beschreibung:
Übergibt den aktuellen Datenvektor der Schieberegister an die Speicherregister.
Dort wird der aktuelle Datenvektor auf die LED-Matrix ausgegeben.
Rückgabewert:


Sample(sample nr)
Eingabevariable:
sample nr . . . wählbarer Standarddatensatz (0. . . 7)
Beschreibung:
Gibt einen Standarddatensatz für die LED-Matrix entsprechend der eingegebenen
Datensatznummer zurück. (Startbildschirm, ’HI’, ’DU’, ’P1’, ’P2’, Unentschieden-
Bildschirm, ’Player 2 Win’, ’4 Gewinnt’)
Rückgabewert:
Datenvektor für LED-Matrix (84 Bit / mehr bei Lauftext - Sample 6 + 7)

Send Data(data)
Eingabevariable:
data. . . Datenvektor für LED-Matrix
Beschreibung:
Schreibt einen Datenvektor auf die LED-Matrix. Der Vektor enthält ein vollständiges
Bild.
Rückgabewert:


Position Check(level)
Eingabevariable:
level . . . zum Prüfen des Bitzustands (0. . . Low, 1. . . High)
global pos. . . aktuelle Position im Matrix-Datenvektor
global player nr . . . zeigt an, welcher Spieler an der Reihe ist (Matrixnavigation)
Beschreibung:
Prüft, ob die LED an der aktuellen Position des Matrix-Datenvektors ein- oder
ausgeschaltet ist. Es wird immer nur rot oder grün eingeschaltet, niemals beide LED-
Farben gemeinsam. Ist eine Farbe eingeschaltet, so gilt die LED als eingeschaltet.
Rückgabewert:
0. . . Bit6 = level, 1. . . Bit=level

Win Check(r)Eingabevariablen:
r . . . Reihe der aktuellen Matrix-Position
global pos. . . aktuelle Position im Matrix-Datenvektor
global data. . . Matrix-Datenvektor
Beschreibung:
Überprüft ausgehend von der übergebenen Position, ob das Spiel gewonnen wurde.
Es wird in alle Richtungen geprüft. (4 Coins in Reihe/Spalte/Diagonal)
Rückgabewert:
0. . . Spiel nicht gewonnen, 1. . . Spiel gewonnen
global win row . . . Vektor mit 4 Daten, gibt die Position des Gewinns an

Win Screen()
Eingabevariable:

Beschreibung:
Zeigt den Gewinn eines Spielers an. Die Anzeige dauert 4s.
Rückgabewert:


Draw Screen()
Eingabevariable:

Beschreibung:
Zeigt an, dass das Spiel unentschieden ausgegangen ist. Die Anzeige dauert 4s.
Rückgabewert:



2.2 Programmablauf

An dieser Stelle wird der Ablauf des Hauptprogramms beschrieben. Um die Übersichtlichkeit zu wahren, wird der Programmablauf in kleinen Absätzen nachvollzogen.

Definiton der Variablen

Das Programm beginnt mit der Definition der Variablen. Dabei wird zwischen GPIO-Variablen und Programm-Variablen unterschieden. Die GPIO-Variablen werden verwendet, um den verschiedenen Signalleitungen je einen GPIO zuzuordnen. Tabelle 2 zeigt eine detaillierte Zuordnung der einzelnen GPIO-Pins. Die Programm-Variablen werden im Hauptprogramm und den Funktionen verwendet. Eine detaillierte Zuordnung zeigt Tabelle 3.

Definiton der Funktionen

Nach den Variablen werden die Funktionen definiert. Dazu ist jede für das Programm geschriebene Funktion mit ihrem jeweiligen Quellcode nacheinander angeordnet. Um dem Interpreter deutlich zu machen, dass es sich um eine Definition handelt, bekommt jede Funktion das Präfix def.

Interrupt Event: Reset()

Die Funktion Reset() wird in einem parallelen Prozess geladen. Sie prüft, ob der Spieler einen Abbruchwunsch äußert. Auf diese Weise kann das Spiel jederzeit neu gestartet werden.

Begrüßung der Spieler

Zur Begrüßung der Spieler wird ein Lauftext "4 Gewinnt" auf der LED-Matrix ausgegeben.

Hauptschleife und Initialisierung

An dieser Stelle beginnt die Hauptschleife des Programms. Während der Initialisierung werden die Grundeinstellungen zum Starten des Spiels getroffen. Dabei wird das Reset-Flag gelöscht, die Grundposition der Matrix eingestellt, Spieler 1 als aktiv gewählt, der Tri-State-Ausgang der Schieberegister aktiviert und der aktuelle Datenvektor in den Schieberegistern gelöscht und an die Ausgänge übernommen.

Spielschleife

Die Spielschleife wiederholt sich so lang, bis das Reset-Flag gesetzt wird. Dies kann durch den parallelen Prozess Reset() oder durch das Ende des Spiels geschehen. Das Spiel wird beendet, wenn ein Spieler gewonnen hat oder keine Züge mehr möglich sind, weil die Matrix zu 100% gefüllt ist.

In der Spielschleife laufen immer die folgenden Schritte nacheinander ab: Sende Daten an LED-Matrix, prüfe Left-Taster, prüfe Enter-Taster, prüfe Right-Taster. Je nachdem, ob und, wenn ja, welcher Taster betätigt wurde, arbeitet die Spielschleife eine andere Aktion ab.

Left-Taster

Der Coin wird auf der LED-Matrix um eine Position weiter nach links verschoben. Sollte der Rand der LED-Matrix erreicht worden sein, so verharrt der Coin auf seiner aktuellen Position am linken Rand der Matrix.

Enter-Taster

Der Coin wird in der aktuellen Spalte fallen gelassen. Er gleitet bis zur untersten noch von keinem Coin besetzten Stelle und verharrt an dieser. Anschließend wird überprüft, ob durch den Wurf ein Gewinn erzielt wurde. Ist das der Fall, wird eine Gewinnmeldung ausgegeben und das Spiel beendet. Falls kein Gewinn festgestellt wurde, wird nun überprüft, ob noch gültige Züge vorgenommen werden können. Sollte die LED-Matrix bereits vollständig ausgefüllt sein, ist dies nicht mehr möglich und ein Unentschiedenbildschirm wird ausgegeben und das Spiel beendet. Falls noch weitere gültige Spielzüge möglich sind, wird der Spieler gewechselt und regulär in die Spielschleife zurück gesprungen.

Right-Taster

Der Coin wird auf der LED-Matrix um eine Position weiter nach rechts verschoben. Sollte der Rand der LED-Matrix erreicht worden sein, so verharrt der Coin auf seiner aktuellen Position am rechten Rand der Matrix.


3. Diskussion


3.1 Bekannte Probleme und Fehler


Flackerndes Display

Die LED-Matrix kann gelegentlich etwas flackern. Das liegt daran, dass die LED-Matrix vom Hauptprogramm beschrieben wird. Da je nach gewählter Aktion ein kürzeres oder längeres Hauptprogramm ausgeführt wird, ist kein gleichmäßiges Beschreiben der LED-Matrix möglich. Dieser Effekt ist jedoch sehr gering und fällt kaum ins Gewicht.


3.2 Ausblick


Parallele Prozesse und Interrupts

In der aktuellen Entwicklung könnten viele Funktionen des Hauptprogramms in Interrupts oder parallele Prozesse ausgelagert werden. So könnte beispielsweise das Beschreiben des Displays wesentlich gleichmäßiger ablaufen, wenn es in einen parallelen Prozessausgelagert wird. Auch das Abfragen der Taster könnte durch die Verwendung von Interrupts effizienter gestaltet werden.

Computergegner

Eine schöne Erweiterung des Projekts wäre ein optionaler Computergegner. So könnte man das Spiel auch allein spielen. Des weiteren wäre auch das Implementieren eines Demonstrationsmodus interessant. Dabei können zwei Computergegner in immer neuen Variationen gegeneinander spielen.

Integration weiterer Spiele

Für spätere Entwicklungen ist es vorstellbar, weitere Spiele auf der bereits vorhandenen Hardware zu programmieren. Gut geeignete Spiele wären beispielsweise Varianten von Tetris, Pong oder Snake.

Keine Kommentare:

Kommentar veröffentlichen