www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Mehrstimmige Lieder mit Atmega8


Autor: Thomas Oster (thommyoster)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi
Ich möchte mit einem Atmega8, der entweder mit dem internen 1Mhz 
oszillator oder einem externen 4Mhz Quartz läuft zwei- bis dreistimmige 
Lieder (über zwei bis drei Lautsprecher (= 1 Lautsprecher pro Ton bzw 
Frequenz) spielen. Das ganz einfach indem man den Pin in der Richtigen 
Frequenz an und ausschaltet.

Tu mich allerdings mit dem Rechnen was schwer (CPU Frequenz usw...) also 
ich weiss, dass der Ton a = 440Hz ist und jeder Halbtonschritt genähert 
(temperiert) 440 * 12.Wurzel aus 2 hoch n (für n-ten Halbtonschritt) 
ist.

Bräuchte also entweder eine Art Beep_(int Freq_inHz) Funktion oder eine 
andere Möglichkeit.
Reicht der Interne oder der 4Mhz Oszillator aus? Sonst müsste ich wieder 
bestellen und ich wollte es zu Weihnachten fertig haben.

Also ich geb mich selbst gleich ans rechnen usw. aber falls jemand sowas 
schon mal gemacht hat (in ASM oder besser in C), könnt ihr mir ja mal 
ein paar Tipps geben.

Danke im Vorraus.


Übrigens (wen es interessiert): Der Plan ist aus 3 defekten 
Duracell-Hasen (ihr wisst schon, die die sich bewegen und irgendein Lied 
piepen) ein schönes Trio zu machen dh. in einem ist ein Atmega8 drin der 
über Kabel mit jew. einem Lautsprecher in jew. einem Hasen verbunden 
wird und dann "singt" jeder seine Stimme..... mal gespannt wie's klingt 
und aussieht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: thommyoster (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die schnelle Antwort.
Allerdings ist das Problem bei diesem Beispiel, dass durch die Timer 
Compare nutzung nur ein Lautsprecher funktionieren kann, da der Atmega8 
nur einen Timer hat, der diese Funktion hat.

Autor: uninteressent (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1. Wieso unbedingt mehrere Lautsprecher?
2.
>das Problem bei diesem Beispiel, dass durch die Timer
>Compare nutzung nur ein Lautsprecher funktionieren kann, da der Atmega8
>nur einen Timer hat, der diese Funktion hat.

Ja? Wie wär's mit Timer auf die kleinste gemeinsame Frequenz einstellen 
und dann runterteilen(mit Hilfsvariablen)?

Autor: Aufnehmer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die o.g. Variante kann EINE Stimme. Nimmst Du das Ganze x3 hast Du 3 
Stimmen. Der interne 1MHz-Oszillator sollte völlig ausreichen, es sei 
denn, Du willst exakt 440,0 Hz erreichen. Ansonsten ist für eine Melodie 
nur das Verhältnis der Frequenzen interessant, nicht der Absolutwert. 
Auch das a wird inzwischen meist auf 443 bis 443 Hz gestimmt, je nach 
Oboe im Orchester.. :-)

Es geht auch mit einem Mega8 und 3 Stimmen: In einer Schleife zählst Du 
3 Variablen herunter. Immer wenn eine =0 ist, schaltest Du den 
zugehörigen AusgangsPin um und lädst die Variable wieder aus einer 
Konstante (der Tonhöhe). Wenn die Variable nicht =0 ist, setzt Du einen 
Dummy-Befehl. Wichtig dabei ist, daß Deine Hauptschleife immer exakt die 
gleiche Taktzahl benötigt zum Durchlaufen. Somit hast Du einen Dreiklang 
an den 3 Ausgängen (Wenn Du sie zusammenmischt). Für den nächsten Ton 
kannst Du einen Timer verwenden, wo Du die "Konstanten" änderst. Hat 
schon 1988 mit einem Z80 so funktioniert.

Autor: Joerg Wolfram (joergwolfram)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann's auch ein bisschen komplizierter sein?

http://www.jcwolfram.de/projekte/avr/avrmusicbox/main.php

Hat zwei DDS-Stimmen, Hüllkurven, Sequenzer und "Pseudeo 16Bit PWM" 
Ausgabe. Läuft auf Mega8(8) mit 8MHz internem Takt.
Um die zwei Stimmen zu trennen, müsste nur das Ende der Interruptroutine 
etwas umgebaut werden. Wenn Interesse besteht, schaue ich mir heute 
abend den Code nochmal an.

Gruß Jörg

Autor: Kai G. (runtimeterror)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also "Alle meine Entchen" als Sinus mit Nachhall auf einem Mega16 (8er 
geht auch) waren kein Problem.

Die Klangberechnung erfolgt ganz einfach - man nehme:
Für jeden Kanal eine 16-Bit-Variable für die Phase
Für jeden Kanal eine 16-Bit-Variable für die Frequenz
Für jeden Kanal eine 16-Bit-Variable für die Dauer des Tons
Ein Zeiger auf eine Tabelle mit Frequenz- und Dauerwerten
Ein Timerinterrupt, der folgendes tut:
- Temp = 0
- für alle Kanäle in i
    - Tondauer[i] verringern
    - Wenn <0 (oder == 0, wenn du alles richtig machst)
        - Tondauer[i] = Tondauer aus Tabelle, Zeiger++
        - Frequenz[i] = Frequenz aus Tabelle, Zeiger++
    - Ende Wenn
    - Phase[i] += Frequenz[i]
    - Signed-Bit (MSB) in Temp schieben: Temp <<= 1; Temp |= ((Phase[i] >= 128) ? 1 : 0) ... oder so
- nächster Kanal
- PORTx = Temp (Bit 0 ist Kanal 3, Bit 1 ist Kanal 2, Bit 3 ist Kanal 1)
Wenn ich nichts auf die Schnelle vergessen habe, dann sollte das ohne 
Weiteres gehen. Ist nur Pseudocode! Ich habe ein paar Details außer Acht 
gelassen.

Die 'Frequenzen' kannst du dir einfach ausrechnen.

Wie du die Melodietabelle aufbaust, ist dir überlassen... wahrscheinlich 
sind drei einzelne Tabellen einfacher. So wie oben beschrieben wäre der 
Aufbau der Tabelle recht schwierig. Auch das Ende der Tontabelle ist im 
obigen Algorithmus nicht berücksichtigt. Stille erzeugst du mit einer 
Phase von 0, wobei du den Lautsprecher dann kapazitiv koppeln solltest 
um nicht zu viel Strom zu verbraten.

Sollte sogar in C locker zu schaffen sein.

Das Ganze geht auch ohne Timer einfach in einer Schleife, aber du musst 
dann für eine konstante Schleifendauer sorgen, was in C meines Wissens 
nach prinzipbedingt nicht geht.

Ich habe jetzt mal ein Rechtecksignnal vorgesehen, kannst du ja einfach 
glätten - Sinus ist minimal schwieriger (einfach die Phase als 
Lookup-Wert für eine Sinus-Tabelle verwenden), sollte aber dann über ein 
R2R-Netzwerk ausgegeben werden, was für einen Mega8 recht viele Ports 
verbraucht. Ausgabe mittels PWM sehe ich aber nicht mehr vor 
Weihnachten, wenn überhaupt sinnvoll möglich ;)

Der Sinus würde auch die Lautstärkeregulierung und damit einen einfachen 
Nachhall erlauben (Lookup-Wert einfach mit Tondauer[i] multiplizieren - 
exponentieller Lautstärkeabfall ist zu aufwändig für den Nutzen) - dann 
wird's mit C evtl. knapp.

Hoffe, das hilft die ein wenig weiter und ich bin mal gespannt, was da 
am Ende rauskommt ;)

