| Weitere Artikel aus dem Elo-Magazin |
Ping Pong Wettbewerb
Aus Ping Pong wird Ding Dong: ein elektronischer Türgong mit Briefkastenauswertung
Taster 1 bewrikt eine Soundausgabe für den Türgong. Beim Betätigen des Tasters 2 ertönt eine Sprachausgabe. Die Soundausgabe erledigt der Atmega8 der Ping Pong Platine, indem er eine von 2 kleinen WAV-Dateien über die integrierte PWM-Einheit abspielt. Diese WAV-Dateien sind im on-chip Flash gespeichert, es wird kein zusätzlicher Speicher benötigt.
von Michael Gaus
Über 2 Taster können 2 verschiedene Sounds abgespielt werden und anschließend wird für ca. 3 Sekunden ein passendes Logo blinkend in der LED-Anzeige dargestellt.
Taster 1 bewrikt eine Soundausgabe "dingdong" für den Türgong, als Logo wird eine Glocke dargestellt. Beim Betätigen des Tasters 2 ertönt eine Sprachausgabe "Sie haben Post" und es wird ein Briefumschlag angezeigt.
Taster 2 könnte z.B. als Öffnerkontakt an der Klappe des Briefkastens angebracht werden, sodass er auslöst, sobald die Klappe angehoben wird, wenn Post eingeworfen wird. Die Soundausgabe erledigt der Atmega8 der Ping Pong Platine, indem er eine von 2 kleinen WAV-Dateien über die integrierte PWM-Einheit abspielt.
Ganzen Artikel lesen...

