PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Ping Pong umprogrammieren?



bnitram
09.12.2010, 15:20
Hallo Forum,
Ich wollte mir in nächster Zeit dieses (http://www.conrad.de/ce/de/product/902766/CONRAD-RETRO-SPIEL-PING-PONG/SHOP_AREA_17618&promotionareaSearchDetail=005) Ping Pong von Conrad kaufen um mich langsam mit der Programmierung des Atmegas vertraut zu machen, da ich zurzeit nur den Pro-Bot besitze.
Jetzt zu meiner Frage:
Ich habe jetzt shon viel darüber gelesen das Spiel umzuprogrammieren.
Was brauche ich außer diesem (http://www.ak-modul-bus.de/stat/mega8_isp_programmer.html) ISP Programmer an Software??
Ich möchte das dann in C programmieren.
Also welche programmier umgebung brauche ich??

Mfg
bnitram

radbruch
09.12.2010, 16:52
Hallo

Der Programmer benötigt zwingend eine RS232-Schnittstelle. Ob das auch mit einem RS232-USB Wandler funktioniert, kann ich nicht sagen.

Das Spiel kannte natürlich auch schon, aber bei 5 Euronen konnte ich jetzt nicht mehr widerstehen. Danke für den Tipp, hab' gleich mal 5 Einheiten geordert. Nettes Weihnachtsprojekt. ;)

http://www.elo-web.de/elo/entwicklung-und-projekte/ping-pong
(Nur registrierte Besucher können alles sehen!)

Gruß

mic

bnitram
09.12.2010, 17:01
Mit welcher Software wird es denn Programmiert??
RS232 ist kein Problem für mich, da ich noch eine Schittstelle besitze obwohl ich ein super Mainboard besitze.

Mfg
bnitram

PS:
Den Link kenne ich schon, habe ichts heraus gefunden.

radbruch
09.12.2010, 17:07
Das ist ein Mega8, den kannst du z.B. in C oder Bascom programmieren.

Die "Kollegen" sind uns mal wieder 'ne Nasenlänge vorraus:
http://www.mikrocontroller.net/topic/184955

BurningWave
09.12.2010, 17:32
Als Software eignet sich das (kostenlose) AVR Studio mit WinAVR gcc-Plugin (zum Programmieren in C).

Es gibt hier aber Unmengen an Threads, die sich mit dem Thema "Einstieg in µC-Technik" beschäftigen. Einfach mal die Forensuche benutzen.

bnitram
09.12.2010, 18:54
Danke,
Aber was muss ich da Downloaden??
Ich blick da garnicht durch!! ](*,) :oops:
Könntest du mir evt. einen Link geben, damit ich eine Programmierungsumgebung dafür habe wo ich in C programmieren kann???

Mfg
bnitram

Besserwessi
09.12.2010, 20:20
Für die C-Programmierung braucht man den Compiler (GCC) und einen Editor, ggf. mit Zusatzfunktionen.
Als Minimalversion ist das das WINAVR paket. Da ist als Editor Porgrammers Notepad mit dabei. Damit geht es schon, ist aber nicht besonders Komfortabel.

Als besseren Ersatz für den Editor kann man AVRStudio (von Atmel) nutzen. Gcc aus dem Winavr Paket braucht man aber weiterhin.

bnitram
09.12.2010, 21:07
Also muss ich mir einmal Winavr und einmal das AVRstudio downloaden ?
Welcher AVRstudio soll ich denn nehmen ?(version. . . . )
Und muss ich das GCC irgent wie einbinden oder geht das so ?

Auf jeden Fall Danke

Mfg
bnitram

BurningWave
09.12.2010, 21:19
Welcher AVRstudio soll ich denn nehmen ?(version. . . . )

Die neueste ;)

Einfach erst das AVR Studio und dann WinAVR installieren. Das avr-gcc Plugin wird automatisch erkannt.

bnitram
10.12.2010, 11:44
Danke
Ich werde es mal probieren.

Mfg
bnitram

radbruch
10.12.2010, 17:52
Hallo

Ich werde folgendes verwenden:

WinAVR: http://sourceforge.net/projects/winavr/files/WinAVR/
KamAVR: http://www.avrfreaks.net/index.php?module=Freaks%20Tools&func=viewItem&item_id=632
BurnOMat: http://avr8-burn-o-mat.aaabbb.de/
mySmartUSB: http://shop.myavr.de/Programmer/mySmartUSB%20MK2%20(Programmer%20und%20Bridge).htm ?sp=article.sp.php&artID=42
myAVR ProgTool: http://shop.myavr.de/index.php?ws=download_file.ws.php&dlid=112&filename=software/tool_myavr-progtool-131-b2107_en_de.zip
(für die Fuses, aber das werden wir wohl nicht benötigen)

Gruß

mic

[Edit]
Am Samstag um 15 Uhr wurde das Päckchen schon geliefert :)

Sonntag 16 Uhr:


C:\WinAVR\bin\avrdude.exe -C C:\WinAVR\bin\avrdude.conf -p m8 -P com2 -c avr910 -U flash:r:C:\Users\mic\Documents\pingpong.hex:a

Found programmer: Id = "AVR ISP"; type = S
Software Version = 2.3; Hardware Version = 2.0
Programmer supports auto addr increment.
Programmer supports buffered memory access with buffersize = 8 bytes.

Programmer supports the following devices:
[gekürzt]

Reading | ################################################## | 100% 0.06s

avrdude.exe: Device signature = 0x1e9307
avrdude.exe: reading flash memory:

Reading | ################################################## | 100% 9.22s

avrdude.exe: writing output file "C:\Users\mic\Documents\pingpong.hex"
avrdude.exe: output file C:\Users\mic\Documents\pingpong.hex auto detected as Intel Hex

avrdude.exe done. Thank you.



ISP mache ich ja eher selten, deshalb ist das für mich auch schon ein kleiner Erfolg ;)

bnitram
10.12.2010, 18:06
Ah
Also ich habe jetzt WinAVR und das neuste AVRStudio.

Wenn das Paket dann ankommt werde ich mal etwas programmieren.

Mfg
bnitram

radbruch
12.12.2010, 16:34
Hallo

Inzwischen kann ich das Teil erfolgreich flashen:

http://i2.ytimg.com/vi/MRRoa1hpnMQ/1.jpg (http://www.youtube.com/watch?v=MRRoa1hpnMQ)
http://www.youtube.com/watch?v=MRRoa1hpnMQ
http://www.youtube.com/watch?v=JpfsYIkHjlw

Da wird sich meine Angetraute sicher sehr freuen, wenn ich ihr fünf so Dinger an den Christbaum hänge ;)

Dieses Beispielprogramm von der elo-Webseite habe ich ausgewählt, weil es zeigt, wie einfach die Daten an die LCD-Matrix ausgegeben werden. Und weil es einen brauchbaren Font mitbringt:


/*
* laufschrift.c
*
* Ein einfache "Laufschrift" auf dem Ping-Pong Board.
*
* Kompilierbar mittels AVR Studio 4 oder WinAVR
*
* Der Sourcecode und das Hexfile dürfen frei verwendet werden.
* Nutzung erfolgt auf eigene Gefahr.
*
* Ver. Date Author Comments
* ------- ---------- -------------- ------------------------------
* 1.00 07.11.2009 Sascha Bader initial
*/


/* -----------------------------------------
* Defines (Präprozessor Makros)
* -----------------------------------------*/

#define F_CPU 8000000UL /* CPU Takt (für delay-Routine) */