Gruß

Kai

Autor: Kai G. (runtimeterror)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Stille erzeugst du mit einer Phase von 0, ...

Lötzinn! Die Frequenz (Winkelgeschwindigkeit) muss 0 sein, die Phase und 
damit die Elongation bleiben dann konstant.

Autor: Messknecht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachhall? Du meinst wohl Nachklang(Sustain)?

Ein Hall(Nachhall) ist äuserst komplex mit zufälliger 
Frequenz/Amplituden- und Phasenmodolation.

Autor: Thomas Oster (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also so kompliziert muss es garnicht sein. Mir würde eine einfache 
Rechteckspannung vollkommen ausreichen.

Auch auf die ganze Tontabelle würd ich gerne verzichten. Am besten wär 
(zumindest für mein Projekt, da es relativ schnell gehen muss) einfach 
eine Funktion
void Beep(double Frequenz1, double Frequenz2, double Frequenz3, int DauerMS)
{
    /*Blockierende Routine (wie die WindowsAPI Funktion Beep, 
nur dass für jeden der drei Pins eine Frequenz angegeben wird)
, die auf den Pins die entsprechende Rechteckspannung erzeugen.*/
}
//bzw.
void Toggle(int CPUTakte1, int CPUTakte2, int CPUTakte3, int DauerMS)
{
    /*Blockierende Routine, die eine Rechteckspannung mit der
     Periodendauer entspr der CPUTakte erzeugt*/
}
Komm nur mit dem Inline-asm und volatile usw noch nicht so gut klar, 
sonst hätte ich es bestimmt schon selbst geschafft (bin ja nicht faul, 
hab im Moment nur leider wenig Zeit)

Ist so zwar mühsam die Lieder einzutippen aber das ist kein Problem.
Ich weiss, es ist nicht sonderlich elegant... aber das kann man ja 
später noch alles machen.

Hab mir die Frequenzen, die entsprechenden Prozessortaktzahlen bei 
gegebener CPU-Frequenz (1 bzw 4 Mhz) und die daraus resultierende 
Abweichung von der eigentlichen Frequenz schon in eine schöne 
Exceltabelle gebaut, die ich leider grad nicht zur Hand habe.. (ich nehm 
A trotzdem als 440Hz, auch wenn es die Orchester anders machen.... )
Hab bei 1 Mhz nur Abweichungen < 0.07%, also für das Projekt mehr als 
ausreichend.

Könnte mir jemand evtl diese Routine fertig machen, der etwas fit mit 
Inline-Asm bzw. den Taktzyklen der einzelnen Befehle ist?

Danke im Vorraus und schon mal Frohe Weihnachten.

PS: Großes Lob an die Community.  Ihr macht es für Einsteiger richtig 
leicht reinzukommen usw.

Autor: Kai G. (runtimeterror)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jaja... Nachklang...

Aber wenn wir eh gerade eh gerade im Klugscheißermodus sind ;)

