Freitag, 11. Februar 2011

Conrad Retro-Spiel Ping Pong Modifikation

Ich habe mir vor einiger Zeit den Bausatz für das Retro Ping Pong Spiel von Conrad besorgt. Irgendwann bin ich dann auf die Idee gekommen mir ein eigenes Programm auf den Mikrocontroller zu laden. Ich wollte mir ein Programm schreiben, welches eine Laufschrift realisiert. Letztendlich ist mir dies auch gelungen und in diesem Beitrag möchte ich beschreiben, wie das funktioniert.

Das Original-Spiel. Bild-Quelle: www.expli.de
Mein modifiziertes Pong-Spiel mit Laufschrift


Was benötigt wird

Zunächst benötigt man den Bausatz von Conrad, welche man online für aktuell 5€ kaufen kann:
http://www.conrad.de/ce/de/product/902766/CONRAD-RETRO-SPIEL-PING-PONG/SHOP_AREA_17618&promotionareaSearchDetail=005

Der Bausatz verwendet einen Atmel8 Mikroprozessor. Zum programmieren habe ich Eclipse for C/C++ Developer mit dem AVR-Plugin verwendet, sowie Ponyprog2000 zum flashen. Ich habe den Atmega8 mittels C programmiert.

Die Programme sind hier zu finden:
Eclipse: http://www.eclipse.org/
AVR-Plugin: http://avr-eclipse.sourceforge.net/wiki/index.php
PonyProg2000: http://www.lancos.com/prog.html

Ein gutes Tutorial zum Programmieren von AVR Mikrocontroller findet ihr auf:
http://www.mikrocontroller.net


Außerdem benötigt man ein Kabel zum verbinden des PC's mit dem Controller. Benötigt wird ein Kabel mit ISP Stecker, da der Controller Onboard programmiert wird. Das Kabel sollte Kompatibel mit Ponyprog sein. Ich habe mir das Kabel selber gebastelt.

Gute Bauanleitungem für das Kabel gibt es hier:
http://www.blafusel.de/misc/atmega8_isp.html
http://rumil.de/hardware/avrisp.html


Der Bausatz besteht im Wesentlichen aus einem Atmega8 SMD und zwei Schieberegistern (74HC4094D) zum ansteuern von 120 LED's.

Die Datasheets zu den Bauteilen findet man hier:
Atmega8: http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf
74HC4094: http://www.datasheetcatalog.com/datasheets_pdf/7/4/H/C/74HC4094.shtml



Der Mikrocontroller

Zunächst einmal beschreibe ich die Funktionsweise des Boards.

Im Wesentlichen besteht der Bausatz aus 120 LED's die einzeln angesteuert werden können. Durch die Schieberegister wird die "Spalte" der zu leuchtenden LED ausgewählt, während der Atmega8 selber die "Zeile" der LED's ansteuert. Dabei stellt der Atmega8 prinzipiell den Plus-Pol der Dioden, während die Schieberegister den Minus-Pol darstellen. Das heißt, dass auf der Zeile eine "1" anliegt, während in der entsprechenden Spalte eine "0" anliegen muss.
Der Atmega8 legt dabei den Inhalt der Schieberegister fest.



Der Atmega8 steuert die Eingänge STROBE, D, CLK der Schieberegister. das OE der 74HC4094D ist permanent auf 1 gesetzt. Näheres zur Funktionsweise der Schieberegister sind im Datasheet zu finden.

Die ISP Pins bei 180° Drehung des Boards
Um den Atmega8 zu programmieren ist bereits eine 6 Port ISP Schnittstelle vorhanden. Allerdings weicht die Belegung der Norm ab, zumal auch die MISO Leitung fehlt, welche benötigt wird um den Inhalt des Atmega8 auszulesen oder zu vergleichen.

Ich habe eine kleine Skizze angelegt um die PIN Belegung des ISP zu beschreiben. Die ISP befindet sich bei Draufsicht auf das Board an der oberen Seite. Zur besseren Handhabung habe ich das Board um 180 Grad gedreht, sodass der Atmega8 auf der linken Seite ist.


Die MISO Leitung muss direkt an den Atmega8 gelötet werden, welches dem PIN 16 entspricht.


Nun komme ich zu der PIN-Belegung welche für die Programme auf dem Atmega8 benötigt werden um die LED's gezielt anzusteuern.

Die Pinbelegung des Atmega8 zur Auswahl der Zeilen und zum Beschreiben der Schieberegister ist die folgende:

pin    bez.    funktion

13    PB1    Zeile 1
12    PB0    Zeile 2
11    PD7    Zeile 3
10    PD6    Zeile 4
9      PD5    Zeile 5
2      PD4    Zeile 6
26    PC3    Zeile 7
25    PC2    Zeile 8
24    PC1    Zeile 9
23    PC0    Zeile 10

Zum ansteuern der Schieberegister:
16    PB4    D
15    PB3    CLK
14    PB2    Strobe


Um die Schieberegister zu beschreiben muss zuerst die STROBE Leitung auf HIGH gesetzt werden. Danach wird je nachdem eine 0 oder eine 1 auf die Leitung D gesetzt, welche dann mit einen positiven CLK Flanke gesetzt werden.
Es interessiert lediglich nur das erste Schieberegister, da das zweite vom ersten angesteuert wird, wenn das erste das Limit von 8 Bit erreicht hat. Dadurch wird beispielsweise das 9. Bit automatisch durchgereicht und muss bei der Programmierung nicht berücksichtigt werden. Intern arbeiten die Register mit 16 Bit, da es sich um 2 x 8 Bit Schieberegister handelt, auch wenn das Feld 12 nur Spalten enthält.