#define WIDTH 12 /* Breite des Displays */
#define HEIGHT 10 /* Höhe des Displays */
#define FONTWIDTH 8 /* Breite des Zeichensatzes */
#define FONTHEIGHT 10 /* Höhe des Zeichensatzes */

#define GetPixel(x,y) leds[y]&(1<<x) /* Makro: Ein "Pixel" auslesen */
#define SetPixel(x,y) leds[y]|=1<<x /* Makro: Ein "Pixel" setzen */
#define ClearPixel(x,y) leds[y]&=~(1<<x) /* Makro: Ein "Pixel" löschen */


/* -----------------------------------------
* Includes
* -----------------------------------------*/
#include <inttypes.h> /* Definition der Datentypen uint8_t usw. */
#include <avr/interrupt.h> /* Interruptbehandlungsroutinen (für Timerinterrupt) */
#include <util/delay.h> /* Definition der Verzögerungsfunktionen (_delay_ms) */
#include <avr/pgmspace.h> /* Hilfsfunktionen um Daten aus dem Flash zu lesen */
#include "font.h" /* Definition des Zeichensatzes */


/* -----------------------------------------
* Globale Variablen
* -----------------------------------------*/
uint16_t leds[WIDTH]; /* Inhalt der LED-Matrix */
prog_uint8_t * fnt = (prog_uint8_t *) font; /* Zeiger auf den Zeichensatz im Flash */
volatile uint8_t col = 0; /* Aktuelle Spalte (für Interruptroutine)
"volatile", da durch Interrupt verändert */

/* -----------------------------------------
* Text der Laufschrift (Globele Variable)
* -----------------------------------------*/
prog_uint8_t text[] =
" Frohes Fest und guten Rutsch :)\
~"; /* Ende-Kennzeichen (nicht vergessen) */

/* -----------------------------------------
* Prototypen der Funktionen
* -----------------------------------------*/
void PrintScrollColumn(uint8_t c, int pixelx, int y);
void ScrollLeft(void);

/* -------------------------------------------------------------------------
* Main Funktion
*
* Initialisiert den Timer Interrupt und
* behandelt die Laufschrift
* -------------------------------------------------------------------------*/
int main(void)
{
uint8_t * tpos;
uint8_t softx;

cli(); // Interrupts sperren (damit keiner dazwischenfunkt)

/*---------------------------------------------------
* Ports konfigurieren (Ein-/Ausgänge)
*---------------------------------------------------*/
DDRC = 0x0f; // ( 0x0f PORTC als AD-Eingang)
DDRB = 0xff; // Portb = Output
DDRD = 0xff; // Portd = Output

/*---------------------------------------------------------------------------
* 8-Bit Timer TCCR0 für das Multiplexing der LEDs initialisieren
* Es wird ca. alle 2 Mikrosekunden ein Overflow0 Interrupt ausgelöst
* Berechnung: T = Vorteiler * Wertebereich Zähler / Taktfreuenz
* = 64 * 256 / ( 8000000 Hz ) = 2,048 ms
*---------------------------------------------------------------------------*/
TCCR0 |= (1<<CS01) | (1<<CS00); // 8-bit Timer mit 1/64 Vorteiler
TIFR |= (1<<TOV0); // Clear overflow flag (TOV0)
TIMSK |= (1<<TOIE0); // timer0 will create overflow interrupt

sei(); // Interrupts erlauben

/*---------------------------------------------------
* Hauptschleife (Laufschrift erzeugen)
*---------------------------------------------------*/
while(1) // Endlosschleife
{
for (tpos=text;pgm_read_byte(tpos)!='~';tpos++) // Aktuelles Zeichen lesen
{
for (softx=0;softx<FONTWIDTH;softx++) // Pixel des Zeichens abarbeiten
{
ScrollLeft(); // Platz schaffen und Zeilen nach links schieben
PrintScrollColumn(pgm_read_byte(tpos),softx,0); // Ganz rechts eine Spalte des Zeichens ausgeben
_delay_ms(35); // Ein bischen warten damit es nicht zu schnell wird
}
}
}
return 0;
}

/* -------------------------------------------------------------------------
* Funktion PrintScrollColumn
*
* Aktualisiert die Spalte ganz rechts mit
* einem 1 "Pixel" breitem Ausschnitt des
* Lauftextes.
*
* \param c Auszugebendes Zeichen
* \param pixelx Auszugebende Spalte des Zeichens
* \param y Vertikale Vverschiebnung
* -------------------------------------------------------------------------*/
void PrintScrollColumn(uint8_t c, int pixelx, int y)
{
unsigned char fontbyte = 0;
uint8_t pixelpos;
uint8_t fonty;
uint8_t mask;

pixelpos = pixelx & 0x07; /* Auf 8 Pixel pro Zeichen limitieren */

for (fonty=0;fonty<FONTHEIGHT;fonty++)
{
fontbyte = pgm_read_byte_near(fnt+c*FONTHEIGHT+fonty); /* Ein Byte (Zeile) des aktuellen Zeichens lesen */

mask = 1<<pixelpos; /* Maske auf die gewünschte Spalte zurechtschieben */
if ((fontbyte & mask) != 0) /* Prüfen ob das Bit in der Spalte des Zeichens gesetzt ist */
{
leds[WIDTH-1]|=1<<fonty; /* Setzen eines Pixels im Display ganz rechts */
}
else
{
leds[WIDTH-1]&=~(1<<fonty); /* Löschen eines Pixels im Display ganz rechts */
}
}
}

/* -------------------------------------------------------------------------
* Funktion ScrollLeft
*
* Verschiebt den Inhalt LED-Matrix um eine Spalte nach links.
* Die erste Spalte tritt dabei an die Position der letzten Spalte.
* -------------------------------------------------------------------------*/
void ScrollLeft(void)
{
uint8_t xcol; /* Spaltenzähler */
uint16_t first; /* Zwischenspeicher der ersten Spalte */

first = leds[0]; /* Erste Spalte sichern */
for (xcol=0;xcol<WIDTH-1;xcol++)
{
leds[xcol]=leds[xcol+1]; /* Spalten nach links verschieben */
}
leds[WIDTH-1] = first; /* Erste Spalte an letzte Spalte kopieren */
}