>Ein Hall(Nachhall) ist äuserst komplex mit zufälliger
>Frequenz/Amplituden- und Phasenmodolation.

- komplex ist das eigentlich nicht. Das Originalsignal kann einfach mit 
der Raumimpulsantwort gefaltet werden... ist nur in der vollen 
Ausprägung sehr rechenintensiv.
- zufälliger ist daran eigentlich auch nichts - lässt sich alles 
berechnen
- Frequenzen werden da meines Wissens nach nicht moduliert

Eine einfache Umsetzung passt auch in einen Mikrocontroller: Ringpuffer 
mit einfacher arithmetischer Glättung. Ach ja: Nachklang ist übrigens 
nur ein Spezialfall des Nachhalls.

Einen hab ich noch: "Modulation", nicht "Modolation"

Quitt? ;)


@Thomas Oster
Bin leider zeitlich auf zu kurz angebunden um dir das mal eben 
runterzutippen... zudem arbeite ich bis jetzt nur in Assembler - mit 
C-Code kann ich nicht dienen.

Aber wo genau ist dein Problem den obigen Pseudocode zu übersetzen?
Das Mit der Tonkalibierung kannst du später machen - Hauptsache das 
tutet erstmal.

Die Tonhöhen kannst du auch pi mal Daumen machen. Hauptsache die 
Relationen zwischen den Frequenzen stimmen (Faktor ungefähr! 
1,0594630943592952645618252949463 also 2^1/12) - aber das scheinst du ja 
eh schon zu wissen ;)

bei 4 MHz soll das laufen?

Ach ja... die zweite Variante deiner Funktion ist besser... double 
kannst du niemals in der notwendigen Geschwindigkeit verarbeiten!

Gruß Kai

Autor: eProfi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie wäre es denn, wenn Du mal die große Foren-Suche aktivieren würdest?
http://www.mikrocontroller.net/search

Stichworte wie
Ton*, Sinus*, Mehrton*, DTMF, DDS*, Musik*, Midi*
bringen mehr als genug Treffer. Und das ganze von 2 auf 4-stimmig 
auszubauen dürfte doch zu schaffen sein.

Da gibt es doch diese Erweiterungsmodule für Soundkarten, das sind 
komplette Midi-Synthesizer, die sehr einfach anzusteuern sind.

Autor: Kai G. (runtimeterror)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, da ja schon fast Weihnachten ist:

Lass dir das nicht zur Gewohnheit werden!
.include "m8def.inc"

; Der Port muss logischer Weise als Ausgang definiert werden.

