Liste der Anhänge anzeigen (Anzahl: 1)
Hallo
Ich werde folgendes verwenden:
WinAVR: http://sourceforge.net/projects/winavr/files/WinAVR/
KamAVR: http://www.avrfreaks.net/index.php?m...em&item_id=632
BurnOMat: http://avr8-burn-o-mat.aaabbb.de/
mySmartUSB: http://shop.myavr.de/Programmer/mySm...p.php&artID=42
myAVR ProgTool: http://shop.myavr.de/index.php?ws=do...2107_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:
Code:
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 ;)
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo
Inzwischen kann ich das Teil erfolgreich flashen:
Bild hier
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:
Code:
/*
* 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/mikrocontr...ng/laufschrift)
Gruß
mic
[Edit]
Bild hier
http://www.youtube.com/watch?v=tsTk4Un89uA
Neuer Bildspeicher mit nur 15 Bytes und set()/unset()-Funktionen. Echt ein nettes Spielzeug:
Code:
#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 */
}
Liste der Anhänge anzeigen (Anzahl: 1)
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:
Bild hier
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:
Bild hier
COM muss man an die eigenen Verhältnisse anpassen. (btw. hat auch mein HighTech-Board noch eine echte serielle Schnittstelle :)
Gruß
mic
[Edit]
Bild hier
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:
Code:
#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:
Code:
// https://www.roboternetz.de/phpBB2/vi...=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)));
}
Code:
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.
Code:
// 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;
}
}