/* -------------------------------------------------------------------------
* Interrupt Routine
*
* Gibt nacheinander alle Spalten mit LED-Daten aus.
* Dazu wird mittels der Schieberegister die aktuelle Spalte
* ausgewählt und dann das Bitmuster derselben auf die Ports
* gegeben.
* Beim nächsten Interrupt ist dann die nächste Spalte dran.
* -------------------------------------------------------------------------*/
// interrupt routine
SIGNAL (SIG_OVERFLOW0)
{
uint16_t ledval;
uint8_t portcout;
uint8_t portdout;

cli(); /* Interrupts verbieten */

/*--------------------------------------------------
* Aktuelle Spalte ermitteln
*--------------------------------------------------*/
col++;
if (col == 12)
{
col = 0;
}

/*--------------------------------------------------
* Ports initialisieren
*--------------------------------------------------*/
PORTD = 0;
PORTB = 0;
PORTC = 0;

/*---------------------------------------------------
* Eine einzelne 0 durch die Schiebergister schieben
*---------------------------------------------------*/
if ( col == 0 )
{
PORTB &= ~(1 << 4); /* Bei der ersten Spalte eine 0 ausgeben (PB4 = 0) */
/* Diese 0 geht auf die Reise durch die Schieberegister */
}
else
{
PORTB |= (1 << 4); /* Danach Einsen hinterherschicken (PB4 = 1) */
}

/*---------------------------------------------------
* Impulse für die Schieberegister generieren
*---------------------------------------------------*/
PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */

PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */

/*---------------------------------------------------
* Daten der Spalte holen und auf die Ports verteilen
*---------------------------------------------------*/
ledval = leds[col];
portdout = ledval & 0xff; /* low byte */
portcout = portdout & 0x0f; /* low nibble */
portdout = portdout & 0xf0; /* high nibble */

PORTD = portdout & 0xff;
PORTC = portcout & 0xff;
PORTB = (ledval >> 8) & 0x03; /* high byte */

sei(); /* Interrupts wieder erlauben */
}
(Code von http://www.elo-web.de/elo/mikrocontroller-und-programmierung/ping-pong/laufschrift)

Gruß

mic

[Edit]
http://i1.ytimg.com/vi/tsTk4Un89uA/1.jpg (http://www.youtube.com/watch?v=tsTk4Un89uA)
http://www.youtube.com/watch?v=tsTk4Un89uA

Neuer Bildspeicher mit nur 15 Bytes und set()/unset()-Funktionen. Echt ein nettes Spielzeug:


#define F_CPU 8000000UL

#include <inttypes.h>
#include <avr/interrupt.h>
#include <util/delay.h>

uint8_t bildspeicher[15];
volatile uint8_t col = 0;
uint8_t x, y;

void set(uint8_t zeile, uint8_t spalte)
{
uint8_t temp;

if(zeile < 8)
bildspeicher[spalte] |= (1 << zeile);
else
{
if(spalte<4) temp = 12;
else if(spalte<8) temp = 13;
else temp = 14;
if(zeile & 1) bildspeicher[temp] |= (1<<((spalte%4)*2+1)); // Zeile 10
else bildspeicher[temp] |= (1<<(spalte%4)*2); // Zeile 9
}
}

void unset(uint8_t zeile, uint8_t spalte)
{
uint8_t temp;

if(zeile < 8)
bildspeicher[spalte] &= ~(1 << zeile);
else
{
if(spalte<4) temp = 12;
else if(spalte<8) temp = 13;
else temp = 14;
if(zeile & 1) bildspeicher[temp] &= ~(1<<((spalte%4)*2+1)); // Zeile 10
else bildspeicher[temp] &= ~(1<<(spalte%4)*2); // Zeile 9
}
}

int main(void)
{
cli();

DDRC = 0x0f;
DDRB = 0xff;
DDRD = 0xff;

/*---------------------------------------------------------------------------
* 8-Bit Timer TCCR0 für das Multiplexing der LEDs initialisieren
* Es wird ca. alle 2 Mikrosekunden ein Overflow0 Interrupt ausgelöst
* Berechnung: T = Vorteiler * Wertebereich Zähler / Taktfreuenz
* = 64 * 256 / ( 8000000 Hz ) = 2,048 ms
*---------------------------------------------------------------------------*/
TCCR0 |= (1<<CS01) | (1<<CS00); // 8-bit Timer mit 1/64 Vorteiler
TIFR |= (1<<TOV0); // Clear overflow flag (TOV0)
TIMSK |= (1<<TOIE0); // timer0 will create overflow interrupt

sei(); // Interrupts erlauben

while(1)
{
for(x=0; x<15; x++) bildspeicher[x] = 0b10101010;
_delay_ms(500);
for(x=0; x<15; x++) bildspeicher[x] = 255; // alle LEDs an
_delay_ms(1000);
for(x=0; x<15; x++) bildspeicher[x] = 0; // alle LEDs aus
_delay_ms(500);

for(x=0; x<12; x++)
for(y=0; y<10; y++)
{
set(y, x);
_delay_ms(25);
}
_delay_ms(1000);

for(y=10; y; y--)
for(x=12; x; x--)
{
unset(y-1, x-1);
_delay_ms(25);
}
_delay_ms(1000);
}
return (0);
}

/* -------------------------------------------------------------------------
* Interrupt Routine
*
* Gibt nacheinander alle Spalten mit LED-Daten aus.
* Dazu wird mittels der Schieberegister die aktuelle Spalte
* ausgewählt und dann das Bitmuster derselben auf die Ports
* gegeben.
* Beim nächsten Interrupt ist dann die nächste Spalte dran.
* -------------------------------------------------------------------------*/
// interrupt routine
SIGNAL (SIG_OVERFLOW0)
{
uint16_t ledval;
uint8_t portcout;
uint8_t portdout;

cli(); /* Interrupts verbieten */

/*--------------------------------------------------
* Aktuelle Spalte ermitteln
*--------------------------------------------------*/
col++;
if (col == 12)
{
col = 0;
}

/*--------------------------------------------------
* Ports initialisieren
*--------------------------------------------------*/
PORTD = 0;
PORTB = 0;
PORTC = 0;

/*---------------------------------------------------
* Eine einzelne 0 durch die Schiebergister schieben
*---------------------------------------------------*/
if ( col == 0 )
{
PORTB &= ~(1 << 4); /* Bei der ersten Spalte eine 0 ausgeben (PB4 = 0) */
/* Diese 0 geht auf die Reise durch die Schieberegister */
}
else
{
PORTB |= (1 << 4); /* Danach Einsen hinterherschicken (PB4 = 1) */
}

/*---------------------------------------------------
* Impulse für die Schieberegister generieren
*---------------------------------------------------*/
PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */

PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */

/*---------------------------------------------------
* Daten der Spalte holen und auf die Ports verteilen
*---------------------------------------------------*/
ledval = bildspeicher[col];
portdout = ledval & 0xff; /* low byte */
portcout = portdout & 0x0f; /* low nibble */
portdout = portdout & 0xf0; /* high nibble */

PORTD = portdout & 0xff;
PORTC = portcout & 0xff;
if(col<4) ledval=bildspeicher[12];
else if(col<8) ledval=bildspeicher[13];
else ledval=bildspeicher[14];
PORTB = (ledval >> (col%4)*2) & 0x03; /* high byte */

sei(); /* Interrupts wieder erlauben */
}

bnitram
13.12.2010, 12:01
Hallo,
Sieht sehr gut aus was du da gemacht hasst! :)
Aber eine Frage habe ich noch:
Ich habe das Ping Pong jetzt zuammen gebaut und warte nur noch auf den Programmer.
Ich habe festgestellt das überwiegent KamAVR bei Elo-web benutzt wird.(radbruch benutzt es auch)
Was mus ich in der IDE einstellen damit ich das PingPong programmieren kann??
Muss ich irrgentetwas beachten??

Mfg
bnitram

radbruch
13.12.2010, 13:16
Hallo

Um das Spiel zu testen kann man auch mit den Fingern auf P1 bis P4 oder C4 (Selbsttest) rumfummeln. Das Programm erkennt den Hautwiderstand dann als Poti ;)

Meine Einstellungen beim KAMAvr, zusätzlich muss man dem Programm noch einmalig (klappt bei mir allerdings grad noch nicht) mitteilen, wo die WINAvr-Installation ist:

http://radbruch.bplaced.net/pingpong/einstellungen_kamavr.jpg

Um die Makefiles braucht man sich dann nicht mehr kümmern, denn die werden automatisch erzeugt. Um ein neues Projekt anzulegen (oder um das aktuelle Projekt zu Clonen) speichere ich zuerst das Projekt mit [File->Save Project as..] und anschliessend die Datei mit [File->Save File as...] jeweils mit dem neuen Namen ab. Nach dem Speichern der Datei lasse ich dann noch die Projektdatei in den neuen Namen ändern (PopUp mit [Ja] beantworten) und fertig. Zusätzliche C-Dateien (wie z.B. asuro.c) werden über einen Rechtsklick auf "Files" (über dem test.c) und "Add File..." eingebunden. Die erzeugte Hex-Datei trägt immer den Projektnamen.

