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.
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...
Was hast du an dem Code hier aus dem Forum verändert? Weil der aus dem Forum funktioniert ja problemlos.
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
Den Hinweis aus dem 'Liesmich.txt'
1 | Fuses: CKDIV8 ausschalten! |
hast du beachtet?
> 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
@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 ;)
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!
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.)
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.