Diese WAV-Dateien sind im on-chip Flash gespeichert, es wird kein zusätzlicher Speicher benötigt. Ziel war es, mit möglichst wenigen externen Bauelementen auszukommen. Hier werden nur 3 zusätzliche Bauteile plus die Taster benötigt.
Zusätzlich benötigte Hardware
a) Bauteile: 1 Kondensator, 1 Widerstand, Lautsprecher.
b) 2 Taster oder je nach gewünschtem Auslösekontakt auch Optokoppler oder Relais. Diese werden an den Portpins D.3 oder D.2 (also Lötpads D3 und D2) gegen GND angeschlossen. Die benötigten Pullup-Widerstände werden im Controller intern hinzugeschaltet.
Da mit einer minimalen Anzahl von Bauelementen ausgekommen werden soll, wurde auf den eigentlich erforderlichen Tiefpass am PWM-Ausgang des ATmega8 verzichtet. Stattdessen wird über einen Elko plus Vorwiderstand direkt ein kleiner Lautsprecher angeschlossen. Zu beachten ist, dass hier kein Verstärker und keine Aktivboxen angeschlossen werden dürfen, da diese ohne Tiefpass zerstört werden könnten. Da der verwendete PWM-Ausgang OC1A (PB1) auf der Ping Pong Platine normalerweise die unterste LED-Reihe ansteuert, sollte man am besten den Widerstand R10 auslöten, damit keine weitere Belastung an diesem Pin hängt, die sich störend auf die Soundausgabe auswirkt.
Direkt nach dem Einschalten der Spannungsversorgung wird der Türgong aktiviert und danach das Logo blinkend angezeigt.
Anschließend wird der Atmega8 in den stromsparenden Standybymodus versetzt. Sobald einer der beiden Taster betätigt wird, wird der Standbymodus beendet und der entsprechende Sound abgespielt sowie das Logo angezeigt.
Wird während der Sound- / Logoausgabe der andere Taster betätigt, wird dies gespeichert und direkt anschließend der andere Sound abgespielt.
WAV-Dateien konvertieren
Die WAV-Dateien müssen folgendes Format aufweisen: RIFF-WAVE Format/PCM, 8-bit, mono und 4 kHz Samplingrate.
Der Flashspeicher des ATmega8 ist 8 kBytes groß. Für die Firmware sind 1,5 kBytes (also 1536 Bytes) reserviert, sodass für die beiden WAV-Dateien maximal 6656 Bytes zur Verfügung stehen. Das enspricht einer Gesamtspieldauer von ca. 1,6 Sekunden. Die Firmware liegt im Bereich von 0x0000-0x05FF, ab 0x0600 beginnen die WAV-Dateien. Die Speicheraufteilung sieht wie folgt aus:
0x0000-0x05FF: Firmware
0x0600: Anzahl der gespeicherten WAV-Dateien (hier 2)
0x0601-0x0602: Größe von WAV-Datei 1 (16-Bit Wert Highbyte / Lowbyte)
0x0603-0x0604: Größe von WAV-Datei 2 (16-Bit Wert Highbyte / Lowbyte)
0x0605-0x1FFF: WAV-Dateien ohne Header, hintereinander binär gespeichert
Falls die WAV-Datei noch nicht im oben genannten PCM-Format vorliegt, kann sie mit dem Freewaretool Audacity konvertiert werden. Dazu öffnet man die Datei in Audacity und wählt unten links bei "Projektrate" den Punkt "Andere" aus und tippt dann 4000 ein für 4 KHz. Außerdem kann man bestimmte Bereiche der WAV-Datei markieren und ausschneiden, z.B. Pausen am Anfang oder Ende. Um bei Stereoformat auf Mono zu wechseln, klickt man im Kästchen mit dem Dateinamen auf den kleinen Pfeil und wählt "Stereotonspur aufteilen". Anschließend kann man z.B. den rechten Tonkanal durch Klick auf das X löschen und beim linken Tonkanal durch erneuten Klick auf den kleinen Pfeil "Mono" auswählen. Bei "Bearbeiten" => "Einstellungen" muss bei "Dateiformat" als unkomprimiertes Exportformat "WAV (Microsoft 8 bit PCM) eingestellt werden. Um die Datei zu speichern markiert man den gewünschten Bereich und wählt unter "Datei" den Punkt "Exportieren als WAV" aus. Nun müsste die Datei im korrekten Format vorliegen. Im Windows Explorer kann mit einem Rechtsklick auf die Datei unter "Eigenschaften" und dann "Dateiinfo" das Format nochmals überprüfen.
Um die WAV-Dateien in das Hexfile zur Programmierung des Atmega8 zu bekommen ist ein weiteres selbstgeschriebenes Tool "wav2pong" erforderlich, das im ZIP-File enthalten ist. Das Tool entfernt die 44 Bytes großen Header der WAV-Dateien und fügt diese zu einer Binärdatei zusammen, wobei am Anfang die Anzahl der gespeicherten WAV-Dateien sowie die Größen der Dateien abgespeichert werden. Anschließend wird die Binärdatei über ein hex2bin Tool in das Intelhex-Format konvertiert und in das Hexfile, das beim Kompilieren/Linken entstanden ist ab Adresse 0x0600 eingefügt, sodass am Ende eine Hexdatei bestehend aus Firmware und WAV-Dateien entsteht, die in den Atmega8 programmiert wird. Der komplette Konvertiervorgang wird über eine Batchdatei convert.bat gestartet.
Folgende Schritte sind erforderlich:
a) In das Verzeichnis "wav2pong" (im ZIP-File enthalten) wechseln.
b) Falls die Firmware geändert wurde, muss die neu erstellte Firmware dingdong.hex in das Verzeichnis wav2pong kopiert werden.
c) Die gewünschten WV-Dateien in das Verzeichnis wav2pong kopieren.
d) Die Datei setup.ini mit einem Texteditor öffnen und bei "file1" und "file2" die Dateinamen der WAV-Dateien eintragen.
e) Batchdatei convert.bat starten. Es werden die Dateien wav.bin (enthält den Bereich mit den WAV-Dateien, so wie bei der Speicheraufteilung ab 0x0600 beschrieben) und code.hex neu erzeugt.
f) code.hex ist die Komplettdatei bestehend aus Firmware und WAV-Dateien, die dann in den ATmega8 programmiert werden kann.
Ein fertiges Hexfile mit Firmware plus den zwei WAV-Dateien "dingdong" und "post" ist im ZIP-File unter dem Namen "code.hex" bereits enthalten. Dieses kann direkt in den Atmega8 programmiert werden. Achtung: das im Unterverzeichnis "code" enthaltene Hexfile "dingdong.hex" ist nur die reine Firmware, also ohne integrierte WAV-Dateien. Es kann verwendet werden, falls eigene WAV-Dateien integriert werden sollen, die per Batchfile in diese Hexdatei angegeliedert werden wie oben beschrieben.
Download: Wav2Pong
Der Code für den ATmega8 auf der Ping Pong Platine 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 dingdong.prj).
Download: Code
/*****************************************************
Compiler : CodeVisionAVR 2.04.6 Evaluation
Chip type : ATmega8
Clock frequency : 8 MHz (int. RC-OSC)
*****************************************************/
//***************************************************
// Ding Dong
// 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 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)
register unsigned char tim1count = 0;
register flash unsigned char *pFlash;
bit bInt0 = 1;
bit bInt1 = 0;
bit bSleep = 0;
// clear the whole screen, all LEDs off
void clearScreen(void)
{
unsigned char i;
for(i = 0; i < (WIDTH); i++)
{
leds[i] = 0;
}
}
// 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;
cBlinkcnt++;
if(cBlinkcnt >= 250)
{
cBlinkcnt = 0;
cBlink = ~cBlink;
}
count++;
if(count >= 12)
{
count = 0;
P_DATA = 0;
}
else
{
P_DATA = 1;
}
P_CLK = 1;
P_CLK = 0;
PORTC &= (~0x0F);
PORTD &= (~0xF0);
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);
}
// 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 &= (~0x0F);
PORTD &= (~0xF0);
PORTB = 0;
ADCSRA &= 0x7F; // stopADC
DDRD.2 = 0;
PORTD.2 = 1; // enable pullup for INT0
PORTD.3 = 1; // enable pullup for INT1
bSleep = 1;
GICR |= 0xC0; // enable INT0 and INT1
sleep_enable();
powerdown();
bSleep = 0;
//ADCSRA |= 0x80; //startADC
TCCR2 |= 0x03; //start Timer2
}
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
bInt0 = 1;
if(!bSleep)
{
GICR &= (~0x40); // disable INT0
}
}
// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
bInt1 = 1;
if(!bSleep)
{
GICR &= (~0x80); // disable INT1
}
}
// initialization of INT0 and INT1
void initExtInts(void)
{
// External Interrupt(s) initialization
// INT0: disabled
// INT0 Mode: Low level
// INT1: disabled
// INT1 Mode: Low level
GICR &= (~0xC0); //disable INT0 and INT1
MCUCR &= 0xF0;
GIFR=0xC0;
}
// initialization of ports
void init(void)
{
PORTB = 0;
DDRB = 0x1F;
PORTC = 0;
DDRC = 0x0F;
PORTD = 0;
DDRD = 0xF0;
PORTD.2 = 1; // enable pullup for INT0
PORTD.3 = 1; // enable pullup for INT1
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
}
// show logo bell on LED display
void showLogo_1(void)
{
leds[0] = 0;
leds[1] = 0;
leds[2] = 0;
leds[3] = 64;
leds[4] = 124;
leds[5] = 126;
leds[6] = 255;
leds[7] = 126;
leds[8] = 124;
leds[9] = 64;
leds[10] = 0;
leds[11] = 0;
}
// show logo post on LED display
void showLogo_2(void)
{
leds[0] = 255;
leds[1] = 195;
leds[2] = 165;
leds[3] = 153;
leds[4] = 145;
leds[5] = 161;
leds[6] = 145;
leds[7] = 153;
leds[8] = 165;
leds[9] = 195;
leds[10] = 255;
leds[11] = 0;
}
// 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;
}
// Timer1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
tim1count++;
tim1count &= 0x07;
}
// initialize PWM with timer1 for sound generation
void initSound(void)
{
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: Fast PWM top=00FFh
// OC1A output: Non-Inv.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x81;
TCCR1B=0x09;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
TIMSK |= 0x04; // enable counter1 overflow interrupt
}
// disable timer1 for sound generation
void disableSound(void)
{
TIMSK &= (~0x04); // disable timer1 interrupt
TCCR1B = 0x00; // stop counter1
TCCR1A=0x00;
}
// play sound
// soundnumber: number of sound (1...x)
void playSound(unsigned char soundnumber)
{
register unsigned char dat;
unsigned char j;
unsigned int iSize, iStart;
pFlash = (flash unsigned char *)0x0600; // start address of wav data in flash
if( ((*pFlash) == 0xFF) || ((*pFlash) == 0x00) )
return;
if( soundnumber > (*pFlash) )
soundnumber = 1; // if less files available than soundnumber => play sound1
pFlash++;
iStart = 1 + soundnumber*2;
iSize = 0;
for(j = 0; j < soundnumber; j++)
{
iStart += iSize;
iSize = (unsigned int)(*pFlash) << 8;
pFlash++;
iSize |= (*pFlash);
pFlash++;
}
pFlash = (flash unsigned char *)0x0600;
pFlash += iStart;
while (iSize != 0)
{
dat = *pFlash++;
while(tim1count != 0x07);
tim1count |= 0x80;
OCR1A = dat; // play data
iSize--;
}
}
// activate sound
// soundnumber: number of sound (1...x)
void sound(unsigned char soundnumber)
{
TIMSK &= (~0x40); // disable timer2 interrupt (LEDs off)
initSound();
playSound(soundnumber);
while(OCR1A != 0) //fade out PWM to zero
{
OCR1A--;
delay_us(100);
}
disableSound();
PORTB.1 = 0;
TIMSK |= 0x40; // enable timer2 interrupt
}
// main routine
void main(void)
{
init();
initTimer2();
initExtInts();
clearScreen();
#asm("sei") // Global enable interrupts
while(1)
{
do
{
if(bInt0)
{
sound(1);
blinkOn();
showLogo_1();
delay_ms(3000);
blinkOff();
clearScreen();
delay_ms(100);
bInt0 = 0;
GICR |= 0x40; // enable INT0
}
if(bInt1)
{
sound(2);
blinkOn();
showLogo_2();
delay_ms(3000);
blinkOff();
clearScreen();
delay_ms(100);
bInt1 = 0;
GICR |= 0x80; // enable INT1
}
}
while(bInt0 || bInt1);
setStandbyMode();
}
}
![]() | Franzis Ping-Pong Produktart: Softwarebox 29,95 € |