Mit dem aktuellen Treiber wurde mein mySmartUSB als serieller Programmer eingebunden. Deshalb sollte meine Einstellung (über [Settings->AVRDUDE]) auch mit dem einfachen seriellen Programmer funktionieren:

http://radbruch.bplaced.net/pingpong/einstellungen_burnomat.jpg

COM muss man an die eigenen Verhältnisse anpassen. (btw. hat auch mein HighTech-Board (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=517249#517249) noch eine echte serielle Schnittstelle :)

Gruß

mic

[Edit]
http://i4.ytimg.com/vi/ofswO2OCq-U/1.jpg (http://www.youtube.com/watch?v=ofswO2OCq-U)
http://www.youtube.com/watch?v=ofswO2OCq-U

Leider ist das Video schlecht, aber mit etwas gutem Willen kann man die vier Helligkeitsstufen erkennen. Interessanterweise zeigen die blauen Phantomleds bei Sekunde drei und vier genau den gewollten Effekt;)

Der ungeputzte Quellcode:

#define F_CPU 8000000UL

#include <inttypes.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#define colors 4 // Anzahl der Farbebenen

uint8_t bildspeicher[colors][15];
volatile uint8_t col = 0;
uint8_t x, y;

void set(uint8_t x, uint8_t y, uint8_t c);

int main(void)
{
cli();

DDRC = 0x0f;
DDRB = 0xff;
DDRD = 0xff;

/*---------------------------------------------------------------------------
* 8-Bit Timer TCCR0 für das Multiplexing der LEDs initialisieren
* Es wird ca. alle 2 Mikrosekunden ein Overflow0 Interrupt ausgelöst
* Berechnung: T = Vorteiler * Wertebereich Zähler / Taktfreuenz
* = 64 * 256 / ( 8000000 Hz ) = 2,048 ms
*---------------------------------------------------------------------------*/
TCCR0 |= (1<<CS01) | (0<<CS00); // 8-bit Timer mit 1/8 Vorteiler !!!!!!!!!!
TIFR |= (1<<TOV0); // Clear overflow flag (TOV0)
TIMSK |= (1<<TOIE0); // timer0 will create overflow interrupt

sei(); // Interrupts erlauben

for(x=0; x<15; x++)
for(y=0; y<colors; y++)bildspeicher[y][x] = 0b10101010; // voll hell
_delay_ms(300);
for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, (x/3)%colors+1); // Helligkeitsstufen anzeigen
_delay_ms(2000);
for(x=0; x<15; x++)
for(y=0; y<colors; y++)bildspeicher[y][x] = 0; // alle LEDs in allen Ebenen aus

while(1)
{
for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, ((x+1)^(y+3)^(col+5))%colors+1); // Zufallsfarbe ;)
_delay_ms(100);
}
return (0);
}

SIGNAL (SIG_OVERFLOW0)
{
static uint8_t ebene=0;
uint16_t ledval;
uint8_t portcout;
uint8_t portdout;

cli(); /* Interrupts verbieten */ // aha ;)

PORTD = 0;
PORTB = 0;
PORTC = 0;

if ( col == 0 ) PORTB &= ~(1 << 4);/* Bei der ersten Spalte eine 0 ausgeben (PB4 = 0) */
else PORTB |= (1 << 4); /* Danach Einsen hinterherschicken (PB4 = 1) */

PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */

PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */

/*---------------------------------------------------
* Daten der Spalte holen und auf die Ports verteilen
*---------------------------------------------------*/
ledval = bildspeicher[ebene][col];
portdout = ledval & 0xff; /* low byte */
portcout = portdout & 0x0f; /* low nibble */
portdout = portdout & 0xf0; /* high nibble */

PORTD = portdout & 0xff;
PORTC = portcout & 0xff;
if(col<4) ledval=bildspeicher[ebene][12];
else if(col<8) ledval=bildspeicher[ebene][13];
else ledval=bildspeicher[ebene][14];
PORTB = (ledval >> (col%4)*2) & 0x03; /* high byte */

col++;
if(col>11)
{
col=0;
ebene++;
if(ebene == colors) ebene=0;
}

sei(); /* Interrupts wieder erlauben */
}
void set(uint8_t x, uint8_t y, uint8_t c)
{
uint8_t ebene, temp;

y = 9-y; // Koordinatennullpunkt unten links
if(y < 8)
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][x] |= (1 << y);
else bildspeicher[ebene][x] &= ~(1 << y);
else
{
if(x<4) temp = 12;
else if(x<8) temp = 13;
else temp = 14;
if(y & 1)
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][temp] |= (1<<((x%4)*2+1)); // y 10
else bildspeicher[ebene][temp] &= ~(1<<((x%4)*2+1));
else
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][temp] |= (1<<((x%4)*2)); // y 9
else bildspeicher[ebene][temp] &= ~(1<<((x%4)*2));
}
}


Geputzte Version:

// https://www.roboternetz.de/phpBB2/viewtopic.php?p=529702#529702 mic 15.12.2010

#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>

//#define F_CPU 8000000UL // macht KAMAvr automatisch
#define colors 4 // Anzahl der Farbebenen

// Einen Bildpunkt an x, y setzen. Werte für c: 0 ist aus, 1 ist dunkel, 4 ist hell
void set(uint8_t x, uint8_t y, uint8_t c);

volatile uint8_t col = 0;
uint8_t x, y, bildspeicher[colors][15];

int main(void)
{
cli();

DDRB = 0xff;
DDRC = 0x0f;
DDRD = 0xff;

TCCR0 |= (1<<CS01) | (0<<CS00); // 8-bit Timer mit 1/8 Vorteiler !!!!!!!!!!
TIFR |= (1<<TOV0); // Clear overflow flag (TOV0)
TIMSK |= (1<<TOIE0); // timer0 will create overflow interrupt
sei(); // Interrupts erlauben

for(x=0; x<15; x++)
for(y=0; y<colors; y++)bildspeicher[y][x] = 0b10101010; // LEDs voll hell
_delay_ms(300);

for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, (x/3)%colors+1); // Helligkeitsstufen anzeigen
_delay_ms(2000);

for(x=0; x<15; x++)
for(y=0; y<colors; y++)bildspeicher[y][x] = 0; // alle LEDs in allen Ebenen aus

while(1)
{
for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, (x^y^TCNT0)%colors+1); // Zufallsfarbe ;)
_delay_ms(100);
}
return (0);
}

SIGNAL (SIG_OVERFLOW0)
{
static uint8_t ebene=0;
uint16_t ledval;

PORTB &= ~0x03; // Nur die Pins der Displaymatrix werden auf Low gesetzt
PORTC &= ~0x0f;
PORTD &= ~0xf0;

// Spalten
if(col) PORTB |= (1<<4); /* Danach Einsen hinterherschicken (PB4 = 1) */
else PORTB &= ~(1<<4); /* Bei der ersten Spalte eine 0 ausgeben (PB4 = 0) */
PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */
PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */

// Zeilen
ledval = bildspeicher[ebene][col]; // y 9 bis 2
PORTC = ledval & 0x0f;
PORTD = ledval & 0xf0;
ledval = bildspeicher[ebene][12+(col>>2)]; // y 1 und 0
PORTB = (ledval >> (col%4)*2) & 0x03;

col++;
if(col>11)
{
col=0;
ebene++;
if(ebene == colors) ebene=0;
}
}