.equ DURATION_DECREMENT = 1

.equ AUDIO_PORT = PORTD ; Bitte Ausgabeport korrekt einstellen!
.equ AUDIO_IO_1 = PIND0 ; Bitte angeben, an welchem Pin der erste Speaker hängt
.equ AUDIO_IO_2 = PIND1 ; Bitte angeben, an welchem Pin der zweite Speaker hängt
.equ AUDIO_IO_3 = PIND2 ; Bitte angeben, an welchem Pin der dritte Speaker hängt


; Übergabeparameter - jeweils uint16
.def frequency1Low_reg = r16
.def frequency1High_reg = r17
.def frequency2Low_reg = r18
.def frequency2High_reg = r19
.def frequency3Low_reg = r20
.def frequency3High_reg = r21
.def durationLow_reg = r22
.def durationHigh_reg = r23

; Zwischenwerte
.def phase1Low_reg = r0
.def phase1High_reg = r1
.def phase2Low_reg = r2
.def phase2High_reg = r3
.def phase3Low_reg = r4
.def phase3High_reg = r5
.def portMask_reg = r6
.def toggleMask1_reg = r24
.def toggleMask2_reg = r25
.def toggleMask3_reg = r26 ; Nimm nach Möglichkeit einen anderen Register - kollidiert mit XL
.def delay_reg = r27 ; Nimm nach Möglichkeit einen anderen Register - kollidiert mit XH


; Testinitialisierung (muss durch deine Übergabeparameter befüllt werden)

.equ TEST_FREQUENCY_1 = 100 << 1
.equ TEST_FREQUENCY_2 = 440 << 1
.equ TEST_FREQUENCY_3 = 10000 << 1
.equ TEST_DURATION = 65535

ldi frequency1Low_reg, low(TEST_FREQUENCY_1)
ldi frequency1High_reg, high(TEST_FREQUENCY_1)
ldi frequency2Low_reg, low(TEST_FREQUENCY_2)
ldi frequency2High_reg, high(TEST_FREQUENCY_2)
ldi frequency3Low_reg, low(TEST_FREQUENCY_3)
ldi frequency3High_reg, high(TEST_FREQUENCY_3)
ldi durationLow_reg, low(TEST_DURATION)
ldi durationHigh_reg, high(TEST_DURATION)

; <<<<<<<<<<<<<<<<<< START >>>>>>>>>>>>>>>>>>>>

; Phasen auf 0 initialisieren (ist eigentlich egal womit die starten, aber ordentlicher)

cli ; Alle Interrupts aus

clr phase1Low_reg
clr phase1High_reg
clr phase2Low_reg
clr phase2High_reg
clr phase3Low_reg
clr phase3High_reg

; alte I/O-Settings einlesen
in portMask_reg, AUDIO_PORT

; Toggle-Masken setzen
ldi toggleMask1_reg, 1 << AUDIO_IO_1
ldi toggleMask2_reg, 1 << AUDIO_IO_2
ldi toggleMask3_reg, 1 << AUDIO_IO_3

soundLoop:

  ; Verzögerung, damit die Hauptschleife exakt 61 Takte braucht
  ldi delay_reg, 14
  delay:
    subi delay_reg, 1
  brne delay
  nop
  nop

  ; Phase 1 weiterzählen und bei Überlauf den zugehörigen I/O umschalten (toggle)
  add phase1Low_reg, frequency1Low_reg
  adc phase1High_reg, frequency1High_reg
  brcc noToggle1
    eor portMask_reg, toggleMask1_reg
  noToggle1:

  ; Phase 2 weiterzählen und bei Überlauf den zugehörigen I/O umschalten (toggle)
  add phase2Low_reg, frequency2Low_reg
  adc phase2High_reg, frequency2High_reg
  brcc noToggle2
    eor portMask_reg, toggleMask2_reg
  noToggle2:

  ; Phase 3 weiterzählen und bei Überlauf den zugehörigen I/O umschalten (toggle)
  add phase3Low_reg, frequency3Low_reg
  adc phase3High_reg, frequency3High_reg
  brcc noToggle3
    eor portMask_reg, toggleMask3_reg
  noToggle3:

  ; Den Lautsprechern etwaige Änderungen mitteilen
  out AUDIO_PORT, portMask_reg

  ; while (--duration > 0)
  subi durationLow_reg, low(DURATION_DECREMENT)
  sbci durationHigh_reg, high(DURATION_DECREMENT)
