![]() |
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 |
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:
guter beitrag, hat mir bei der programmierung sehr effektiv geholfen!
Kommentar veröffentlichen