| Weitere Artikel aus dem Elo-Magazin |
Festtagsmusik mit der Ping-Pong-Platine
von Otto Effinger
Es können über die 2 Potis für 3 verschiedene Festtage jeweils 3 passende Lieder ausgewählt werden, die dann nach Münzeinwurf über einen von der Ping-Pong-Platine ferngesteuerten MP3-Player abgespielt werden. Zur Auswahl stehen Ostern (bzw. allgemein Frühling), Geburtstag sowie Weihnachten. Während die Musik läuft, wird im Display ein zum gewählten Festtag passendes Logo angezeigt. Ein Video, auf dem das Geburtstagslied 1 abgespielt wird, kann in Youtube angesehen werden.
Zusätzlich benötigte Hardware
a) Ein zusätzlicher Taster (1-poliger Schließer), der zwischen PortD.3 (also Lötpad D3) und GND angeschlossen wird. Der benötigte Pullup-Widerstand wird im Controller intern hinzugeschaltet. Der Taster wird nur beim erstmaligen Einrichten benötigt und wird am besten intern im Ping-Pong Gehäuse so montiert, dass dieser durch Anheben des Deckels gut erreichbar ist.
Ganzen Artikel lesen...

b) MP3-Player, hier wurde das Modell DMP-200 verwendet, Hersteller Hama, Modellnummer 00055458).
c) Kopfhörer oder Mini-Aktivboxen zum Anschluss an den MP3-Player.
d) 2 Optokoppler mit 2 Vorwiderständen jeweils auf der Primärseite, um die Tasten "Play/Pause" und "Forward" des MP3-Players fernsteuern zu können. Die Tasten werden durch jeweils durch den Sekundärkreis der Optokoppler überbrückt. Die Taste Play/Pause wird über PORTC.4 und die Taste Forward über PORTC.5 des ATmega8 gesteuert.
Einrichten des MP3-Players
Damit die Fernsteuerung korrekt funktioniert, muss der MP3-Player nach dem Einschalten immer mit Lied 1 beginnen. Die Fernsteuerung führt folgende Schritte durch:
a) Power-On des MP3-Players durch langes Aktivieren der Play/Pause-Taste
b) Anwahl des gewählten Liedes durch mehrfaches Betätigen der Forward-Taste, bzw. beim ersten Lied durch Betätigen der Play/Pause-Taste
c) Nach Ablauf der Spieldauer Betätigen der Play/Pause-Taste
d) Power-Off des MP3-Players durch langes Aktivieren der Play/Pause-Taste
Einmaliges Einrichten des hier gewählten Modells DMP-200 (Hersteller Hama, Modellnummer 00055458):
a) SD-Karte mit FAT-Dateisystem formatieren
b) Einzeln (damit nachher die Abspielreihenfolge passt) die gewünschten Lieder im MP3-Format auf die SD-Karte kopieren: zuerst Lied 1-3 für Ostern (z.B. Alle Vögel sind schon da), dann Lied 4-6 für Geburtstag (z.B. Happy Birthday) und schließlich Lied 7-9 für Weihnachten (z.B. Stille Nacht). Es ist von Vorteil, wenn am Ende eines Liedes noch eine Pause von einigen Sekunden in der MP3-Datei vorhanden ist, da dann vermieden wird, dass später evtl. noch ein kurzes Stück des nachfolgenden Liedes abgespielt wird.
c) SD-Karte in MP3-Player einlegen und manuell einschalten durch langes Drücken der Play/Pause-Taste.
d) Gewünschte Lautstärke am MP3-Player manuell einstellen.
e) Während Lied 1 abgespielt wird, den MP3-Player manuell ausschalten durch langes Drücken der Play/Pause-Taste.
f) SD-Karte entnehmen, Lock-Schalter aktivieren und wieder in den MP3-Player einlegen. Normalerweise speichert der MP3-Player beim Ausschalten das zuletzt gespielte Lied sowie die Lautstärke in einer speziellen Datei auf der SD-Karte, durch den aktivierten Lock-Schalter führt er dies zukünftig jedoch nicht mehr durch. Somit wird nach Power-On immer mit dem vor dem Lock-Schalter zuletzt gewählten Lied 1 begonnen.
Abspeichern der Spieldauer der einzelnen Lieder
Bei der ersten Inbetriebnahme müssen einmalig die Spieldauern der einzelnen Lieder erfasst und gespeichert werden. Dies wird folgendermaßen durchgeführt:
a) Versorgungsspannung der Ping-Pong-Platine ausschalten.
b) Bei gedrücktem Taster die Versorgungsspannung einschalten, dadurch wird der Setup-Modus aktiviert.
c) Im Display erscheint nun der Festtag (O für Ostern, G für Geburtstag, W für Weihnachten) sowie die Liednummer (1, 2 oder 3). Mit dem linken Poti oberhalb des Münzschlitzes kann der Festtag ausgewählt werden und mit dem rechten Poti die Liednummer 1-3 des Festtags.
d) Durch Drücken des Tasters wird der MP3-Player ferngesteuert eingeschaltet, das gewählte Lied abgespielt sowie die Zeitmessung im Controller der Ping-Pong-Platine gestartet. Die abgelaufene Zeit wird als ungefährer Wert in Sekunden auf dem Display angezeigt.
e) Sobald der letzte Ton des Liedes verklungen ist, muss erneut der Taster gedrückt werden. Die gemessene Zeit wird dann im internen EEPROM des Atmega8 abgespeichert und der MP3-Player ferngesteuert abgeschaltet.
f) Im Display erscheint wieder die Anzeige des Festtags und die Liednummer. Nun kann das nächste Lied angewählt werden und so die Zeitmessung für jedes der 9 Lieder durchgeführt werden wie ab Punkt c) beschrieben.
g) Nachdem alle Spieldauern erfasst wurden, wird die Versorgungsspannung kurz aus- und dann wieder eingeschaltet, dieses mal ohne gedrückten Taster. Das je nach Stellung der beiden Potis gewählte Lied wird abgespielt und dann anschließend der Standby-Modus aktiviert. Zukünftig kann nun die Bedienung wie unter 5) beschrieben erfolgen.
Anzeige und Bedienung
Zuerst werden die Potis in die gewünschte Stellung gebracht:
Mit dem linken Poti oberhalb des Münzschlitzes kann der Festtag ausgewählt werden. Der Einstellbereich des Potis ist hierzu in 3 Teile unterteilt:
- Bereich am linken Anschlag: Ostern (bzw. allgemein Frühling)
- Mittelstellung: Geburtstag
- Bereich am rechten Anschlag: Weihnachten
Mit dem rechten Poti wird die gewünschte Liednummer 1-3 des Festtags ausgewählt. Der Einstellbereich des Potis ist hierzu ebenfalls in 3 Teile unterteilt. Durch Einwurf einer Münze werden die gewählten Einstellungen bestätigt. Im Display erscheint kurz eine Kennung des Festtags (O für Ostern, G für Geburtstag, W für Weihnachten) sowie die Liednummer (1, 2 oder 3), anschließend wird das zum Festtag passende Logo blinkend angezeigt (Osterhase, Geburtstagsgeschenk oder Weihnachtsbaum). Der MP3-Player wird ferngesteuert eingeschaltet und das gewünschte Lied angewählt. Das Logo leuchtet nun dauerhaft und das Lied wird abgespielt. Nachdem die Spieldauer abgelaufen ist, blinkt das Logo wieder und der MP3-Player wird ferngesteuert ausgeschaltet. Anschließend wird die Ping-Pong-Platine in den stromsparenden Standby-Modus versetzt, bis erneut eine Münze eingeworfen wird. Ein abgespieltes Lied kann durch Drücken des Tasters vorzeitig abgebrochen werden, der MP3-Player wird dann ferngesteuert ausgeschaltet. Dies ist für den Testbetrieb nützlich.
Quellcode
Der Code wurde mit dem C-Compiler CodeVision AVR (Version 2.04.6 Evaluation) erstellt. Diese Evaluation-Version kann für den privaten nicht-kommerziellen Gebrauch kostenlos verwendet werden und ist auf eine Codegröße von 3 kB beschränkt, was für diese Anwendung ausreicht. Das komplette Projekt ist in der ZIP-Datei enthalten (Projektdatei für CodeVison AVR ist fest.prj).
Download: Quelltext und Hexfile
Quellen/Links:
Video: http://www.youtube.com/watch?v=zFxA-tNJ9ws
C-Compiler CodeVision AVR: http://www.hpinfotech.ro/html/download.htm
/*****************************************************
Compiler : CodeVisionAVR 2.04.6 Evaluation
Chip type : ATmega8
Clock frequency : 8 MHz (int. RC-OSC)
*****************************************************/
//***************************************************
// Festtagsmusik
// auf Basis des Franzis-Pingpong
// http://www.elo-web.de/ping-pong-start
//***************************************************
#include <mega8.h>
#include <stdio.h>
#include <delay.h>
#include <sleep.h>
#define P_CLK PORTB.3
#define P_DATA PORTB.4
#define P_STROBE PORTB.2
#define PIN_COIN PIND.2
#define PULLUP_COIN PORTD.2
#define DIR_COIN DDRD.2
#define COIN_INSERTED (PIN_COIN == 0)
#define PIN_KEY PIND.3
#define PULLUP_KEY PORTD.3
#define KEY_PRESSED (PIN_KEY == 0)
#define P_PLAY PORTC.4 // Portpin for "Play/Pause" of MP3 player
#define DP_PLAY DDRC.4
#define P_FWD PORTC.5 // Portpin for "Forward" of MP3 player
#define DP_FWD DDRC.5
#define WIDTH 12 // number of fields in horizontal direction
#define HEIGHT 10 // number of fields in vertical direction
unsigned int leds[WIDTH]; // current state of each LED (organized in columns)
unsigned int ledsBlink[WIDTH]; // current blink state of each LED (organized in columns)
unsigned int iRecordTime = 0;
bit bRecordTime = 0; // 1 = record time
eeprom unsigned int eeTime[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // recorded time for each song stored in EPPROM
flash unsigned char decimals[] =
{
0x1F, 0x11, 0x1F, // 0
0x04, 0x02, 0x1F, // 1
0x1D, 0x15, 0x17, // 2
0x15, 0x15, 0x1F, // 3
0x07, 0x04, 0x1F, // 4
0x17, 0x15, 0x1D, // 5
0x1F, 0x15, 0x1D, // 6
0x01, 0x01, 0x1F, // 7
0x1F, 0x15, 0x1F, // 8
0x17, 0x15, 0x1f, // 9
0x04, 0x04, 0x04 // -
};
flash unsigned char celebration[] =
{
0x3E,0x51,0x49,0x45,0x3E, // 0
0x00,0x42,0x7F,0x40,0x00, // 1
0x42,0x61,0x51,0x49,0x46, // 2
0x22,0x41,0x49,0x49,0x36, // 3
0x18,0x14,0x12,0x7F,0x10, // 4
0x27,0x45,0x45,0x45,0x39, // 5
0x3C,0x4A,0x49,0x49,0x30, // 6
0x01,0x71,0x09,0x05,0x03, // 7
0x36,0x49,0x49,0x49,0x36, // 8
0x06,0x49,0x49,0x29,0x1E, // 9
0x3E,0x41,0x41,0x41,0x3E, // O (Ostern)
0x3E,0x41,0x49,0x49,0x3A, // G (Geburtstag)
0x7F,0x20,0x10,0x20,0x7F // W (Weihnachten)
};
flash unsigned char celeb[] = { 'O', 'G', 'W' };
flash unsigned int christmastree[] =
{
0x0080,
0x00D0,
0x00B8,
0x0094,
0x0382,
0x0001,
0x0382,
0x0094,
0x00B8,
0x00D0,
0x0080,
0x0000
};
flash unsigned int birthdaygift[] =
{
0x03F8,
0x03FB,
0x031D,
0x031E,
0x0318,
0x03F8,
0x0318,
0x031E,
0x031D,
0x03FB,
0x03F8,
0x0000
};
flash unsigned int easterbunny[] =
{
0x03E0,
0x001F,
0x0309,
0x00AE,
0x01A8,
0x0388,
0x01A8,
0x00AE,
0x0309,
0x001F,
0x03E0,
0x0000
};
// clear the whole screen, all LEDs off
void clearScreen(void)
{
unsigned char i;
for(i = 0; i < (WIDTH); i++)
{
leds[i] = 0;
}
}
// wait until key is pressed
void waitUntilKeypressed(void)
{
while(!KEY_PRESSED);
delay_ms(20);
}
// wait until key is released
void waitUntilKeyreleased(void)
{
while(KEY_PRESSED);
delay_ms(20);
}
// check if key is pressed
// return: 0=key not pressed, 1=key pressed
unsigned char keyPressed(void)
{
unsigned char result = 0;
if(KEY_PRESSED)
result = 1;
delay_ms(20);
return result;
}
// Timer 2 overflow interrupt service routine
// multiplexing of LEDs
// approx. every 1ms
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
static unsigned char count = 0;
unsigned char cValue, cValue2;
static unsigned char cBlink = 0x00;
static unsigned char cBlinkcnt = 0;
static unsigned char cRec100ms = 0;
cBlinkcnt++;
if(cBlinkcnt >= 250)
{
cBlinkcnt = 0;
cBlink = ~cBlink;
}
if(bRecordTime)
{
cRec100ms++;
if(cRec100ms >= 100)
{
cRec100ms = 0;
iRecordTime++;
}
}
else
{
cRec100ms = 0;
}
count++;
if(count >= 12)
{
count = 0;
P_DATA = 0;
}
else
{
P_DATA = 1;
}
P_CLK = 1;
P_CLK = 0;
PORTC &= (~0x0F);
PORTD &= (~0xF0);
PORTB &= (~0x03);
P_STROBE = 1;
P_STROBE = 0;
cValue = (unsigned char)leds[count] & 0x0F;
cValue2 = (unsigned char)ledsBlink[count] & cBlink;
cValue &= (~cValue2);
PORTC |= cValue;
cValue = (unsigned char)leds[count] & 0xF0;
cValue &= (~cValue2);
PORTD |= cValue;
cValue = (unsigned char)(leds[count]>>8) & 0x03;
cValue2 = (unsigned char)(ledsBlink[count]>>8) & cBlink;
cValue &= (~cValue2);
PORTB |= cValue;
}
// initialization of Timer2
void initTimer2(void)
{
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 250,000 kHz
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x03;
TCNT2=0x00;
OCR2=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x40;
}
// set controller in Powerdown mode
void setStandbyMode(void)
{
TCCR2 &= 0xF8; //stop Timer2
PORTC = 0;
PORTD = 0;
PORTB = 0;
ADCSRA &= 0x7F; // stopADC
DDRD.2 = 0;
PORTD.2 = 1;
GICR |= 0x40; //enable INT0
sleep_enable();
powerdown();
GICR &= (~0x40); //disable INT0
PULLUP_KEY = 1;
DDRD.2 = 1;
PORTD.2 = 0;
ADCSRA |= 0x80; //startADC
TCCR2 |= 0x03; //start Timer2
}
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
}
// initialization of INT0
void initInt0(void)
{
// External Interrupt(s) initialization
// INT0: disabled
// INT0 Mode: Low level
GICR &= (~0x40); //disable INT0
MCUCR &= 0xFC;
GIFR=0x40;
}
// initialization of ports
void init(void)
{
PORTB = 0;
DDRB = 0x1F;
PORTC = 0;
DDRC = 0x0F;
PORTD = 0;
DDRD = 0xF4;
PULLUP_KEY = 1;
P_PLAY = 0;
DP_PLAY = 1;
P_FWD = 0;
DP_FWD = 1;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
}
// display a decimal value
// x: decimal value (0..999)
// row: row to begin displaying value
// col: column to begin displaying value
void showDecimal(unsigned int x, unsigned char row, unsigned char col)
{
unsigned char i, j, value, divisor;
x %= 1000;
divisor = 100;
for(i = 0; i < 3; i++)
{
value = x / divisor;
for(j = 0; j < 3; j++)
{ // show decimal using stored font
leds[col++] = ((unsigned int)decimals[value*3 + j] << row);
}
leds[col++] = 0;
x = x % divisor;
divisor /= 10;
}
}
// display a char for celebration
// x: char ('0'..'9', 'O', 'G', 'W')
// row: row to begin displaying value
// col: column to begin displaying value
void showCelebrationChar(unsigned char x, unsigned char row, unsigned char col)
{
unsigned char value, j;
if(x == 'O')
value = 10;
else if(x == 'G')
value = 11;
else if(x == 'W')
value = 12;
else
value = x - '0';
for(j = 0; j < 5; j++)
{ // show char using stored font
leds[col++] = ((unsigned int)celebration[value*5 + j] << row);
}
}
#define ADC_VREF_TYPE 0x00
// initialization of ADC
void initADC(void)
{
// ADC initialization
// ADC Clock frequency: 125,000 kHz
// ADC Voltage Reference: AREF pin
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x86;
}
// Read the AD conversion result
// adc_input: ADC channel (0..7)
// return: ADC value (0..1023)
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}
// select song to be played
// return: selected song number (0...2)
unsigned char selectSong(void)
{
unsigned char x;
x = 2 - (read_adc(7) / (1023/3 + 1));
return(x);
}
// select celebration
// return: selected value (0=easter, 1=birthday, 2=christmas)
unsigned char selectCelebration(void)
{
unsigned char x;
x = (read_adc(6) / 342);
return(x);
}
#define DELAY_KEY 100
#define DELAY_NEXTKEY 100
// power-on command for MP3 player
void mp3_powerOn(void)
{
P_PLAY = 1;
delay_ms(3000); // power on delay
P_PLAY = 0;
delay_ms(500);
}
// power-off command for MP3 player
void mp3_powerOff(void)
{
P_PLAY = 1;
delay_ms(3000); // power off delay
P_PLAY = 0;
delay_ms(DELAY_NEXTKEY);
}
// play command for MP3 player
void mp3_play(void)
{
P_PLAY = 1;
delay_ms(DELAY_KEY);
P_PLAY = 0;
delay_ms(DELAY_NEXTKEY);
}
// pause command for MP3 player
void mp3_pause(void)
{
P_PLAY = 1; // Pause
delay_ms(DELAY_KEY);
P_PLAY = 0;
delay_ms(300);
}
// forward command for MP3 player
void mp3_forward(unsigned char x)
{
unsigned char i;
for(i = 0; i < x; i++)
{
P_FWD = 1;
delay_ms(DELAY_KEY);
P_FWD = 0;
delay_ms(DELAY_NEXTKEY);
}
}
// show a logo on LED display
// pLogo: pointer to logo
void showLogo(flash unsigned int *pLogo)
{
unsigned char i;
for(i = 0; i < 12; i++)
{
leds[i] = *pLogo++;
}
}
// switch on blinking for all LEDs
void blinkOn(void)
{
unsigned char i;
for(i = 0; i < 12; i++)
ledsBlink[i] = 0xFFFF;
}
// switch off blinking for all LEDs
void blinkOff(void)
{
unsigned char i;
for(i = 0; i < 12; i++)
ledsBlink[i] = 0;
}
// main routine
void main(void)
{
unsigned char c, s, index;
unsigned int t, iTime;
bit bSetup = 0;
init();
if(keyPressed())
{ // if keypressed at poweron => setup play times
bSetup = 1;
}
initTimer2();
initADC();
initInt0();
clearScreen();
#asm("sei") // Global enable interrupts
if(bSetup)
{ // if keypressed at poweron => setup play times
while(1)
{
s = selectSong(); // read selected song
c = selectCelebration(); // read selected celebration category
showCelebrationChar(celeb[c], 1, 0);
showCelebrationChar(s+1+'0', 1, 6);
if(bSetup)
{
waitUntilKeyreleased();
bSetup = 0;
}
if(keyPressed())
{
waitUntilKeyreleased();
clearScreen();
showDecimal(0, 2, 0);
index = c*3 + s;
mp3_powerOn(); // "power on"
if(index == 0) // if first song then "play"
{
mp3_play();
}
else // else "forward" to selected song
{
mp3_forward(index);
}
iRecordTime = 0;
bRecordTime = 1; // start record time
do
{
t = iRecordTime;
showDecimal(t/10, 2, 0); // show recorded time approximately in seconds
}
while(!keyPressed()); // until key is pressed
bRecordTime = 0; // stop record time
mp3_pause(); // "pause"
mp3_powerOff(); // "power off"
bSetup = 1;
eeTime[index] = iRecordTime; // store recorded time to EEPROM
delay_ms(4000); // wait until MP3 player is powered off
clearScreen();
}
}
}
while(1)
{
clearScreen();
s = selectSong(); // read selected song
c = selectCelebration(); // read selected celebration category
showCelebrationChar(celeb[c], 1, 0);
showCelebrationChar(s+1+'0', 1, 6);
delay_ms(1000);
if(c == 0) // show logo for selected celebration
showLogo(easterbunny);
else if(c == 1)
showLogo(birthdaygift);
else
showLogo(christmastree);
blinkOn();
index = c*3 + s;
mp3_powerOn(); // "power on"
if(index == 0) // if first song then "play"
{
mp3_play();
}
else // else "forward" to selected song
{
mp3_forward(index);
}
iRecordTime = 0;
bRecordTime = 1;
iTime = eeTime[index];
blinkOff();
do // loop until song is terminated
{
t = iRecordTime;
if(keyPressed())
{
waitUntilKeyreleased();
break;
}
}
while(t < iTime);
mp3_pause(); // "pause"
blinkOn();
mp3_powerOff(); // "power off"
bRecordTime = 0;
delay_ms(4000); // wait until MP3 player is powered off
blinkOff();
clearScreen();
setStandbyMode();
}
}