brne soundLoop

sei ; Es darf wieder unterbrochen werden

; <<<<<<<<<<<<<<<<<< ENDE >>>>>>>>>>>>>>>>>>>>

die:
  rjmp die;

Nicht wirklich schwer, oder?

Ist jetzt nur im Simulator getestet, tut aber was es soll.
Der Code kann nicht abstürzen.

Als Dauer einen Wert von 0 - 65535 eintragen:
- 1 -> 15,26 µs (1 / 65536)
- n -> (n / 65536) s
- 0 = 1 s

Die Frequenzen musst du verdoppeln. Also 880 für 440 Hz. Im Anhang habe 
ich kurz die Werte für die temperierte Tonskala mit A' = 440 Hz 
angegeben. Ist definitiv genau genug für deine Fälle.

Das Script verlässt sich auf dir 4 MHz! Bei 2 MHz werden alle Töne um 
eine Oktave tiefer.

Beim Einbau in C kann ich dir leider nicht helfen, aber ich denke, dass 
das kein Problem ist. Ggf. musst du alle Register retten und so'n Kram.

Ich kalibriere jetzt meinen Adventskalender...

Autor: Kai G. (runtimeterror)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine Rückmeldung wäre nicht schlecht, danke!

Autor: Thomas Oster (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, Also erstmal ein riesen Dankeschön für den Code, er funktioniert 
super. Bin gerade dabei die Sachen fertig zu bauen evtl kann ich nachher 
ein Video reinstellen.
Hat leider nicht vor Weihnachten geklappt.... aber dann gibt's das 
Geschenk halt zu Neujahr.
Hab mir überlegt, die Sache erstmal in ASM zu lassen, aber den Code in C 
einzubauen dürfte auch nicht so lange dauern...
Naja, also ich meld mich sobald ich fertig bin.
Dankeschön

Autor: Christoph H. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch mehr Methoden, zweistimmige Lieder zu erzeugen:

http://www.roboterclub-freiburg.de/asuro/software/...
( Hier werden 2 Motoren als Lautsprecher benutzt, man kann das Signal 
aber genausogut addieren und als PWM ausgeben. Es wird übrigens nur 1 
Timer für das gesammte Systemtiming verwendet
)

http://www.roboterclub-freiburg.de/atmega_sound/at...
( 3 stimmiger Synthesizer )

Autor: Paul Baumann (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Christoph H.
Das gefällt mir sehr gut! (Der 2.Link) Nur eine Frage, ich will den 
"Fred" nicht shanghaien: Ich würde gerne nur einen Dreiklanggong
für meine Eisenbahnanlage haben wollen, reicht es dafür, in der Datei
"sound.c" die anderen Sounds rauszuwerfen und das Ganze neu zu 
übersetzen?
(Die Sprache C ist nicht mein Gebiet)
MfG Paul

Autor: Maximilian K. (laplace)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Christoph H.

lol
Der ATmega SID ist genial. Vor allem der minimalistische Aufbau mit dem 
ATmega als plattgefahrenem "Hundertfüßler" kann schon fast als moderne 
Kunst bezeichnet werden. Und euer Roboterclub ist auch eine schöne Idee. 
lob Wenn ich mehr Zeit hätt...

Autor: Christoph H. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Maximilian
>Und euer Roboterclub ist auch eine schöne Idee.
>Wenn ich mehr Zeit hätt...

Vielen Dank, das höre ich gerne. Die Robterclubs sind halt das, was vor 
25 Jahren die ersten Computerclubs waren ...

>Vor allem der minimalistische Aufbau mit dem
>ATmega als plattgefahrenem "Hundertfüßler

Wobei ich allerdings zugeben muß, dass ich einen ähnlichen Aufbau vorher 
schon mal gesehen habe: nämlich einen Atmega, 2xAA Batterie und 2 LEDs ( 
keine Vorwiderstände )

@Paul
>reicht es dafür, in der Datei
>"sound.c" die anderen Sounds rauszuwerfen und das Ganze neu zu
>übersetzen?

Ja, der sound ist vollsändig in sound.c definiert. Mit den dort 
eingeführten #defines sollte es einfach sein, eine eigene Melody zu 
erzeugen.

Autor: Paul Baumann (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Christoph H.
Danke für die Auskunft. Na, da wollen wir mal sehen, was wir hören. ;-)

MfG Paul

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.