Zum initialisieren des Atmega8 habe ich ein kleines Programm geschrieben, welches die Schieberegister vollständig mit 1en beschreibt (LED's sind dann alle aus!) und die Ports alle auf 0 setzt. Das Programm kann auch als Beispiel für die Programmierung genommen werden:

Hier der C Code:

init.h


/*
* init.h
*
* Created on: 11.02.2011
* Author: tetratux
*/

#ifndef INIT_H_
#define INIT_H_

extern void initialisiere(void);

#endif /* INIT_H_ */

#define ZEILE1 PORTB |= (1 << PB1);
#define NOT_ZEILE1 PORTB &= ~(1 << PB1);

#define ZEILE2 PORTB |= (1 << PB0);
#define NOT_ZEILE2 PORTB &= ~(1 << PB0);

#define ZEILE3 PORTD |= (1 << PD7);
#define NOT_ZEILE3 PORTD &= ~(1 << PD7);

#define ZEILE4 PORTD |= (1 << PD6);
#define NOT_ZEILE4 PORTD &= ~(1 << PD6);

#define ZEILE5 PORTD |= (1 << PD5);
#define NOT_ZEILE5 PORTD &= ~(1 << PD5);

#define ZEILE6 PORTD |= (1 << PD4);
#define NOT_ZEILE6 PORTD &= ~(1 << PD4);

#define ZEILE7 PORTC |= (1 << PC3);
#define NOT_ZEILE7 PORTC &= ~(1 << PC3);

#define ZEILE8 PORTC |= (1 << PC2);
#define NOT_ZEILE8 PORTC &= ~(1 << PC2);

#define ZEILE9 PORTC |= (1 << PC1);
#define NOT_ZEILE9 PORTC &= ~(1 << PC1);

#define ZEILE10 PORTC |= (1 << PC0);
#define NOT_ZEILE10 PORTC &= ~(1 << PC0);

#define INPUT_1 PORTB |= (1 << PB4);
#define INPUT_0 PORTB &= ~(1 << PB4);
#define POS_FLANKE PORTB |= (1 << PB3);
#define NEG_FLANKE PORTB &= ~(1 << PB3);
#define STROBE_HIGH PORTB |= (1 << PB2);
#define STROBE_LOW PORTB &= ~(1 << PB2);

#define ANZAHL_SPALTEN_FELD 16 
#define ANZAHL_ZEILEN_FELD 10


init.c

/*
 * init.c
 *
 *  Created on: 11.02.2011
 *      Author: tetratux
 */

#include <avr/io.h>
#include <util/delay.h>

#include "init.h"

extern void initialisiere(void) {

    /* Ausgaenge setzen */

    // Zeilen
    DDRB |= (1 << PB1); // Zeile1
    DDRB |= (1 << PB0); // Zeile2
    DDRD |= (1 << PD7); // Zeile3
    DDRD |= (1 << PD6); // Zeile4
    DDRD |= (1 << PD5); // Zeile5
    DDRD |= (1 << PD4); // Zeile6
    DDRC |= (1 << PC3); // Zeile7
    DDRC |= (1 << PC2); // Zeile8
    DDRC |= (1 << PC1); // Zeile9
    DDRC |= (1 << PC0); // Zeile10

    // Ausgaenge zum Schieberegister
    DDRB |= (1 << PB5);
    DDRB |= (1 << PB4);
    DDRB |= (1 << PB3);
    DDRB |= (1 << PB2);

    // Setze alle Ausgaenge auf 0
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;

    // Beschreibe Schieberegister mit 1en

    // Strobe auf High
    STROBE_HIGH

    // Leitung zum Eingang D des Schieberegisters mit 1 belegen
    INPUT_1

    // Erzeuge soviele Clocksignale, dass die Schieberegister
    // komplett mit einsen gefuellt sind
    for (int i = 0; i < ANZAHL_SPALTEN_FELD; i++) {

        POS_FLANKE
        NEG_FLANKE

    }

    // Setze Leitung wieder auf null
    INPUT_0

    // Strobe auf Low
    STROBE_LOW
}

Zu Problemen in der Anzeige kann es kommen, wenn versucht wird eine LED zu aktivieren die zum Beispiel in der 1. Zeile liegt und gleichzeitig eine LED aus einer anderen Zeile, die aber auch in einer anderen Spalte liegt. Würden beide Zeilen gleichzeitig aktiviert sein, würde es nicht das gewünschte Muster ergeben, da die Schieberegister nun mal nur die Bits für eine Zeile beinhalten können. Darum muss ein Algorithmus geschrieben werden, die immer nur eine Zeile anzeigt und das Schieberegister auch nur die Bits für diese Zeile beinhalten. Die Zeilen würden nacheinander aktiviert werden. Wenn der Wechsel zwischen den Zeilen schnell genug hintereinander erfolgt, wird dadurch eine optische Täuschung erzeugt, sodass das gewünschte Muster erscheint. ;-)

1 Kommentar:

Unknown hat gesagt…

guter beitrag, hat mir bei der programmierung sehr effektiv geholfen!