void set(uint8_t x, uint8_t y, uint8_t c)
{
uint8_t ebene;

y = 9-y; // Koordinatennullpunkt unten links
if(y < 8) // y 9 bis 2
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][x] |= (1 << y);
else bildspeicher[ebene][x] &= ~(1 << y);
else // y 1 und 0
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][12+(x>>2)] |= (1<<((x%4)*2+(y&1)));
else bildspeicher[ebene][12+(x>>2)] &= ~(1<<((x%4)*2+(y&1)));
}


Aufbau der 15 Bytes des Bildspeichers:

0000 0000 0011 | Bytenummer
0123 4567 8901 |

0000 0000 0000 | Bit \
1111 1111 1111 | | Port C
2222 2222 2222 | | Pin 0-3
3333 3333 3333 | /
4444 4444 4444 | \
5555 5555 5555 | | Port D
6666 6666 6666 | | Pin 4-7
7777 7777 7777 | /

0246 0246 0246 | Bit \ Port B
1357 1357 1357 | / Pin 0 und 1

|12| |13| |14| | Bytenummer

Koordinatennullpunkt von set(0,0, Helligkeit)
ist links unten.

// Einfache Ansteuerung des PingPong-Spiels mic 20.12.2010

// Das Programm sollte mit der orginalen Hardware des Spiels funktionieren.

// Es werden drei Funktionen zur Verfügung gestellt:

// cls() löscht das Display
// set(x, y, c) setzt an x, y eine LED. Werte für c: 0 ist aus, 1 bis 4 die Helligkeit
// readADC(Kanal) liest den ADC, P2 ist Kanal 6, P3 ist Kanal 7

// Neben den vier Helligkeitsstufen sind die LEDs nun auch dimmbar, allerdings
// nur alle zusammen. Möglich wird das durch den Timer2, den ich hier im
// FastPWM-Mode betreibe und eine zusätzliche ISR. Wie gehabt, werden die LEDs beim
// Bildaufbau über die Überlauf-ISR gesetzt. Gelöscht werden sie nun aber in der
// Compare-ISR. Diese wird immer dann aufgerufen, wenn das Zählregister des Timers
// den selben Inhalt wie das OCR2-Register hat.

// Beim Nulldurchgang des Zählregisters werden die LEDs eingeschaltet, bei OCR2
// wieder ausgeschaltet. Somit ist die Leuchtdauer umso größer, je höher der Wert
// im OCR2-Register ist. 0 ist dunkel, 255 ist hell.

#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>

//#define F_CPU 8000000UL // macht KAMAvr automatisch
#define colors 4 // Anzahl der Farbebenen

void cls(void); // alle LEDs in allen Ebenen aus

// Einen Bildpunkt an x, y setzen. Werte für c: 0 ist aus, 1 ist dunkel, 4 ist hell
void set(uint8_t x, uint8_t y, uint8_t c);

// Potiwerte einlesen, P2 ist Kanal 6, P3 ist Kanal 7
uint16_t readADC(uint8_t channel);

volatile uint8_t col = 0;
uint8_t x, y, z, bildspeicher[colors][15];

int main(void)
{
cli();

DDRB = 0xff;
DDRC = 0x0f;
DDRD = 0xf0;

TCCR2 = (1<<CS21) | (0<<CS20); // 8-bit Timer mit 1/8 Vorteiler
TCCR2 |= (1<<WGM21) | (1<<WGM20); // Fast PWM
TCCR2 |= (0<<COM21) | (0<<COM20); // no OC2-Pin
OCR2 = 20; // 0=dunkel, 255=hell
TIFR = (1<<OCF2) | (1<<TOV2); // Clear old flags
TIMSK |= (1<<TOIE2) | (1<<OCIE2); // overflow and compare interrupt

// A/D Conversion (aus der asuro-Lib)
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // clk/64

sei(); // Interrupts erlauben

for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, (x/3)%colors+1); // Helligkeitsstufen anzeigen
_delay_ms(2000);

DDRD |= (1<<PD2); //GND für Potis
PORTD |= (1<<PD2);
while(1)
{
z=readADC(6)/110+1;
OCR2=readADC(7)/4;
for(x=0; x<12; x++)
{
if(z==x) set(x, 0, 0); else set(x, 0, 2);
for(y=1; y<9; y++)
if(z==x-1) set(x, y, 3);
else if(z==x) set(x, y, 4);
else if(z==x+1) set(x, y, 3);
else set(x, y, 1);
if(z==x) set(x, 9, 0); else set(x, 9, 2);
}
_delay_ms(50);
}
return (0);
}

void cls(void)
{
uint8_t x, y;
for(x=0; x<15; x++)
for(y=0; y<colors; y++)bildspeicher[y][x] = 0;
}
void set(uint8_t x, uint8_t y, uint8_t c)
{
uint8_t ebene;

y = 9-y; // Koordinatennullpunkt unten links
if(y < 8) // y 9 bis 2
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][x] |= (1 << y);
else bildspeicher[ebene][x] &= ~(1 << y);
else // y 1 und 0
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][12+(x>>2)] |= (1<<((x%4)*2+(y&1)));
else bildspeicher[ebene][12+(x>>2)] &= ~(1<<((x%4)*2+(y&1)));
}
uint16_t readADC(uint8_t channel)
{
ADMUX = (1 << REFS0) | (channel & 7);// AVCC reference with external capacitor
ADCSRA |= (1 << ADSC); // Start conversion
while (!(ADCSRA & (1 << ADIF))); // wait for conversion complete
ADCSRA |= (1 << ADIF); // clear ADCIF
return(ADC);
}
SIGNAL (SIG_OUTPUT_COMPARE2)
{
PORTB &= ~0x03; // Die Pins der Displaymatrix werden auf Low gesetzt
PORTC &= ~0x0f;
PORTD &= ~0xf4; // PD2 ist GND für Potis!
}
SIGNAL (SIG_OVERFLOW2)
{
static uint8_t ebene=0;
uint16_t ledval, portb;

// Spalten
if(col) PORTB |= (1<<4); /* Danach Einsen hinterherschicken (PB4 = 1) */
else PORTB &= ~(1<<4); /* Bei der ersten Spalte eine 0 ausgeben (PB4 = 0) */
PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */
PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */

// Zeilen
ledval = bildspeicher[ebene][12+(col>>2)]; // y 1 und 0
portb = (ledval >> (col%4)*2) & 0x03;
ledval = bildspeicher[ebene][col]; // y 9 bis 2
PORTC |= ledval & 0x0f;
PORTD |= ledval & 0xf0;
PORTB |= portb;

col++;
if(col>11)
{
col=0;
ebene++;
if(ebene == colors) ebene=0;
}
}

radbruch
27.12.2010, 19:55
Hallo

Nachdem ich nun noch etwas an der Software rumgeschnitzt habe, kann sich das Ergebniss wirklich sehen lassen:

http://i2.ytimg.com/vi/EpW4n09WuZA/2.jpg (http://www.youtube.com/watch?v=EpW4n09WuZA)
http://www.youtube.com/watch?v=EpW4n09WuZA

Das sind jetzt vier deutlich unterscheidbare Helligkeitsstufen. Der Trick: Jetzt wird Timer2 im FastPWM-Mode verwendet. Das ermöglicht zwei getrennte ISR, die Overflow-ISR setzt die LEDs, die OutputCompare-ISR löscht sie wieder:


// Scrollen mit vier Helligkeitsstufen mic 27.12.2010

#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>

