Forum: Mikrocontroller und Digitale Elektronik Melodieklingel mit PWM


von Bacchus (Gast)


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)


Lesenswert?

Mein Code sieht so aus:
1
//Türklingel mit 100 Melodien M. Baudisch 12/2008
2
3
#include <inttypes.h>    // uint8_t usw.
4
#include <avr/io.h>
5
#include <avr/interrupt.h>  // Interrupts
6
#include <avr/eeprom.h>
7
#include <avr/pgmspace.h>
8
#include <avr/sleep.h>
9
#include <util/delay.h>    // definiert _delay_ms und _delay_loop_2 
10
11
#include "lied.h"           //Melodien, Offsets und Tonhöhen
12
13
#define     SPEAKER         (1<<PB1)
14
#define     TASTE           (1<<PB2)
15
#define     SOUNDON         TCCR1=(0<<CTC1)|(1<<PWM1A)|(1<<COM1A0)|(1<<CS10)|(0<<CS11)|(0<<CS12)
16
17
18
#define     SOUNDOFF        TCCR1=0
19
20
//TCCR1 = (1<<CTC1) |           Timer wird nach erreichen des Vergleichswertes 
21
//                              OCR1A rückgesetzt OCR1A -> Frequenz
22
//        (1<<COM1A0) |         Output (OC1A/B1) wird getoggled
23
//        (1<<CS10)|(1<<CS11)|(1<<CS12);  //Vorteiler 64
24
25
26
//Initialisierungswert des Zufallsgenerators ist EEPROM Speicherstelle
27
28
uint8_t EEMEM seed;
29
30
//Vars in Registern, spart Platz
31
32
volatile register uint16_t soundtime  asm("r14");
33
34
register uint16_t liedcount    asm("r4");  
35
register uint16_t liedende     asm("r6");
36
register uint8_t liednr        asm("r8");
37
register uint8_t speed         asm("r9");
38
register uint8_t dauer         asm("r10");
39
register uint8_t hoehe         asm("r11");
40
register uint8_t note          asm("r12");
41
register uint8_t i             asm("r13");
42
43
const unsigned char sinus[256] = {133,136,139,142,
44
                            145,149,152,155,
45
                    158,161,164,167,
46
                    169,172,175,178,
47
                    181,184,186,189,
48
                    192,194,197,200,
49
                    202,205,207,209,
50
                    212,214,216,218,
51
                    220,222,224,226,
52
                    228,230,232,233,
53
                    235,237,238,240,
54
                    241,242,243,245,
55
                    246,247,248,248,
56
                    249,250,251,251,
57
                    252,252,252,253,
58
                    253,253,253,253,
59
                    253,253,252,252,
60
                    252,251,251,250,
61
                    249,248,248,247,
62
                    246,245,243,242,
63
                    241,240,238,237,
64
                    235,233,232,230,
65
                    228,226,224,222,
66
                    220,218,216,214,
67
                    212,209,207,205,
68
                    202,200,197,194,
69
                    192,189,186,184,
70
                    181,178,175,172,
71
                    169,167,164,161,
72
                    158,155,152,149,
73
                    145,142,139,136,
74
                    133,130,127,124,
75
                    121,118,115,112,
76
                    109,105,102,99,
77
                    96,93,90,87,
78
                    85,82,79,76,
79
                    73,70,68,65,
80
                    62,60,57,54,
81
                    52,49,47,45,
82
                    42,40,38,36,
83
                    34,32,30,28,
84
                    26,24,22,21,
85
                    19,17,16,14,
86
                    13,12,11,9,
87
                    8,7,6,6,
88
                    5,4,3,3,
89
                    2,2,2,1,
90
                    1,1,1,1,
91
                    1,1,2,2,
92
                    2,3,3,4,
93
                    5,6,6,7,
94
                    8,9,11,12,
95
                    13,14,16,17,
96
                    19,21,22,24,
97
                    26,28,30,32,
98
                    34,36,38,40,
99
                    42,45,47,49,
100
                    52,54,57,60,
101
                    62,65,68,70,
102
                    73,76,79,82,
103
                    85,87,90,93,
104
                    96,99,102,105,
105
                    109,112,115,118,
106
                    121,124,127,127};
107
108
// Interruptroutine ohne Inhalt zum Aufwachen aus dem Sleep
109
EMPTY_INTERRUPT(PCINT0_vect);
110
111
112
ISR(TIM0_COMPA_vect) {    //Interrupt Zeitsteuerung
113
114
  if (soundtime) 
115
  {
116
    soundtime--;
117
    OCR1A=sinus[i++];
118
  }
119
}
120
121
122
//Zufallszahlengenerator
123
124
#define MYRAND_MULTIPLIER  17
125
#define MYRAND_CONSTANT    37
126
127
128
uint8_t g_nLastRandom;
129
130
131
uint8_t MyRandGet()
132
{
133
  //copy into local variables to avoid 16-bit integer expansion
134
  uint8_t Multiplier = MYRAND_MULTIPLIER;
135
  uint8_t Constant = MYRAND_CONSTANT;
136
137
  //implicit %256 by 8-bit datatype overflow
138
  g_nLastRandom = (g_nLastRandom * Multiplier + Constant);
139
  
140
  return g_nLastRandom % ANZ;
141
}
142
 
