Forum: Mikrocontroller und Digitale Elektronik Melodieklingel mit PWM


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Bacchus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die alte Melodieklingel aus DDR Zeiten mit U880 hat schlapp gemacht. 
Also hab ich mich nach einem Ersatz hier im Forum umgeschaut und diesen 
Artikel Beitrag "Klingel mit 100 Melodien - last minute Weihnachtsgeschenk" gefunden. 
Nichts leichter als das und die ganze Sache nachgebaut.
Leider ist der Klang nicht so gut wie bei der Variante mit dem U880 :(
Woran liegt das? Das Prinzip ist doch im Grunde das gleiche oder? Ein 
Timer der nach einer gewissen Zeit also je nach benötigter Frequenz 
immer den Ausgangspin toggelt.

Wie ließe sich das ganze mit PWM realisieren?
Müsste ja im Grunde so sein das ein Interrupt regelmaßig (Tonfrequenz) 
ausgeführt wird und auf einer Sinustabelle einen Schritt weiter geht. 
Der zweite Timer nimmt den Sinuswert als Tastgrad für die PWM.

Hat da jemand ein paar C-Code Schnipsel damit ich irgendwo aufsetzen 
kann? Gerade die Einstellungen der Timer würden mich interessieren.

von Bacchus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mein Code sieht so aus:
//Türklingel mit 100 Melodien M. Baudisch 12/2008

#include <inttypes.h>    // uint8_t usw.
#include <avr/io.h>
#include <avr/interrupt.h>  // Interrupts
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <util/delay.h>    // definiert _delay_ms und _delay_loop_2 

#include "lied.h"           //Melodien, Offsets und Tonhöhen

#define     SPEAKER         (1<<PB1)
#define     TASTE           (1<<PB2)
#define     SOUNDON         TCCR1=(0<<CTC1)|(1<<PWM1A)|(1<<COM1A0)|(1<<CS10)|(0<<CS11)|(0<<CS12)


#define     SOUNDOFF        TCCR1=0

//TCCR1 = (1<<CTC1) |           Timer wird nach erreichen des Vergleichswertes 
//                              OCR1A rückgesetzt OCR1A -> Frequenz
//        (1<<COM1A0) |         Output (OC1A/B1) wird getoggled
//        (1<<CS10)|(1<<CS11)|(1<<CS12);  //Vorteiler 64


//Initialisierungswert des Zufallsgenerators ist EEPROM Speicherstelle

uint8_t EEMEM seed;

//Vars in Registern, spart Platz

volatile register uint16_t soundtime  asm("r14");

register uint16_t liedcount    asm("r4");  
register uint16_t liedende     asm("r6");
register uint8_t liednr        asm("r8");
register uint8_t speed         asm("r9");
register uint8_t dauer         asm("r10");
register uint8_t hoehe         asm("r11");
register uint8_t note          asm("r12");
register uint8_t i             asm("r13");

const unsigned char sinus[256] = {133,136,139,142,
                            145,149,152,155,
                    158,161,164,167,
                    169,172,175,178,
                    181,184,186,189,
                    192,194,197,200,
                    202,205,207,209,
                    212,214,216,218,
                    220,222,224,226,
                    228,230,232,233,
                    235,237,238,240,
                    241,242,243,245,
                    246,247,248,248,
                    249,250,251,251,
                    252,252,252,253,
                    253,253,253,253,
                    253,253,252,252,
                    252,251,251,250,
                    249,248,248,247,
                    246,245,243,242,
                    241,240,238,237,
                    235,233,232,230,
                    228,226,224,222,
                    220,218,216,214,
                    212,209,207,205,
                    202,200,197,194,
                    192,189,186,184,
                    181,178,175,172,
                    169,167,164,161,
                    158,155,152,149,
                    145,142,139,136,
                    133,130,127,124,
                    121,118,115,112,
                    109,105,102,99,
                    96,93,90,87,
                    85,82,79,76,
                    73,70,68,65,
                    62,60,57,54,
                    52,49,47,45,
                    42,40,38,36,
                    34,32,30,28,
                    26,24,22,21,
                    19,17,16,14,
                    13,12,11,9,
                    8,7,6,6,
                    5,4,3,3,
                    2,2,2,1,
                    1,1,1,1,
                    1,1,2,2,
                    2,3,3,4,
                    5,6,6,7,
                    8,9,11,12,
                    13,14,16,17,
                    19,21,22,24,
                    26,28,30,32,
                    34,36,38,40,
                    42,45,47,49,
                    52,54,57,60,
                    62,65,68,70,
                    73,76,79,82,
                    85,87,90,93,
                    96,99,102,105,
                    109,112,115,118,
                    121,124,127,127};

// Interruptroutine ohne Inhalt zum Aufwachen aus dem Sleep
EMPTY_INTERRUPT(PCINT0_vect);


ISR(TIM0_COMPA_vect) {    //Interrupt Zeitsteuerung

  if (soundtime) 
  {
    soundtime--;
    OCR1A=sinus[i++];
  }
}


//Zufallszahlengenerator

#define MYRAND_MULTIPLIER  17
#define MYRAND_CONSTANT    37


uint8_t g_nLastRandom;


uint8_t MyRandGet()
{
  //copy into local variables to avoid 16-bit integer expansion
  uint8_t Multiplier = MYRAND_MULTIPLIER;
  uint8_t Constant = MYRAND_CONSTANT;

  //implicit %256 by 8-bit datatype overflow
  g_nLastRandom = (g_nLastRandom * Multiplier + Constant);
  
  return g_nLastRandom % ANZ;
}
 



int main()
{

PORTB = TASTE; //Pull up an

//Timer 0 initialisieren

TCCR0B=(0<<CS01)|(0<<CS01)|(1<<CS00); //Vorteiler 1024 
TCCR0A=(1<<WGM01); //CTC Mode
GTCCR = (0<<PWM1B) | (1<<COM1B1)|(0<<COM1B0);


TIMSK = (1 << OCIE0A); //Interrupts auf Comp Wert A zulassen
OCR0A=155; //ergibt ca. 20ms bzw. 50Hz

PCMSK = (1<<PCINT2); //Pichange an PB2 konfigurieren

GIMSK=(1<<PCIE); // Pinchange Interrupt zulassen


set_sleep_mode(SLEEP_MODE_PWR_DOWN);    

//Zufallsgenerator mit Seed initialisieren
g_nLastRandom = eeprom_read_byte(&seed);

eeprom_write_byte(&seed,MyRandGet()); //neuen Seed schreiben

// da Variablen in Registern, explizite Initialisierung nötig!

liednr=0;
speed=0;
soundtime=0;
OCR1C=255;

//sei(); //Interrupts ein


for(;;)
{

if (liednr) //wenn Melodie aktiv
{ 
    if (!soundtime) //wenn kein Ton oder Pause läuft
        {  //keine Tonzeitdauer aktiv 
        SOUNDOFF;     //Tonwiedergabe aus
        if (!speed) //wir sind am Liedanfang
            {
            liedcount=eeprom_read_word(&liedofs[liednr-1]);
            if (liednr==ANZ) liedende=sizeof(lied); else 
                                            //bei letztem Lied ist Endwert Größe des Arrays
            liedende=eeprom_read_word(&liedofs[liednr]); //ansonsten 1 Byte vor nächstem Lied
            
            speed=pgm_read_byte(&lied[liedcount]);
            DDRB  = SPEAKER; //Speaker an
            liedcount++;
           } else //mitten im lied
           {
            _delay_ms(20); // um Töne zu trennen
            //Ton ausgeben
            note=pgm_read_byte(&lied[liedcount]); //Note holen
            hoehe=note & 0x1f; //Tonhöhe ausmaskieren (5 Bit)
            dauer= zeit[(note >> 5)]; // 5 Bit nach rechts schieben und Zeit holen
            if (hoehe) //wenn keine Pause 
                    {
                    OCR0A=eeprom_read_byte(&ton[hoehe-1]);
                 
          SOUNDON;
                    }

            cli();     //da Variable vom Interrupt während der Zuweisung geändert werden könnte  
            soundtime=dauer*speed*255;
            sei();             
            
            liedcount++;

            //Lied zu Ende
            if (liedcount>liedende)
            {
            speed =0;
            liednr=0;
            
            }
           }




        } //(!soundtime)

} //liednr


else {

    SOUNDOFF; //falls noch Ton an
    
    DDRB  &= !(SPEAKER); //Speaker Port hochohmig
    sleep_mode();
    liednr=MyRandGet()+1;  

    }

} //for


} //main

Laut Simulation tut es auch was es soll aber in echt kommt kein Ton 
raus...

von Nils F. (nilsnilss)


Bewertung
0 lesenswert
nicht lesenswert
DDRB vergessen.

DDRB |= SPEAKER;

: Bearbeitet durch User
von Bacchus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nein daran liegt es leider nicht

von Nils F. (nilsnilss)


Bewertung
0 lesenswert
nicht lesenswert
Was hast du an dem Code hier aus dem Forum verändert? Weil der aus dem 
Forum funktioniert ja problemlos.

von Jens (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bacchus schrieb:
> Das Prinzip ist doch im Grunde das gleiche oder?
Jein. Beim DDR-MELO4 wird die Frequenz noch mit einem FF halbiert. 
Dadurch erzeugt man so eine Art Mini-Akkord.

> die alte Melodieklingel aus DDR Zeiten mit U880 hat schlapp gemacht.
Und das kann man nicht reparieren?
Abgesehen vom Stromverbrauch und der Größe, kann man doch bei dem Ding 
nicht meckern...

Jens

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Den Hinweis aus dem 'Liesmich.txt'

Fuses: CKDIV8 ausschalten!
hast du beachtet?

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
> Hat da jemand ein paar C-Code Schnipsel damit ich irgendwo aufsetzen kann?

Ich würd an deiner Stelle mal kleinere Brötchen backen.
Einfach nur mittels _delay_ms den Pin toggeln lassen.
#define F_CPU 8000000   // aber nur wenn du CKDIV8 ausgeschaltet hast!

#include <avr/io.h>
#include <util/delay.h>

int main()
{
  DDRB = ( 1 << PB1 );

  while( 1 ) {
    PORTB |= ( 1 << PB1 );
    _delay_ms( 1 );
    PORTB &= ~( 1 << PB1 );
    _delay_ms( 1 );
  }
}

wenn die Hardware grundsätzlich in Ordnung ist UND die Taktfrequenz 
stimmt, dann trötet dir der µC was vor. Hört sich zwar grauenhaft an 
aber ist immerhin ein erster Funktionstest


Edit:
die 1ms kommen so zu stande.
Der Kammerton A hat eine Frequenz von 440Hz. Kehrwert davon macht rund 
0.00227. Die Hälfte davon (weil ja einmal ein UND aus eine komplette 
Schwingun ergeben) sind ungefähr, über den Daumen 1ms. D.h. den Ton 
sollte man hören und er sollte in etwa im mittleren Hörbereich liegen.

: Bearbeitet durch Moderator
von Bacchus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
@Nils: Ich habe versucht die PWM Funktionalität mit einzubauen

@Jens: Ah ok danke für die Info!
Das Problem scheint mir der EPROM zu sein. Klar könnte man einen neuen 
einsetzen aber man kann ja auch mal was neues probieren ;)

von Bacchus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Grundsätzlich nochmal: Das mit dem Pintoggeln habe ich bereits 
ausprobiert und es funktioniert wunderbar. Fuses sind alle richtig 
gesetzt. Programmcode habe ich verstanden.

Es geht mir jetzt um die Optimierung!

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Bacchus schrieb:

>
> Es geht mir jetzt um die Optimierung!

Wen interessiert Optimieiung solange ... Zitat Ereöffnungsposting
> Laut Simulation tut es auch was es soll aber in echt kommt kein Ton raus...

Wenn nichts raus kommt, brauchst du nichts optimieren sondern musst den 
Fehler finden und beheben.

FAQ: Timer


(Wobei ich persönlich als erstes mal die Registerbindung der Variablen 
aufheben würde. Ich leg mich doch nicht mit dem Compiler an, welche 
Register er nicht benutzen kann. 256 Bytes SRAM sind zwar nicht viel, 
reicht aber für die halbe Handvoll Variablen alle mal.)

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz schrieb:

> Wenn nichts raus kommt, brauchst du nichts optimieren sondern musst den
> Fehler finden und beheben.
>
> FAQ: Timer

Und azu würde ich dann trotzdem wieder bei der einfachen Variante 
anfangen und die _delay_ms Geschichte rauswerfen, den Timer 
konfigurieren und dann den alle 1ms den Pin toggeln lassen.

So führt eines zum anderen.
Du baust dann zwar das Programm im Grunde wieder neu auf, hast aber den 
Vorteil, dass du auch verstehst was du da tust.

von Bacchus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Also nochmal für dich Karl Heinz zur Erklärung:
Ich habe das Projekt aus dem anderen Mikrocontroller Beitrag genommen 
und umgesetzt. Hat alles wunderbar funktioniert. Nun finde ich aber, 
dass die Klingel noch schöner klingen könnte (Stichwort Optimierung). 
Ich habe mich also ran gemacht und das bestehende Projekt nach meinen 
Überlegungen umgeschrieben. Ich suche zurzeit also tatsächlich nur nach 
dem Fehler.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.