#define colors 4 // Anzahl der Farbebenen

volatile uint8_t ebene=0, col = 0;
uint8_t x, y, z, bildspeicher[colors][15];

// alle LEDs in allen Ebenen aus
void cls(void);

// Einen Bildpunkt an x, y setzen. Werte für c: 0 ist aus, 1 ist dunkel, 4 ist hell
void set(uint8_t x, uint8_t y, uint8_t c);

// Potiwerte einlesen, P2 ist Kanal 6, P3 ist Kanal 7
uint16_t readADC(uint8_t channel);

// WatchDog beim Initialisieren ausschalten
// https://www.roboternetz.de/phpBB2/viewtopic.php?p=531597#531597
void kill_WD(void) __attribute__((naked)) __attribute__((section(".init3")));
void kill_WD(void) { MCUSR = 0; wdt_disable(); }

int main(void)
{
cli();

DDRB = 0xff;
DDRC = 0x0f;
DDRD = 0xf0;

TCCR2 = (1<<CS21) | (0<<CS20); // 8-bit Timer mit 1/8 Vorteiler
TCCR2 |= (1<<WGM21) | (1<<WGM20); // Fast PWM
TCCR2 |= (0<<COM21) | (0<<COM20); // no OC2-Pin
OCR2 = 100; // 0=dunkel, 255=hell
TIFR = (1<<OCF2) | (1<<TOV2); // Clear old flags
TIMSK |= (1<<TOIE2) | (1<<OCIE2); // overflow and compare interrupt

// A/D Conversion (aus der asuro-Lib)
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // clk/64

sei(); // Interrupts erlauben

for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, (x/3)%colors+1); // Helligkeitsstufen anzeigen
_delay_ms(1000);

cls();
z=0;

while(1)
{
for(x=0; x<12; x++)
for(y=0; y<10; y++)
set(x, y, ((x+y+(z%12))/3)%colors+1);
_delay_ms(50);
z++;
}
return (0);
}

void cls(void)
{
uint8_t x, y;
for(x=0; x<15; x++)
for(y=0; y<colors; y++)bildspeicher[y][x] = 0;
}
void set(uint8_t x, uint8_t y, uint8_t c)
{
uint8_t ebene;

y = 9-y; // Koordinatennullpunkt unten links
if(y < 8) // y 9 bis 2
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][x] |= (1 << y);
else bildspeicher[ebene][x] &= ~(1 << y);
else // y 1 und 0
for(ebene=0; ebene<colors; ebene++)
if(c>ebene) bildspeicher[ebene][12+(x>>2)] |= (1<<((x%4)*2+(y&1)));
else bildspeicher[ebene][12+(x>>2)] &= ~(1<<((x%4)*2+(y&1)));
}
uint16_t readADC(uint8_t channel)
{
ADMUX = (1 << REFS0) | (channel & 7);// AVCC reference with external capacitor
ADCSRA |= (1 << ADSC); // Start conversion
while (!(ADCSRA & (1 << ADIF))); // wait for conversion complete
ADCSRA |= (1 << ADIF); // clear ADCIF
return(ADC);
}
SIGNAL (SIG_OUTPUT_COMPARE2)
{
OCR2 = (24<<ebene); // hihi
PORTB &= ~0x03; // Die Pins der Displaymatrix werden auf Low gesetzt
PORTC &= ~0x0f;
PORTD &= ~0xf0;
}
SIGNAL (SIG_OVERFLOW2)
{
uint8_t ledval, portb;

// Spalten
if(col) PORTB |= (1<<4); /* Danach Einsen hinterherschicken (PB4 = 1) */
else PORTB &= ~(1<<4); /* Bei der ersten Spalte eine 0 ausgeben (PB4 = 0) */
PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */
PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */

// Zeilen
ledval = bildspeicher[ebene][12+(col>>2)]; // y 1 und 0
portb = (ledval >> (col%4)*2) & 0x03;
ledval = bildspeicher[ebene][col]; // y 9 bis 2
PORTC |= ledval & 0x0f;
PORTD |= ledval & 0xf0;
PORTB |= portb;

col++;

if(col>11)
{
col=0;
ebene++;
if(ebene == colors) ebene=0;
}
}


Gruß

mic

Ein Vorkucker:
http://i3.ytimg.com/vi/z18KkJU_0Ic/2.jpg (http://www.youtube.com/watch?v=z18KkJU_0Ic) http://i2.ytimg.com/vi/IKszXFViHR8/2.jpg (http://www.youtube.com/watch?v=IKszXFViHR8) http://i3.ytimg.com/vi/ngim6DzIcGQ/2.jpg (http://www.youtube.com/watch?v=ngim6DzIcGQ)
http://www.youtube.com/watch?v=z18KkJU_0Ic
http://www.youtube.com/watch?v=IKszXFViHR8
http://www.youtube.com/watch?v=ngim6DzIcGQ

Die 0,00012 Megapixelkamera:
https://www.roboternetz.de/phpBB2/viewtopic.php?p=531996#531996

DerSchatten
31.12.2010, 15:37
Hi, kannst du vielleicht auch einen Schaltplan deines Lochrasteraufbaus online stellen. Was hast du für eine Kamera verwendet?

radbruch
31.12.2010, 16:56
Hallo

http://radbruch.bplaced.net/pingpong/pingpongadapter1_klein.jpg (http://radbruch.bplaced.net/pingpong/pingpongadapter1.jpg) http://radbruch.bplaced.net/pingpong/pingpongadapter2_klein.jpg (http://radbruch.bplaced.net/pingpong/pingpongadapter2.jpg)

Mit den Stiftleisten J1 und J4 kann man zwischen VBat oder Vcc/P1 bzw. GND oder P4 auswählen. J2 und J3 sind Anschlüsse für externe Sensoren oder Aktoren, von links nach rechts Spannung, GND und Signal.