143
144
145
146
int main()
147
{
148
149
PORTB = TASTE; //Pull up an
150
151
//Timer 0 initialisieren
152
153
TCCR0B=(0<<CS01)|(0<<CS01)|(1<<CS00); //Vorteiler 1024 
154
TCCR0A=(1<<WGM01); //CTC Mode
155
GTCCR = (0<<PWM1B) | (1<<COM1B1)|(0<<COM1B0);
156
157
158
TIMSK = (1 << OCIE0A); //Interrupts auf Comp Wert A zulassen
159
OCR0A=155; //ergibt ca. 20ms bzw. 50Hz
160
161
PCMSK = (1<<PCINT2); //Pichange an PB2 konfigurieren
162
163
GIMSK=(1<<PCIE); // Pinchange Interrupt zulassen
164
165
166
set_sleep_mode(SLEEP_MODE_PWR_DOWN);    
167
168
//Zufallsgenerator mit Seed initialisieren
169
g_nLastRandom = eeprom_read_byte(&seed);
170
171
eeprom_write_byte(&seed,MyRandGet()); //neuen Seed schreiben
172
173
// da Variablen in Registern, explizite Initialisierung nötig!
174
175
liednr=0;
176
speed=0;
177
soundtime=0;
178
OCR1C=255;
179
180
//sei(); //Interrupts ein
181
182
183
for(;;)
184
{
185
186
if (liednr) //wenn Melodie aktiv
187
{ 
188
    if (!soundtime) //wenn kein Ton oder Pause läuft
189
        {  //keine Tonzeitdauer aktiv 
190
        SOUNDOFF;     //Tonwiedergabe aus
191
        if (!speed) //wir sind am Liedanfang
192
            {
193
            liedcount=eeprom_read_word(&liedofs[liednr-1]);
194
            if (liednr==ANZ) liedende=sizeof(lied); else 
195
                                            //bei letztem Lied ist Endwert Größe des Arrays
196
            liedende=eeprom_read_word(&liedofs[liednr]); //ansonsten 1 Byte vor nächstem Lied
197
            
198
            speed=pgm_read_byte(&lied[liedcount]);
199
            DDRB  = SPEAKER; //Speaker an
200
            liedcount++;
201
           } else //mitten im lied
202
           {
203
            _delay_ms(20); // um Töne zu trennen
204
            //Ton ausgeben
205
            note=pgm_read_byte(&lied[liedcount]); //Note holen
206
            hoehe=note & 0x1f; //Tonhöhe ausmaskieren (5 Bit)
207
            dauer= zeit[(note >> 5)]; // 5 Bit nach rechts schieben und Zeit holen
208
            if (hoehe) //wenn keine Pause 
209
                    {
210
                    OCR0A=eeprom_read_byte(&ton[hoehe-1]);
211
                 
212
          SOUNDON;
213
                    }
214
215
            cli();     //da Variable vom Interrupt während der Zuweisung geändert werden könnte  
216
            soundtime=dauer*speed*255;
217
            sei();             
218
            
219
            liedcount++;
220
221
            //Lied zu Ende
222
            if (liedcount>liedende)
223
            {
224
            speed =0;
225
            liednr=0;
226
            
227
            }
228
           }
229
230
231
232
233
        } //(!soundtime)
234
235
} //liednr
236
237
238
else {
239
240
    SOUNDOFF; //falls noch Ton an
241
    
242
    DDRB  &= !(SPEAKER); //Speaker Port hochohmig
243
    sleep_mode();
244
    liednr=MyRandGet()+1;  
245
246
    }
247
248
} //for
249
250
251
} //main

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

von Nil (nilsnilss)


Lesenswert?

DDRB vergessen.

DDRB |= SPEAKER;

: Bearbeitet durch User
von Bacchus (Gast)


Lesenswert?

Nein daran liegt es leider nicht

von Nil (nilsnilss)


Lesenswert?

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

von Jens (Gast)


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)


Lesenswert?

Den Hinweis aus dem 'Liesmich.txt'
1
Fuses: CKDIV8 ausschalten!
hast du beachtet?

von Karl H. (kbuchegg)


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.
1
#define F_CPU 8000000   // aber nur wenn du CKDIV8 ausgeschaltet hast!
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
int main()
7
{
8
  DDRB = ( 1 << PB1 );
9
10
  while( 1 ) {
11
    PORTB |= ( 1 << PB1 );
12
    _delay_ms( 1 );
13
    PORTB &= ~( 1 << PB1 );
14
    _delay_ms( 1 );
15
  }
16
}

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 User
von Bacchus (Gast)


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)


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)


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)


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)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.