Ursprünglich hatte ich die PingPong-Platine nur in die Verbindungsstiftleisten eingesteckt (siehe Video oben (http://www.youtube.com/watch?v=ofswO2OCq-U)). Nachdem ich aber einen Mega8 abgeschossen habe, sind die Verbindungen nun verlötet. (Trotzdem habe ich inzwischen einen zweiten Mega8 gekillt)

Die 3mm-Klinkenbuchse ist direkt mit P2, P3 und GND verbunden. Zwischen P3 (ADC7) und GND brückt ein Jumper einen Lastwiderstand (im Bild unten), wenn kein Monitor angeschlossen ist.

Ganz unten erkennt man eine achtpinnige Stiftleiste. Diese ist noch nicht angeschlossen und nur gesteckt.

Es ist die 15€-Kamera (http://www.conrad.de/ce/de/product/150001/) aus dem Mitmachprojekt und sie hängt am P3 (J2).

Gruß

mic

DE8MSH
04.01.2011, 10:27
Hallo mic,

da Du die Pong Platine ja besser kennst als der Hersteller :) folgende Frage:

ich möchte den Scroller so anpassen, dass ich zwei Platinen (oder mehr) per I2C/TWI verbinde. Ich möchte, dass die Werte, die im Prinzip dann Links Außen herausgeschoben würden auf der zweiten Platte angezeigt werden. Wie kann ich das machen? Kannst Du ein Beispiel in diesen Code von Sascha Bader implementieren:

http://www.elo-web.de/elo/mikrocontroller-und-programmierung/ping-pong/laufschrift

Ich habe mir auch sechs davon aus dem Regal genommem. 5,- ist ein Schnapper. Konnte auch schon ein wenig herumexperimentieren:

http://www.youtube.com/watch?v=3ck8w9r_Ogs

oder

http://www.youtube.com/watch?v=jk-GG6OIgl8

Ich danke Dir sehr dafür!

radbruch
04.01.2011, 14:50
Hallo

Mit I2C habe ich noch nichts gemacht und den Code des Zeichenscrollers habe ich auch noch nicht genauer untersucht. Mir ging es bisher in erster Linie um den Aufbau und die Ansteuerung der LED-Matrix.

Der einfachste Ansatz wäre wohl, jedes Modul getrennt scrollen zu lassen und nur die Darstellung zu synconisieren. Dazu müßte das hintere Modul zuerst starten und das vordere Modul müßte abwarten, bis es über eine Signalleitung den Start der Laufschrift und in Folge dann jedes einzelne Weiterscollen mitgeteilt bekommt. Wenn man dazu zwei getrennte Pins verwendet, also Eingang und Ausgang getrennt, kann man sogar mehrere Module kaskadieren. Das erste Modul könnte man z.B. mit einem Jumper am Eingang als solches definieren. Das würde für alle Module das selbe Programm ermöglichen.

Gruß

mic

radbruch
07.01.2011, 17:16
Hallo

Man kann die LED-Matrix auch als Helligkeitssensor missbrauchen:

http://i2.ytimg.com/vi/MhpReBX-s7w/2.jpg (http://www.youtube.com/watch?v=MhpReBX-s7w) http://i3.ytimg.com/vi/2_mNqzsTawU/2.jpg (http://www.youtube.com/watch?v=2_mNqzsTawU)
http://www.youtube.com/watch?v=MhpReBX-s7w
http://www.youtube.com/watch?v=2_mNqzsTawU

Vielleicht kann man das später mal nutzen. Arbeitscode der Messfunktion:


// https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=39560
// http://cs.nyu.edu/~jhan/ledtouch/index.html
uint16_t get_line(uint8_t y) // Helligkeit der Zeile y als 16Bit-Wert einlesen
{
uint16_t temp=0;

clear_line(); // Erst alle 120 Kathoden auf high
PORTB &= ~0x03; // und 120 Anoden auf low schalten
DDRB |= 0x03;
PORTD &= ~0xf0;
DDRD |= 0xf0;
PORTC &= ~0x0f;
DDRC |= 0x0f;
//_delay_ms(5); // und schlieslich alle LEDs "aufladen" (Strom?)
//asm volatile ("nop");

switch(y)
{
case 0: DDRB &= ~2; // Anoden der gewünschten Zeile auf Eingang
while(!(PINB & 2)) // warten bis Pegel high erreicht ist
temp++; // solange Zähler erhöhen
return(temp); // Messwert zurückgeben

case 1: DDRB &= ~1; while(!(PINB & 1)) temp++; return(temp);

case 2: DDRD &=~128; while(!(PIND &128)) temp++; return(temp);
case 3: DDRD &= ~64; while(!(PIND & 64)) temp++; return(temp);
case 4: DDRD &= ~32; while(!(PIND & 32)) temp++; return(temp);
case 5: DDRD &= ~16; while(!(PIND & 16)) temp++; return(temp);

case 6: DDRC &= ~8; while(!(PINC & 8)) temp++; return(temp);
case 7: DDRC &= ~4; while(!(PINC & 4)) temp++; return(temp);
case 8: DDRC &= ~2; while(!(PINC & 2)) temp++; return(temp);
case 9: DDRC &= ~1; while(!(PINC & 1)) temp++; return(temp);
default: return(0);
}
}

void clear_line(void) // setzt alle Ausgänge der Schieberegister auf high
{
uint8_t c;
PORTB |= (1<<4);
for(c=0; c<12; c++)
{
PORTB |= (1 << 3); /* PB3 = 1 (cl) */
PORTB &= ~(1 << 3); /* PB3 = 0 (!cl) */
}
PORTB |= (1 << 2); /* PB2 = 1 (str) */
PORTB &= ~(1 << 2); /* PB2 = 0 (!str) */
}


Gruß

mic

radbruch
15.01.2011, 18:41
Hallo

Eine Anfrage per PN hat mich dazu verleitet hier mal die Ansteuerung der LED-Matrix genauer zu erklären. Basis der Betrachtung ist natürlich der Schaltplan:

http://www.elo-web.de/franzis-media-stat/w250_0/ximage/0910pingpong22.jpg.png (http://www.elo-web.de/ximage/0910pingpong22.jpg) http://www.elo-web.de/franzis-media-stat/w250_0/ximage/0910pingpong23.jpg.png (http://www.elo-web.de/ximage/0910pingpong23.jpg)
(Plan und Bild von http://www.elo-web.de/elo/mikrocontroller-und-programmierung/ping-pong/schaltung-und-selbsttest)

Sowohl im Plan wie auch auf der Platine erkennt man klar die Aufteilung in 10 Zeilen und 12 Spalten. Jede Zeile ist über einen Widerstand mit einem Pin des Mega8 verbunden, jede Spalte mit einem Ausgang der Schieberegister. Die LEDs sind mit der Anode an einer Zeilenleitung angeschlossen, die Kathode hängt an einer Spaltenleitung. Eine LED leuchtet, wenn am Portpin ihrer Zeile ein High und am Schieberegister ihrer Spalte ein Low ausgegeben wird.

Weil es etwas einfacher ist, betrachten wir zuerst die Funktion der Schieberegister (4094D). Diese werden mit drei Signalen angesteuert: Daten, Clock und Strobe (eigentlich sind es vier Signale, aber Enable ist mit Vcc gebrückt und deshalb sind die Schieberegister immer aktiv. Schade eigentlich, denn so kann man den Z-State des 4094D nicht nutzen...) Die Schieberegister haben jeweils acht Ausgänge und sie sind kaskadiert (über QS an IC2). Sie verhalten sich deshalb wie ein grosses Schieberegister mit 16 Ausgängen, von denen wir aber nur 12 für die Spaltenansteuerung nutzen.

Um nun die Ausgänge der Schieberegister anzusteuern, muss man zuerst eine Art Schattenregister mit dem gewünschten Bitmuster füllen. Dazu legt man am Datenanschluß den gewünschten Pegel des Bits an, das man als nächstes in das Schattenregister schieben möchte. Dann übernimmt man mit einem High-Low-Impuls an der Clockleitung dieses Bit an die ganz linke Stelle des Schattenregisters. Zuvor werden aber alle schon im Schattenregister gespeicherten Bits um eine Stelle nach rechts verschoben. Das Bit ganz rechts fällt dabei raus. Man kann beliebig viele Bits in das Schattenregister schieben. Wenn es das gewünschte Bitmuster enthält, kann man mit deinem High-Low-Impuls auf der Strobeleitung das Bitmuster an den Ausgängen ausgeben.

Wie man am AVR die Ausgänge steuert sollte eigentlich klar sein, deshalb hier nur ein kleines Beispiel für die oberste Zeile an PC0:

// Pin auf Ausgang schalten
DDRC |= (1<<PC0);
// Auf Eingang schalten
DDRC &= ~(1<<PC0);
// Pin high
PORTC |= (1<<PC0);
// Pin low
PORTC &= ~(1<<PC0);

Soweit zur Theorie, nun steuern wir mal ein Muster an:


#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>

uint8_t c, spalte, bremsen=5;

int main(void)
{
// initialisieren
DDRB = 0x1f; // PB4 bis PB0 sind Ausgang 76543210
PORTB &= ~0x1f; // und low 0b000DCS00
DDRC = 0x0f; // PC0 bis PC3
PORTC &= ~0x0f;
DDRD = 0xf0; // PD4 bis PD7
PORTD &= ~0xf0;

// alle Ausgänge der Schieberegister auf high schalten
PORTB |= (1<<4); // Datenleitung auf high
for(c=0; c<12; c++) // Schattenregister mit Einsen füllen für 12 Spalten
{
PORTB |= (1 << 3); // Clockleitung high Schiebt das Datenbit in
PORTB &= ~(1 << 3); // Clockleitung low das Schattenregister
}
PORTB |= (1 << 2); // Strobe high gibt das Schattenregister
PORTB &= ~(1 << 2); // Strobe low an die Ausgänge

// ein Low am ersten Ausgang der Schieberegister ausgeben für erste Spalte
PORTB &= ~(1<<4); // Datenleitung auf low
PORTB |= (1 << 3); // Clockleitung high Schiebt das Low links in
PORTB &= ~(1 << 3); // Clockleitung low das Schattenregister und
PORTB |= (1 << 2); // Strobe high gibt es an die Ausgänge
PORTB &= ~(1 << 2); // Strobe low weiter.

// alle Zeilen einschalten
PORTC |= 0x0f; // Zeile 0 bis 3
PORTD |= 0xf0; // Zeile 4 bis 7
PORTB |= 3; // Zeile 8 und 9

_delay_ms(1000); // eine Sekunde leuchten

// Zeilenansteuerung ausschalten
PORTC &= ~0x0f; // Zeile 0 bis 3
PORTD &= ~0xf0; // Zeile 4 bis 7
PORTB &= ~3; // Zeile 8 und 9

// Ein High ins Schattenregister schieben. Dadurch wandert das Low nach rechts
PORTB |= (1<<4); // Datenleitung auf high
PORTB |= (1 << 3); // Clockleitung high Bits schieben
PORTB &= ~(1 << 3); // Clockleitung low
PORTB |= (1 << 2); // Strobe high und ausgeben
PORTB &= ~(1 << 2); // Strobe low

// Ausgänge für Spalte 2 setzen
PORTC |= 0b00001010;
PORTD |= 0b10100000;
PORTB |= 0b00000010;

_delay_ms(1000); // eine Sekunde leuchten

// Zeilenansteuerung ausschalten
PORTC &= ~0x0f; // Zeile 0 bis 3
PORTD &= ~0xf0; // Zeile 4 bis 7
PORTB &= ~3; // Zeile 8 und 9

// Low weiterschieben
PORTB |= (1<<4); // Datenleitung auf high
PORTB |= (1 << 3); // Clockleitung high Bits schieben
PORTB &= ~(1 << 3); // Clockleitung low
PORTB |= (1 << 2); // Strobe high und ausgeben
PORTB &= ~(1 << 2); // Strobe low

// Ausgänge für Spalte 3 setzen
PORTC |= 0b00000101;
PORTD |= 0b01010000;
PORTB |= 0b00000001;

_delay_ms(1000); // eine Sekunde leuchten

spalte=2;
while(1)
{
// Zeilenansteuerung ausschalten
PORTC &= ~0x0f; // Zeile 0 bis 3
PORTD &= ~0xf0; // Zeile 4 bis 7
PORTB &= ~3; // Zeile 8 und 9

// Low weiterschieben
if(spalte) PORTB |= (1<<4); //Nur wenn spalte 0 ist wird ein Low geschoben
else PORTB &= ~(1<<4); // sonst füllen wir mit high auf
PORTB |= (1 << 3); // Clockleitung high Bits schieben
PORTB &= ~(1 << 3); // Clockleitung low
PORTB |= (1 << 2); // Strobe high und ausgeben
PORTB &= ~(1 << 2); // Strobe low

// Muster erzeugen
if(spalte & 2)
{
PORTC |= 0b00000011; // Ausgänge für ungerade Spalten setzen
PORTD |= 0b00110000;
PORTB |= 0b00000011;
}
else
{
PORTC |= 0b00001100; // Ausgänge für gerade Spalten setzen
PORTD |= 0b11000000;
PORTB |= 0b00000000;
}
if(bremsen) _delay_ms(100*bremsen); // variable Leuchtdauer

spalte++;
if(spalte>11)
{
spalte=0;
if(bremsen) bremsen--;
}
}
return(0);
}


Das Programm beginnt mit der Initialisierung der Ports des Mega8. An Port B werden neben den Bits 0 und 1 für die Matrix auch die Bits 2 bis 4 für die Ansteuerung der Schieberegister als Ausgang definiert. Dann wird das Schieberegister mit 12 Highs gefüllt und diese ausgegeben. Dadurch werden alle Kathoden auf High geschaltet und die LEDs sind ausgeschaltet.

Nun werden am Mega8 die Ausgänge für die Zeilen mit dem Muster für die erste Spalte gesetzt, high bedeutet "LED an", low bedeutet "LED aus" für die jeweilige Zeile. Da wir bei der ersten Spalte beginnen, füllen wir zuerst ein Low in das Schattenregister und geben es aus (D=0 -> clock -> strobe). Nun leuchten die ausgewählten Bits der ersten Spalte, im Programm sind das alle. Nach einer kurzen Wartezeit schaltet das Programm alle Ausgänge des Mega8 auf Low, dadurch gehen die Leds wieder aus.

Für die zweite und jede weitere Spalte müssen wir nun ein High(D=1 -> clock -> strobe) nachschieben und ausgeben damit immer nur eine Spalte mit Low angesteuert wird. Nach dem Schieben folgt dann wieder Zeilenmuster ausgeben, warten und Zeilenmuster wieder löschen. Nach 12 mal schieben sind alle Spalten abgearbeitet und alles beginnt wieder von vorne mit Low reinschieben...

Das muss jetzt erst mal reichen. Wie man alternativ zeilenorientierte Bitmuster ausgeben kann, wie man einen Bildspeicher aufbaut, wie man das in eine ISR packt, wie man Zeichen, Werte oder Grafiken darstellen kann, beschreibe ich bei Interesse gerne in einem anderen Beitrag.

Gruß

mic

berecke
27.02.2011, 11:00
Upps. Ist da ein kleiner Fehler?

if(bremsen) _delay_ms(100*bremsen); // variable Leuchtdauer
Also meine Version von AVR Studio veträgt hier nur ein Konstante.

radbruch
27.02.2011, 14:15
Hallo

Im Grunde hat dein AVR-Studio ja recht, im Eifer des Gefechts habe ich das unterschlagen und mein KamAvr/GCC ignoriert es einfach. In util/delay.h kann man es auch nachlesen:


\note In order for these functions to work as intended, compiler
optimizations must be enabled, and the delay time
must be an expression that is a known constant at
compile-time. If these requirements are not met, the resulting
delay will be much longer (and basically unpredictable)...

Es soll ja nur gezeigt werden, dass ab einer gewissen Wiederholgeschwindigkeit der Bildaufbau im Detail nicht mehr erkennbar ist. Da kommt es auf die absolut genaue Zeit nicht an und bei bremsen <1 wird die Verzögerung sowieso wirkungslos. Sauberer (aber ungetestet) wäre es etwa so:


// if(bremsen) _delay_ms(100*bremsen);

uint8_t temp;
if(bremsen) // variable Leuchtdauer
for(temp=0; temp<bremsen; temp++)
_delay_ms(100);


Bei weiteren Versuchen mit dem Pong wird man um Interrupts nicht herumkommen. Dann kann man auf delay.h verzichten und sich die Zeitbasis in der ISR selbst erzeugen.

Gruß

mic