Forum: Mikrocontroller und Digitale Elektronik D/A Wandler für Audio Ausgabe


von Jimin M. (minmin)


Lesenswert?

Hallo Leute,

ich bin durch Zufall über dieses Forum gestolpert und dachte mir, 
vielleicht könnt ihr mir helfen.
Ich habe mir vor kurzem einen LPC2368 von der Firma NXP besorgt. Nun 
wollte ich wissen, wie man über den schon integrierten Lautsprecher eine 
Melodie ausgeben kann. Ich habe ein bisschen im Internet gesucht und 
herausgefunden, dass man die Noten vordefiniert (mit ihrer Frequenz) und 
dann in ein Array die Noten abspeichert und die dann ausgibt. Aber um 
das digitale in ein analoges Signal umzuwandeln, damit es auch gehört 
werden kann, muss man ja den D/A Wandler benutzen. Ich habe im 
Datenblatt danach gesucht und rausgefunden, dass er nur ein Register 
hat. Soweit so gut. Aber mein Problem jetzt, ist die Verständnis wie der 
D/A Wandler genau funktioniert. Sprich, braucht der von extern einen 
Timer, der ihn taktet, usw.
Wäre über eure Hilfe sehr dankbar.

LG

von Karl H. (kbuchegg)


Lesenswert?

Jimin M. schrieb:
> Hallo Leute,
>
> ich bin durch Zufall über dieses Forum gestolpert und dachte mir,
> vielleicht könnt ihr mir helfen.
> Ich habe mir vor kurzem einen LPC2368 von der Firma NXP besorgt. Nun
> wollte ich wissen, wie man über den schon integrierten Lautsprecher

wie ist der Lautsprecher ins System integriert?

Hängt der bereits am D/A Wandler oder ist der an einem Output Pin 
angeschlossen?

Wenn der nur an einem Pin angeschlossen ist:
du hast schon ein 'analoges' Signal. Wenn auch nur mit 2 verschiedenen 
Spannungen. WEnn du an den Portpin eine 1 ausgibt, dann schnellt die 
Lautsprecher Membran nach vorne, wenn du an den Portpin eine 0 ausgibst, 
dann geht die Lautsprechermembran nach hinten. Das reicht dann schon.
Effektiv kannst du damit nur Rechteckschwingungen erzeugen, die einen 
etwas eigenartigen Klang haben, aber soviel schöner ist eine reine 
Sinusschwingung dann auch wieder nicht.
Also: nicht künsteln, einfach machen. Um den Kammerton 'a' auszugeben, 
brauchst du 440Hz, musst also den Pin an dem der Lautsprecher hängt 880 
mal in der Sekunde umschalten. Dann hörst du auch was.

> Aber mein Problem jetzt, ist die Verständnis wie der D/A Wandler genau 
funktioniert.

Du schreibst einen Zahlenwert ins Register und am Ausgang des D/A 
Wandlers kommt die entsprechende Spannung raus. Wenn du eine sich 
verändernde Spannung haben willst (wie sie zb bei einer Schwingung 
notwendig ist), dann muss eben dein Programm den D/A Wandler im 
entsprechenden Takt mit jeweils neuen Werten versorgen.

: Bearbeitet durch User
von Michael W. (Gast)


Lesenswert?

Jimin M. schrieb:
> einen LPC2368 von der Firma NXP besorgt. Nun
> wollte ich wissen, wie man über den schon integrierten Lautsprecher

Ein LPC mit integrierten Lautsprecher??

von Jimin M. (minmin)


Lesenswert?

@Karl Heinz
Danke für die Hilfe. Also der Lautsprecher hängt schon am D/A Wandler so 
wie ich es anhand der Schematic entnehmen konnte. Man müsste jetzt den 
Pin als D/A Wandler aktivieren.
Kann ich fragen, wie du auf den Wert 880 kommst, wenn der "a" Ton 440Hz 
beträgt? Verdoppelst du dann einfach den Wert? Und wie kann ich das dann 
so oft umschalten? Weil im Register habe ich zwar "settling time" 
gelesen, aber ich habe nicht wirklich verstanden, wo ich diese Zeit denn 
nun eintragen könnte.

@Markus W.
Ja der Lautsprecher ist auf dem Board schon vorhanden, ist aber eher, 
denke ich zumindest, sowas wie ein Summer. Also recht klein und ich 
denke auch ziemlich schlechte Qualität. Aber fürs erste zum 
rumprobieren, denke ich, dass es langt :)

von Karl H. (kbuchegg)


Lesenswert?

Jimin M. schrieb:
> @Karl Heinz
> Danke für die Hilfe. Also der Lautsprecher hängt schon am D/A Wandler so
> wie ich es anhand der Schematic entnehmen konnte.

Das musst du wissen.
Hier weiß ja keiner, welches Board du benutzt.

> Kann ich fragen, wie du auf den Wert 880 kommst, wenn der "a" Ton 440Hz
> beträgt?

Eine Rechteckschwingung wechselt in einer SChwingungsperiode 2 mal die 
Polarität
1
    +------+     +------+      +----
2
    |      |     |      |      |
3
  --+      +-----+      +------+
4
5
    |<---------->|
6
     1 Schwingung
7
8
    ^      ^
9
    |      |
10
    |      +---- Wechsel von 1 auf 0
11
    |
12
    +----------- Wechsel von 0 auf 1

da in jeder Schwingung 2 Pegelwechsel vorkommen, du aber 440 
Schwingungen pro Sekunde brauchst, brauchst du daher 880 Pegelwechsel 
pro Sekunde.

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

@Karl Heinz
Das ist ein MBC2300, aber ich bin mir sicher, dass der an diesem Pin 
direkt hängt.
Ah ok jetzt hab ich es verstanden. Danke für die Hilfestellung.

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

Ich habe ein Programm geschrieben, aber irgendwie ändert er nicht die 
Tonlage, obwohl ich in einer for-Schleife die Werte in das Register 
reinschreibe. Hier mein Test-Programm:
1
//    note  periode  frequency
2
#define c   3830    //261Hz
3
#define d   3400    //294Hz
4
#define e   3038    //329Hz
5
#define f   2864    //349Hz
6
#define g   2550    //392Hz
7
#define a   2272    //440Hz
8
#define h   2028    //493Hz
9
#define C   1912    //523Hz
10
//Rest
11
#define R   0
12
13
unsigned int i = 0;
14
int melody[8]={c,d,e,f,g,a,h,C};
15
16
int main(void)
17
{
18
  PINSEL1 = 0x200000;
19
  while(1)
20
  {
21
   for(i=0; i<1024;i++)
22
    {
23
      DACR = (melody[i]<<6); //6 Bits mach links shiften da im Register VALUE von 6 bis 15
24
      
25
    }
26
27
}
28
}

Die Grenze sollte bei 8 liegen, ich weiß, aber ich hab nichts hören 
können, außer ein "Klicken". Deswegen habe ich rumgespielt und 
komischerweiße ändert sich die Lautstärke je größer die Grenze wird. Ich 
habe mich dann gefragt, wie das sein kann, denn mein Array ist ja mit 8 
Werten versehen. Wenn ich nun den Index größer habe wie 8, da steht doch 
eigentlich nichts mehr drin oder nicht?
Was mich auch wundert, dass ich keine "Melodie" raushöre. Ich mein jetzt 
nicht ein ganzes Lied oder so, aber zumindest eine Änderung der Tonlage 
sollte doch mit dieser Logik machbar sein oder? Ich schreibe ja in der 
for-Schleife jedes mal einen neuen Wert in das Register rein.
Hat jemand eine Idee?
Btw die Werte für die Defines die ich habe, habe ich im Internet 
gefunden.

von Karl H. (kbuchegg)


Lesenswert?

Jimin M. schrieb:
> Ich habe ein Programm geschrieben, aber irgendwie ändert er nicht die
> Tonlage, obwohl ich in einer for-Schleife die Werte in das Register
> reinschreibe.

Du verwechselst da was.

Die Frequenz einer Schwingung speigelt sich darin wieder, wie schnell du 
am DAC unterschiedliche Spannungen erzeugst (zb 0V und 5V).
D.h. deine Notenwerte sind nicht die Werte, die du in den DAC schreibst, 
sondern sie steuern, in welchen zeitlichen Abständen du zb 2 
unterschiedliche Werte in das DAC Register (für eine Rechteckschwingung) 
schreiben musst.

Der Unterschied zwischen dem Ton 'a' und dem Ton 'f' sieht so aus
1
'a'
2
3
Spannung ^        +-----+     +-----+     +-----+
4
         |        |     |     |     |     |     |
5
         |    ----+     +-----+     +-----+     +---
6
         |
7
          ----------> Zeit
1
'f'
2
3
Spannung ^         +---+   +---+   +---+   +---+   +---+
4
         |         |   |   |   |   |   |   |   |   |   |
5
         |      ---+   +---+   +---+   +---+   +---+   +---
6
         |
7
          ----------> Zeit

Mit dem DAC steuerst du, wie hoch die Kurve nach oben geht (die Spannung 
am Lautsprecher und damit die Auslenkung der Membrane). Je mehr du 
auslenkst (also abwechselnd 0 und einen entsprechend höheren WErt 
ausgibst) ...
1
'f'
2
3
     +---+   +---+   +---+   +---+   +---+
4
     |   |   |   |   |   |   |   |   |   |
5
     |   |   |   |   |   |   |   |   |   |
6
  ---+   +---+   +---+   +---+   +---+   +---
7
8
  ----------> Zeit
... desto lauter wird der Ton. Und natürlich kann man, indem man nicht 
einfach nur 2 fixe Werte benutzt auch andere Kurvenformen erzeugen
1
'f'
2
3
      +-+     +-+     +-+     +-+     +-+
4
     |   |   |   |   |   |   |   |   |   |
5
     |   |   |   |   |   |   |   |   |   |
6
  --+     +-+     +-+     +-+     +-+     +-
7
8
  ----------> Zeit
die sich dann anders anhören. Das Grundprinzip ist aber noch wie vor, 
dass dir die zeitliche Steuerung der Spannung obliegt.

Um ein 'a' auszugeben, muss also der DAC genau 1/880-tel Sekunde lang 5V 
erzeugen und 1/880-tel Sekunde lang 0V. Und das im ständigen Wechsel.

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

@Karl Heinz
Ok, das habe ich jetzt verstanden, aber die unterschiedlichen Spannungen 
spiegeln sich doch in den Werten wieder, die in das DAC Register 
reingeschrieben werden, oder lieg ich da falsch?
Aber wenn ich dich jetzt richtig verstanden habe, dann schreibe ich doch 
unterschiedliche Abstände rein. Aber ich höre trotzdem nur einen Ton...

von Karl H. (kbuchegg)


Lesenswert?

Jimin M. schrieb:
> @Karl Heinz
> Ok, das habe ich jetzt verstanden, aber die unterschiedlichen Spannungen
> spiegeln sich doch in den Werten wieder, die in das DAC Register
> reingeschrieben werden, oder lieg ich da falsch?

Ja, klar. Aber deswegen hast du ja noch keinen Ton

> Aber wenn ich dich jetzt richtig verstanden habe, dann schreibe ich doch
> unterschiedliche Abstände rein.

Wo genau machst du das denn in deinem Programm?

> Aber ich höre trotzdem nur einen Ton...

Aber nur zufällig.
Dein Programm ist Murks. Ds muss so aussehen
1
  für alle Noten der Melodie {
2
3
    while Dauer der Note noch nicht vorüber {
4
5
      DAC = Wert_für_5V
6
7
      warte( Zeitdauer abhängig von der Tonhöhe )
8
9
      DAC = Wert_für_0V
10
11
      warte( Zeitdauer abhängig von der Tonhöhe )
12
    }
13
  }

Dein Programm hat hauptsächlich mit zeitlicher Steuerung zu tun! Die 
Tonhöhe kommt in den zeitlichen Abständen vor, in denen der DAC mit 
jeweils einem anderen Wert bestückt wird.
Dir scheint nicht klar zu sein, was eigentlich eine Schwingung bzw. ein 
Ton ist bzw. was der DAC eigentlich macht. Der DAC ist kein Synthesizer, 
dem du eine Freuqenz vorgibst und der dann eigenständig eine Schwingung 
erzeugt. Dem DAC gibst du einen Wert und den setzt er in eine Spannung 
um. Gibst du den den Wert (Hausnummer) 1024 dann erzeugt der DAC daraus 
4.78V (ebenfalls Hausnummer). Und zwar als Gleichspannung. Wenn es sein 
muss auch ein halbes Jahr lang, ohne dass sich an den 4.78V irgendwas 
ändert. Eine Schwingung sieht aber anders aus. In einer Schwingung 
ändert sich die Spannung laufend. Bei einem 'a' und einer 
Rechteckschwingung 880 mal in der Sekunde.

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

@Karl Heinz
Stand gestern irgendwann voll auf dem Schlauch und habe nichts mehr so 
richtig verstanden. Tut mir leid, wenn ich dich genervt habe :)
Ich habe jetzt "Alle meine Entchen" programmiert. Funktioniert ganz gut. 
Ich hätte da aber eine Frage. Würde es eigentlich gehen, wenn ich eine 
MP3-Datei ausgeben möchte, über diesen Lautsprecher?

von Karl H. (kbuchegg)


Lesenswert?

Jimin M. schrieb:

> Ich hätte da aber eine Frage. Würde es eigentlich gehen, wenn ich eine
> MP3-Datei ausgeben möchte, über diesen Lautsprecher?

Im Prinzip ja.
Aber der Schritt von einem simplen Ton über eine Rechteckschwingung zu 
einer MP3 Ausgabe ist ungefähr so groß wie der Schritt vom 
Schuhband-Knoten-Machen-können zum Schutzgasschweißen-unter-Wasser.
Bis du ein MP3 dekodieren kannst, musst du noch viele Brötchen backen. 
Fang lieber erst mal mit kleinen Brötchen an, die deinen Fähigkeiten 
angepasst sind. Bis du ein MP3 auf deinen Lautsprecher rauskriegst musst 
du erst noch mindestens 2 Dutzend andere Basistechniken vorher 
beherrschen. Und wenn ich beherrschen sage, dann meine ich auch 
beherrschen. Im Schlaf beherrschen.

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

@Karl Heinz
Hab dich verstanden und nehme deine Empfehlung zu Herzen. Ich muss 
wirklich noch einiges lernen. Würdest du mir vielleicht ein paar Tipps 
geben, mit welchen Methoden ich anfangen sollte? Ich respektiere dieses 
Gebiet sehr und möchte ein guter Programmierer werden. Dazu muss ich 
einiges lernen, aber ich weiß ehrlich gesagt nicht, wo ich anfangen 
soll. Du denkst dir bestimmt, was für ein planloser Mensch, aber ich 
möchte es wirklich lernen.
Vielen Dank, dass du mir gute Tipps gegeben hast.
Schönen Sonntag

von W.S. (Gast)


Lesenswert?

Jimin M. schrieb:
> Hab dich verstanden und nehme deine Empfehlung zu Herzen.

Das klingt nach nettem Jungen - eine Seltenheit hier. Jetzt könnte ich 
mit einem Zitat kommen: "start at the beginning and go until you come to 
the end, then stop", aber das ist sicherlich nicht nützlich - genauso 
wenig, wie Ratschläge ohne die notwendige Kenntnis der Vorbedingungen.

Also: Was hast du bislang so gemacht, was hast du bereits gelesen (und 
bereits verstanden), auf welche Dinge konzentrieren sich deine 
Interessen, kurzum: was für ne Basis besteht bereits und wie kann man 
darauf aufbauen?

Nochwas zur Dudelei: Bitte unterscheide gedanklich zwischen 
Klangerzeugung und Klangwiedergabe. Für die Klangerzeugung hast du ja 
schon etwas in Richtung Rechteckerzeugung mit variabler Periodenlänge 
gemacht. Fein. Klangfarben kriegt man hingegen erst dann verändert, wenn 
man von der reinen Rechteckform weg kommt und einen Amplitudenverlauf 
generiert. Da kommt dann die Trennung zwischen Erzeugung und Wiedergabe, 
weil man sich plötzlich mit sowas wie einer Abtastrate und Samples 
konfrontiert sieht. Ja, Sprachausgabe ist auch drin, ich hatte AUCH 
DIESES (mal wieder) bei der Lernbetty vorgeturnt. Ich hatte dabei auch 
die PCM-Version von Sierra mit implementiert, was eine für simple Geräte 
geeignete Kompression ist. Von dort zu MP3 ist es jedoch noch eine 
ziemliche Strecke - insbesondere was die schiere Rechenleistung 
betrifft. Guck mal nach dem MAD-Player, aber sei nicht traurig, wenn die 
Rechenleistung des LPC2368 dafür nicht ausreicht - oder nur dann grad so 
ausreichen würde, wenn man das Ganze gekonnt in Assembler macht. 
"gekonnt" ist hier entscheidend.

W.S.

von Jimin M. (minmin)


Lesenswert?

@W.S.
Ob das selten ist, weiß ich jetzt nicht, aber ich denke, wenn man 
wirklich etwas beherrschen möchte, kommt das nicht von allein. Man muss 
schon etwas dafür tun und auch von anderen lernen.
Also ich habe Grundkenntnisse mit C und ein bisschen C#(wobei C# schon 
ziemlich lange her ist). Was für ein Level das allerdings ist, kann ich 
nicht genau sagen. Kleinere Programme geschrieben mit und ohne µC. 
Assembler habe ich auch ein bisschen gelernt, aber wirklich beherrschen 
tu ich das nicht.
Was ich so im Internet gelesen habe, war schon logisch. Also dass die 
Audio-Datei erst einmal umkodiert werden muss (WAV to C-Code) und dann 
dieses Sample ausgelesen werden muss, was dann wiederum am Lautsprecher 
wiedergegeben wird. Alles schön und gut, aber ich habe nichts gefunden, 
womit ich eine wav-Datei in einen C-Code umwandeln kann. Vielleicht habe 
ich auch falsch gesucht, kann ich jetzt nicht genau sagen und einen 
Decoder zu basteln, wäre glaub ich eine ganze Nummer zu groß, zumal ich 
nicht mal genau sagen kann, wie das ganze umgewandelt wird. Also das 
ganze Hintergrundwissen hab ich einfach noch nicht. Ich hab auch 
gelesen, dass manche z.B. einen SD-Kartenleser auf ihr Board bauen und 
damit die Dateien rausholen. Aber das würde doch auch ohne gehen oder? 
Ich mein die entsprechend umgewandelte Datei in einem Ordner 
abspeichern, worauf dann mein Programm zugreifen kann, aber ich weiß 
halt auch nicht, ob mein Speicher dafür ausreichen würde.
Ich habe mal im Internet nach dem MAD-Player gesucht. Da stand, dass es 
ein Programm ist, womit man MPEG Audio Dateien dekodieren kann. Ich 
konnte leider nicht das Programm in C-Code finden, um nachzuvollziehen, 
wie das ganze Dekodieren funktioniert. Aber wie schon vorhin erwähnt, 
ich glaub, da bin ich noch ganz klein, um sowas selber zu programmieren.
Ich wäre aber über eine Hilfestellung zum Thema Dekodieren sehr erfreut. 
Vielleicht hat ja irgendjemand ein Sample, das schon dekodiert worden 
ist, womit ich dann ein bisschen "spielen" kann. Da kann ich bestimmt 
noch eine Menge lernen.

Vielen Dank für die netten Tipps.

Gruß

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

Hallo Leute,

ich habe ein Programm geschrieben, dass mit einer festen Frequenz von 
44,1kHz die Melodie, die ich selber eingebe, abtastet. Mein erstes 
Programm, das mir "Alle meine Entchen" am Lautsprecher ausgegeben hat, 
lief mit verschiedenen For-Schleifen. Nun wollte ich das ganze mit den 
Timern programmieren, damit ich eine feste Frequenz habe. Das habe ich 
auch geschafft, aber mein Problem ist nun, dass die Noten die ausgegeben 
werden, sich ziemlich schief anhören. Für mein erstes Programm habe ich 
Werte aus dem Internet genommen (Programm in C mit einem Arduino 
http://www.arduino.cc/en/Tutorial/PlayMelody). Die Werte haben in meinem 
Programm mit den Timern nicht funktioniert, deswegen habe ich die Werte 
für die einzelnen Noten selber berechnet.
Da ich eine Abtastrate von 44,1kHz eingestellt habe, berechnete ich die 
Werte wie folgt:

44100/ 440 = 100
wobei das jetzt ein Beispiel ist mit der Frequenz von dem Ton a. Also 
die Frequenz für den Ton a lautet 440Hz.
Das habe ich dann für die anderen Noten auch gemacht. Aber die Töne 
klingen ziemlich schief.
Kann mir jemand verraten, ob ich die Berechnung richtig gemacht habe?
Wäre wirklich sehr nett.

Schönes Wochenende

von W.S. (Gast)


Lesenswert?

Jimin M. schrieb:
> Was ich so im Internet gelesen habe, war schon logisch. Also dass die
> Audio-Datei erst einmal umkodiert werden muss (WAV to C-Code) und dann
> dieses Sample ausgelesen werden muss, was dann wiederum am Lautsprecher
> wiedergegeben wird.

Nein, das ist nicht logisch.

Also: Du hast einen Mikrocontroller. Der hat RAM und ROM (zumeist 
Flash-ROM). Im ROM sollte verständlicherweise deine Firmware stehen, mit 
der du Leben in deinen Mikrocontroller bringen willst. Wenn du nun 
irgendwelche Klänge abspielen willst, dann brauchst du dafür dreierlei:
1. ne Hardware, also etwas wo es digital reingeht (z.B. DAC) und dan 
analog am Lautsprecher rauskommt.
2. irgendeine Dudel-Routine in deiner Firmware, die eine Folge von 
Samples an die o.g. Hardware ausgeben kann
3. die eigentlichen Samples deines Klanges, den du ausgeben willst.

Deine Firmware mußt du natürlich in irgend einer Programmiersprache 
schreiben, übersetzen und in den ROM deines µC's hineinlöffeln. Aber wie 
diese nun zu den Samples deines Klanges kommt, kann unterschiedlich 
sein. Zum Beispiel ganz vornehm per Wav-Datei auf einer SD-Karte, oder 
eben als Folge von Bytes oder Words irgendwo, wo deine Dudelroutine 
drauf zugreifen kann. Das kann zusammen mit deinem Programm im ROM 
stehen oder auch von einem externen Speicher (serieller Flash u.a.) 
geladen werden (sofern du sowas hast).

Es hängt von deinem Programmiergerät ab, ob du die Samples einer 
Wav-Datei separat in den ROM kriegst, oder ob du erstmal das Ganze in 
eine Quelle deiner Programmiersprache umverpackst und dann zusammen mit 
deiner Firmware in den ROM brennst.

Guck dier hier im Forum ruhig mal die Lernbetty an. Die hat keinen 
echten DAC, aber dafür kann man ja auch einen Timer im PWM-Modus 
benutzen. Ich hatte damals (glaub ich) auch ein Programm mit 
hochgeladen, womit man aus einer Wav-Datei ein Stück C-Quelle machen 
kann, so mit const byte Dudeldei[sowieso] = { ....... };

Obendrein hatte ich wohl noch einen Sierra-Kompressor engebaut, das 
spart ca. 50% Platz im ROM.

W.S.

von Jimin M. (minmin)


Lesenswert?

@W.S.
Danke für die Erklärung. Ich würde mir gerne das anschauen, was du mir 
empfohlen hast, aber ehrlich gesagt, finde ich es nicht. Hast du da 
vielleicht einen Link? Wäre sehr nett.

Gruß

von Paul B. (Gast)


Lesenswert?

Jimin M. schrieb:
> Ja der Lautsprecher ist auf dem Board schon vorhanden, ist aber eher,
> denke ich zumindest, sowas wie ein Summer.
Wenn das wirklich ein Summer ist, wird das schwer mit dem Produzieren 
der Töne meine ich.
Eventuell kannst Du den aber abklemmen und einen echten Lautsprecher 
dranhängen. Das wäre wohl das erste bevor Du beginnst, die richtigen 
Töne zu finden,

von Datenblattleser (Gast)


Lesenswert?

Das ist ein echter Lautsprecher der an einem kleinen Verstärker hängt. 
Der wiederum ist an dem LPC eigenen 10-Bit DAC angeschlossen.

von Jimin M. (minmin)


Lesenswert?

@Datenblattleser
Ja du hast Recht.

Aber kann mir evtl. jemand sagen, ob meine Berechnung für die einzelnen 
Töne richtig ist?
Wenn ich eine Abtastrate von 44,1kHz haben möchte, dann müsste doch die 
Berechnung bei dem Ton 'a' (440Hz) so lauten:

44100/440 = 100

Ich bekomme auch schon Töne raus. Also ich lass jedes mal einen Sinus 
berechnen, der dann via Timer abgetastet wird.
Die Töne klingen den Umständen zufolge ok, aber bei dem Ton 'g' und ich 
glaube 'f', klingen die total schief. Ich weiß aber nicht wirklich 
warum, da die anderen ja richtig klingen.

von Jimin M. (minmin)


Lesenswert?

Wollte nur sagen, dass ich jetzt weiß, an was es lag. Also die Töne 
wurden aufgrund des Integers so krass abgerundet, dass die Töne total 
schief klangen. Hab einfach "double" benutzt und später dann in 
"integer" umgewandelt, um es in mein DAC Register reinschreiben zu 
können.
Jetzt klingt alles besser :)

von J. S. (engineer) Benutzerseite


Lesenswert?

Jimin M. schrieb:
> Aber die Töne klingen ziemlich schief.
> Kann mir jemand verraten, ob ich die Berechnung richtig gemacht habe?
> Wäre wirklich sehr nett.
Ein Tontabelle findest Du hier, bzw kannst sie Dir innerhalb der 
Software aus dieser Vorschrift dynamisch generieren:

Freq = 440 x (196/185) hoch N (N = Halbtonschritt)

http://www.96khz.org/oldpages/musicfrequencygeneration.htm

Jetzt kommt noch Dein Timer ins Spiel:

Damit die Tonhöhen in Akkorden über die Oktaven gut genug zueinander 
passen, müssen sie jeweils auf etwa 5-10 Tausendstel genau sein.

Damit, wie in Deinem Fall, das auch bei Rechtecksignalen mit ihren 
Oberwellen noch gut passt und nicht zu "fett" wird, müssen sie auf 2-3 
Zehntausendstel genau sein. Daraus folgt, dass die Timerauflösung mal 
wenigstens 5-stellig sein sollte.

von Jimin M. (minmin)


Lesenswert?

@Jürgen S.
Danke für deine wertvollen Tipps.
Könnte ich vielleicht etwas fragen, was vielleicht nicht ganz zum Thema 
passt?
Geht es eigentlich einen Pointer mit dem Datentypen "double" zu 
deklarieren und diesen dann auf ein Array mit dem Datentypen "double" 
zeigen zu lassen? Ich "spiele" ein bisschen an meinem Programm rum und 
wollte den Array mit meiner Melodie über einen Zeiger abspielen. Also 
ich habe 2 Melodien und diese wollte ich dann abspielen lassen, wenn 
z.B. der eine Timer auslöst oder keine Ahnung, wenn eine Variable 
gesetzt wird. Ich wollte ein Array-Buffer haben in das dann meine 
gewünschte Melodie geladen wird (Je nach Situation). Hab das ganze mal 
mit der Funktion memmove() gemacht, hat gut geklappt, aber ich musste 
meinem Buffer ganz am Anfang eine feste Größe übergeben(hoffe das ist 
richtig ausgedrückt). Da ich aber Melodien mit verschiedenen Längen 
habe, habe ich meinem Buffer dem größeren Array angepasst. Jedoch wurden 
die restlichen Felder mit "0" ausgefüllt. Um das zu vermeiden (und ich 
wollte es ohne eine for-Schleife machen) habe ich gedacht vielleicht 
würde es mit einem Zeiger gehen.
Also ungefähr so:
1
const double melody_a[] = {c,d,e,f,g,g,a,a,a,a,g,a,a,a,a,g,f,f,f,f,e,e,g,g,g,g,c};
2
double melody; // Buffer
3
double *ptr_melody;
4
5
ptr_melody = melody_a;

Weiter habe ich noch nicht geschrieben :)
Hab aber direkt eine Fehlermeldung bekommen, in der steht, dass die 
Deklaration inkompatibel wäre, was ich nicht verstehe, da ja beide 
Datentypen "double" sind.
Also hier nochmal die Fehlermeldung:

main.c(62): error:  #147: declaration is incompatible with "double 
*ptr_melody" (declared at line 59)
  ptr_melody = melody_a[0];

Wäre nett, wenn mir jemand einen Tipp geben könnte.

Danke

von Karl H. (kbuchegg)


Lesenswert?

Jimin M. schrieb:

> Also ungefähr so:
>
1
> const double melody_a[] = 
2
> {c,d,e,f,g,g,a,a,a,a,g,a,a,a,a,g,f,f,f,f,e,e,g,g,g,g,c};
3
> double melody; // Buffer
4
> double *ptr_melody;
5
> 
6
> ptr_melody = melody_a;
7
>

natürlich geht das.
Aber du hast das Problem schon erkannt. Nur mit dem Pointer alleine 
weißt du nicht, wie gross das Array ist.
D.h. du musst an der Stelle, an der das Array verwendet wird noch eine 
zusätzliche Variable haben, in der du vermerkst wie groß eigentlich das 
Array ist, auf das der Pointer zeigt.
(*)

> Ich wollte ein Array-Buffer haben in das dann meine gewünschte
> Melodie geladen wird
Und umkopieren brauchst du das Array auch nicht. Alles was du brauchst 
sind (wahrscheinlich) 2 Pointer.
Der eine zeigt immer auf den Anfang der Melodie, der andere auf die 
aktuell zu spielende 'Note'.


> Also hier nochmal die Fehlermeldung:
>
> main.c(62): error:  #147: declaration is incompatible with "double
> *ptr_melody" (declared at line 59)
>   ptr_melody = melody_a[0];

logisch:
wenn melody_a das komplette Array ist, dann ist melody_a[0] ein Wert aus 
diesem Array. Ein Wert dieses Arrays, das ist aber ein double. Also 4.0 
oder 7.0 oder 3.141592654
Das ist aber nicht das was du nach ptr_melody kopiert haben willst. 
ptr_melody soll auf den Anfang des Arrays zeigen, soll also die 
Startadresse des Arrays im Speicher beinhalten. Die Startadresse kriegst 
du aber indem du sie dir zb so beschaffen lässt ...
1
   ptr_melody = &melody_a[0];
... also den 'Address_of_Operator' (das &) benutzt. Oder aber indem du 
ausnutzt, dass der Name eines Arrays alleine (also ohne Indexangabe) 
bereits als seine eigene Startadresse fungiert.
1
   ptr_melody = melody_a;

> Hab aber direkt eine Fehlermeldung bekommen, in der steht, dass
> die Deklaration inkompatibel wäre, was ich nicht verstehe, da ja
> beide Datentypen "double" sind.


Die beiden Datentypen sind eben nicht 'beide double'.
In
1
    ptr_melody = melody_a[0];
ist der Datentyp links vom = ein 'double *', während der Datentyp rechts 
vom = ein 'double' ist. Das links vom = ist ein 'Pointer auf double'. 
Das ist ein eigener Datentyp: zuallererst mal ist das ein Pointer (das 
ist die wichtigere Information!). Und so ein Pointer kann auf einen 
double zeigen.
In ....
1
   int *    pI;
2
   char *   pC;
3
   double * pD;
4
   struct test * pPtr;
... sind alle zuallererst mal Pointer! Eine Pointer Variable beinhaltet 
die Adresse von etwas (wo ist dieses andere im Speicher zu finden). Es 
ist zuallererst mal ein Verweis auf irgendetwas anderes. Die zusätzliche 
Angabe (int, char, double, struct test) gibt auskunft darüber, was 
dieses 'andere' ist, was man erhält, wenn man genau unter dieser Adresse 
im Speicher nachsieht. Das ändert aber nichts daran, dass all diese 
Variablen zuallererst mal einfach nur Pointer sind. Und ein Pointer ist 
nun mal kein double.

Der Unterschied ist:
1
  int i;
es wird eine Variable i angelegt. Diese Variable enthält einen Wert 
enthalten. Zum Beispiel 5
1
                         i
2
                       +-------+
3
                       |  5    |
4
                       +-------+

wohingegen
1
  int * pI;
eine Pointer Variable angelegt wird. Diese Pointer Variable enthält die 
Speicheradresse von etwas anderem, wo dann der Wert zu finden ist. pI 
könnte zum Beispiel die Adresse von i enthalten
1
   pI = &i;
wodurch das hier entsteht
1
   pI
2
  +---------+
3
  |   o-------------+
4
  +---------+       |   i
5
                    |  +-------+
6
                    +->|  5    |  
7
                       +-------+
In pI ist die Adresse von i eingetragen (in der Zeichnung ausgedrückt 
durch den Pfeil, da der numerische Wert dieser Adresse recht 
uninteressant ist). Will man den Wert (also die 5) ausgehend von pI 
erhalten, dann muss man einmalig den Pfeil verfolgen um an diesen Wert 
zu kommen. Genau das ist die Bedeutung des '*' in
1
   int j;
2
   j = *pI;

bzw. auch in
1
   *pI = 8;
Diese 'Dereferenzierung' ist nichts anderes als ein 'verfolge von pI 
ausgehend den Pfeil und mach etwas mit dem, was du am Ende des Pfeils 
findest'.

>
> Wäre nett, wenn mir jemand einen Tipp geben könnte.

Wie meistens, ist der heisseste Tip: C-Buch durcharbeiten!
Nur glaubt einem das niemand. Und so müssen dann immer wieder 
diejenigen, die ihre C-Bücher vor langer langer Zeit riguros 
durchgearbeitet haben und geübt haben ehe sie ihr erstes Projekt 
angegangen sind, immer wieder mit Basiskentnissen aushelfen.


(*) Es gäbe noch eine 2-te Möglichkeit: man kann natürlich dem 
verwendenden Code das Ende des Arrays auch dadurch anzeigen, dass man 
einen speziellen Wert als letzten Array Eintrag benutzt, an dem der 
verwendende Code erkennt, dass jetzt das Array zu Ende ist, das also 
keine weiteren sinnvollen Werte mehr kommen. String-Verarbeitung 
funktioniert in C genau nach diesem Prinzip.

: Bearbeitet durch User
von Jimin M. (minmin)


Lesenswert?

@Karl Heinz
Wow, deine Erklärungen sind so gut...Ich bin sehr froh, dass du (und 
auch die anderen) sich so viel Mühe geben und mir meine Fragen 
beantworten.
Ich habe mir aufmerksam alles durchgelesen und wollte die von dir 
vorgeschlagene zweite Möglichkeit ausprobieren. Ich kann dir echt nur 
danken. Es funktioniert :)
Danke Danke Danke

: Bearbeitet durch User
von Michael W. (Gast)


Lesenswert?

Karl Heinz schrieb:
> String-Verarbeitung
> funktioniert in C genau nach diesem Prinzip.
Ich hätte das so und so in einen Musik-String verpackt, wie "C4,B3,A2" 
etc...

von Klops (Gast)


Lesenswert?

Markus W. schrieb:
> Ich hätte das so und so in einen Musik-String verpackt, wie "C4,B3,A2"
> etc...

Ich würde mich diesbezüglich eher an MIDI orientieren.
http://www.berkhan.com/basic/manual_7/manual_d/data_d/chap7-3.htm

Und nicht vergessen, dass die Längen der Töne noch berücksichtigt werden 
sollten (also Note/Länge). Wäre schon recht wichtig, sonst kann man 
genau genommen gar kein Liedchen dudeln lassen ... Später vielleicht 
noch die Lautstärke der Töne; das ist dann ein "Extra" (aber zum Lernen 
- ebenso wie Mehrstimmigkeit - wahrscheinlich nicht schlecht).

von Michael W. (Gast)


Lesenswert?

Das ist richtig, die hatte ich jetzt in meinem Schnellschuss übersehen. 
Ein komplettes MIDI-Protokoll ist aber sicher aufwändig.

von Florian (Gast)


Lesenswert?

Guten Abend zusammen,

vor ein paar Wochen habe ich auch angenfangen mich mit Mikrocontroller 
(MSP430) auseinander zu setzen. Hab soweit kleine einfache Programme auf 
die Beine gestellt (LED blinken, PWM generieren)

Seid kurzem wollte ich mittels DAC ebenfalls etwas ausgeben.
Hab es ganz einfach hinbekommen, ein Dreieckssignal bzw. ein Sinussignal 
auszugeben.

Mein nächster Versuch bestand darin, eine Melodie abzuspielen.

Und dann bin ich auf diesen Thread gestoßen. Vielen Dank an alle die 
hier so ausführlich geschrieben haben.

Dennoch habe ich noch Grundlegende verständnis Probleme, was 
wiederzugeben.


Jimin M. schrieb:
> Ich habe jetzt "Alle meine Entchen" programmiert. Funktioniert ganz gut.

Und zwar wollte ich ebenfalls wie der Threadersteller "Alle meine 
Entchen" wiedergeben. Hab auch soweit verstanden, dass nicht nur 
verschiedene Spannungspegel den Ton formen, sondern man ebenfalls die 
Schwingung benötigt. Deshalb habe ich im Array nach jeder Spannung eine 
Variable hinzugefügt, die die Amplitude wieder auf 0V bringt.

Mir ist bewusst, dass die Clock noch nicht richtig eingestellt ist, aber 
ich wollte für den Anfang überhaupt erstmal höhen unterschiede höre.


Hoffe ihr könnt mir an der Stelle auch weiter helfen.
1
#include <msp430f1611.h>
2
3
4
//    note  periode  frequency
5
#define c   3830    //261Hz     0.
6
#define d   3400    //294Hz     1.    
7
#define e   3038    //329Hz     2.
8
#define f   2864    //349Hz     3.
9
#define g   2550    //392Hz     4.
10
#define a   2272    //440Hz     5.
11
#define h   2028    //493Hz     6.
12
#define C   1912    //523Hz     7.
13
14
//Rest
15
#define R   0       //          8.
16
17
unsigned int i = 0, y=0;
18
19
// alle meine Entchen: c d e f g g a a a g a a a g f f f f e e d d d d c
20
21
int melody[]={c,R,d,R,e,R,f,R,g,R,g,R,a,R,a,R,a,R,g,R,a,R,a,R,a,R,g,R,f,R,f,R,f,R,f,R,e,R,e,R,d,R,d,R,d,R,d,R,c,R};
22
23
int main(void)
24
{
25
  WDTCTL = WDTPW + WDTHOLD;
26
27
  ADC12CTL0 = REFON;                          // interne Referenzspannung auf 2,5V
28
  DAC12_0CTL = DAC12IR + DAC12AMP_7 + DAC12ENC;         // DAC12 initialisieren
29
  
30
  
31
  BCSCTL1 &= ~XT2OFF;
32
  BCSCTL1 |= XTS;
33
  
34
  BCSCTL2 |= SELM_3 + SELS;
35
  
36
37
  
38
  while(1)
39
  {
40
   for(i=0; i<1024;i++)
41
    {
42
      DAC12_0DAT = melody[i]; 
43
      
44
      
45
      // Pause für Einstellung Frequenz
46
      for(y=0; y<500; y++){}
47
    }
48
49
  }
50
}

von Gerald M. (gerald_m17)


Lesenswert?

Du schreibst, du hast erkannt, dass es um Schwingungen geht, schwingst 
aber nicht. Ein Ton besteht nicht aus einmal "an, dann aus" sondern das 
muss er ganz oft machen.
Im Idealfall ist das sinusförmig mit der Frequenz des Tones. Hierfür 
brauch man einen DAC und eine Wertetabelle für einen Sinus. Je genauer 
dein DAC ist, desto mehr Werte brauchst du.
Jedem Wert in deiner Tabelle kann man nun einem Winkel zuordnen. Der 
trick bei der Tonhöhe besteht nun einfach darin, nicht jeden Punkt in 
deinem array auszugeben.
Hat deine komplette (wir haben also einfach eine komplette 
Sinusschwingung in 1000 Teile geteilt) Tabelle nun 1000 Werte 
(ausreichend für einen 8 Bit DAC, da jeder Wert vierfach vorkommt (zwei 
mal positiv und zwei mal negativ)) und du gibst dein signal mit 40 kHz 
aus (diese Frequenz sollte fest sein) kannst du einfach anhand der Menge 
an übersprungenen Werten die Tonhöhe bestimmen.
Bei 40kHz vergehen zwischen der Ausgabe am DAC etwa 25us.
Möchtest du also einen 1kHz Ton ausgeben, musst du in einer Millisekunde 
durch deine Periode durch sein. Du kannst 40 Werte ausgeben, weshalb du 
einfach jeden 25. Wert ausgibst.
Bei einem 440Hz Ton hast du 2.27 ms Zeit für deine Periode. Du kannst 
also 91 Werte ausgeben. Das heißt du nimmst jeden 11. Wert.
Das ganze nennt sich dann phasenakkumulator. Möchtest du also eine 
Sekunde lang einen 1kHz Ton ausgeben, machst du nichts anderes wie 40000 
mal jeden 25. Wert auszugeben.

Du hast das Problem, wenn du es nur digital machen möchtest, dass du 
keine Zwischenstufen ausgeben kannst. Um den ton zu variieren muss also 
die Ausgangsfrequenz (wie oben die 40kHz) variiert werden.
Das macht es relativ einfach den passenden Ton auszugeben (einfach mit 
der doppelten Frequenz den Pin toggeln) aber schwerer die korrekte 
tonlänge auszugeben. Möchtest du eine Sekunde lang 1kHz ausheben, musst 
du 2000 mal den pin mit 2khz toggeln, möchtest du einen 500Hz Ton musst 
du 1000 mal den pin mit 1kHz toggeln.

Noch eine Anmerkung: Ja ich weiß, man brauch keine komplette 
Sinusschwingung in einem array. Und man brauch auch nicht so viele 
Werte, man kann auch interpolieren etc.

von Florian (Gast)


Lesenswert?

@Gerald M.

Wow vielen Dank für die ausführliche Nachricht!

Ich probier das nun mal aus. Hab auch schon eine Seite gefunden, die mir 
1000 Sinus Werte generiert.

Hab jetzt statt deinen vorgeschlagenen 8 Bit DAC, den 12 Bit DAC 
ausgewählt mit den entsprechenden Werten.


An dieser Stelle würde ich gern noch was hinzufügen, bitte nicht falsch 
verstehen.

Den Nachteil den ich an der Konstruktion sehe ist der, dass ich für 
jedes Lied was ich abspielen möchte, die entsprechenden Noten dafür 
nutzen muss.

Ich mein ich hab das in dem vorherigen Post nicht mitgeteilt, aber was 
wäre wenn ich nun eine beliebe wav Datei abspielen möchte, mit dem 
entsprechenden HEX Code?

Denn das wäre mein erstes großes Ziel.

von Florian (Gast)


Lesenswert?

Gerald M. schrieb:
> du gibst dein signal mit 40 kHz
> aus (diese Frequenz sollte fest sein)

Ja danke für den Tipp, leider bin ich noch nciht so sehr eingeübt dass 
ich nicht genau weiß, wie ich das auf 40 kHz runterschrauben soll.

Ich habs mit Timern und Interrupts verscuht, funktioniert aber nicht. 
Oder ich hab da was großes Übersehen.

Da das gar nicht ging, hab ich das wieder raus genommen und die Freq vom 
ACLK die ich vorher mit 8 geteilt habe benutzt. Da bekomme ich 1 MHz.
Steht alles im Code kommentiert.

Mit dem DCO hab ich es auch versucht, den hab ich ebenfalls auf das 
Minimum runtergeschraubt und auch mit den Teiler 4 geteilt, aber kommt 
auch nichts sinnvolles bei rum.

Hier wäre ich für Anregungen sehr dankbar!


Gerald M. schrieb:
> Du hast das Problem, wenn du es nur digital machen möchtest, dass du
> keine Zwischenstufen ausgeben kannst. Um den ton zu variieren muss also
> die Ausgangsfrequenz (wie oben die 40kHz) variiert werden.
> Das macht es relativ einfach den passenden Ton auszugeben (einfach mit
> der doppelten Frequenz den Pin toggeln)


Den Schritt verstehe ich noch nicht ganz. Könntest du mir den bitte 
genauer erklären.



Soweit habe ich deinen Lösungsvorschlag programmiert. Ich höre schon 
eine leichte Melodie, aber die CLK ist noch sehr schnell.

1
#include <msp430f1611.h>
2
3
4
5
6
#define c 261   // 0.
7
#define d 294   // 1.
8
#define e 329   // 2.
9
#define f 349   // 3.
10
#define g 391   // 4.
11
#define a 440   // 5.
12
13
//Rest
14
#define R   0   // 6.
15
16
17
int thisNote = 0;
18
19
// Funktions-Prototypen
20
void setup();
21
void delay(unsigned int m_sec);
22
void tone(int Note);
23
24
// globale Arrays -- alle meine Entchen: 
25
int melody[]={c,d,e,f,g,g,a,a,a,g,a,a,a,g,f,f,f,f,e,e,d,d,d,d,c,R};
26
27
//Sin table values
28
int sin_vector[] = {2048, 2061, 2074, 2087, 2099, 2112, 2125, 2138, 2151, 2164, 2177, 
29
2189, 2202, 2215, 2228, 2241, 2253, 2266, 2279, 2292, 2305, 2317, 2330, 2343, 2356, 
30
2368, 2381, 2394, 2406, 2419, 2432, 2444, 2457, 2469, 2482, 2495, 2507, 2520, 2532, 
31
2545, 2557, 2570, 2582, 2594, 2607, 2619, 2631, 2644, 2656, 2668, 2681, 2693, 2705, 
32
2717, 2729, 2741, 2753, 2766, 2778, 2790, 2802, 2813, 2825, 2837, 2849, 2861, 2873, 
33
2885, 2896, 2908, 2920, 2931, 2943, 2954, 2966, 2977, 2989, 3000, 3012, 3023, 3034, 
34
3045, 3057, 3068, 3079, 3090, 3101, 3112, 3123, 3134, 3145, 3156, 3166, 3177, 3188, 
35
3199, 3209, 3220, 3230, 3241, 3251, 3262, 3272, 3282, 3292, 3303, 3313, 3323, 3333, 
36
3343, 3353, 3363, 3373, 3382, 3392, 3402, 3411, 3421, 3430, 3440, 3449, 3459, 3468, 
37
3477, 3486, 3495, 3505, 3514, 3522, 3531, 3540, 3549, 3558, 3566, 3575, 3583, 3592, 
38
3600, 3609, 3617, 3625, 3633, 3642, 3650, 3658, 3665, 3673, 3681, 3689, 3696, 3704, 
39
3712, 3719, 3726, 3734, 3741, 3748, 3755, 3762, 3769, 3776, 3783, 3790, 3797, 3803, 
40
3810, 3816, 3823, 3829, 3836, 3842, 3848, 3854, 3860, 3866, 3872, 3878, 3883, 3889, 
41
3895, 3900, 3906, 3911, 3916, 3921, 3927, 3932, 3937, 3942, 3946, 3951, 3956, 3961, 
42
3965, 3970, 3974, 3978, 3983, 3987, 3991, 3995, 3999, 4003, 4006, 4010, 4014, 4017, 
43
4021, 4024, 4027, 4031, 4034, 4037, 4040, 4043, 4046, 4048, 4051, 4054, 4056, 4059, 
44
4061, 4063, 4066, 4068, 4070, 4072, 4074, 4075, 4077, 4079, 4080, 4082, 4083, 4085, 
45
4086, 4087, 4088, 4089, 4090, 4091, 4092, 4092, 4093, 4094, 4094, 4094, 4095, 4095, 
46
4095, 4095, 4095, 4095, 4095, 4094, 4094, 4094, 4093, 4092, 4092, 4091, 4090, 4089, 
47
4088, 4087, 4086, 4085, 4083, 4082, 4080, 4079, 4077, 4075, 4074, 4072, 4070, 4068, 
48
4066, 4063, 4061, 4059, 4056, 4054, 4051, 4048, 4046, 4043, 4040, 4037, 4034, 4031, 
49
4027, 4024, 4021, 4017, 4014, 4010, 4006, 4003, 3999, 3995, 3991, 3987, 3983, 3978, 
50
3974, 3970, 3965, 3961, 3956, 3951, 3946, 3942, 3937, 3932, 3927, 3921, 3916, 3911, 
51
3906, 3900, 3895, 3889, 3883, 3878, 3872, 3866, 3860, 3854, 3848, 3842, 3836, 3829, 
52
3823, 3816, 3810, 3803, 3797, 3790, 3783, 3776, 3769, 3762, 3755, 3748, 3741, 3734, 
53
3726, 3719, 3712, 3704, 3696, 3689, 3681, 3673, 3665, 3658, 3650, 3642, 3633, 3625, 
54
3617, 3609, 3600, 3592, 3583, 3575, 3566, 3558, 3549, 3540, 3531, 3522, 3514, 3505, 
55
3495, 3486, 3477, 3468, 3459, 3449, 3440, 3430, 3421, 3411, 3402, 3392, 3382, 3373, 
56
3363, 3353, 3343, 3333, 3323, 3313, 3303, 3292, 3282, 3272, 3262, 3251, 3241, 3230, 
57
3220, 3209, 3199, 3188, 3177, 3166, 3156, 3145, 3134, 3123, 3112, 3101, 3090, 3079, 
58
3068, 3057, 3045, 3034, 3023, 3012, 3000, 2989, 2977, 2966, 2954, 2943, 2931, 2920, 
59
2908, 2896, 2885, 2873, 2861, 2849, 2837, 2825, 2813, 2802, 2790, 2778, 2766, 2753, 
60
2741, 2729, 2717, 2705, 2693, 2681, 2668, 2656, 2644, 2631, 2619, 2607, 2594, 2582, 
61
2570, 2557, 2545, 2532, 2520, 2507, 2495, 2482, 2469, 2457, 2444, 2432, 2419, 2406, 
62
2394, 2381, 2368, 2356, 2343, 2330, 2317, 2305, 2292, 2279, 2266, 2253, 2241, 2228, 
63
2215, 2202, 2189, 2177, 2164, 2151, 2138, 2125, 2112, 2099, 2087, 2074, 2061, 2048, 
64
2035, 2022, 2009, 1997, 1984, 1971, 1958, 1945, 1932, 1919, 1907, 1894, 1881, 1868, 
65
1855, 1843, 1830, 1817, 1804, 1791, 1779, 1766, 1753, 1740, 1728, 1715, 1702, 1690, 
66
1677, 1664, 1652, 1639, 1627, 1614, 1601, 1589, 1576, 1564, 1551, 1539, 1526, 1514, 
67
1502, 1489, 1477, 1465, 1452, 1440, 1428, 1415, 1403, 1391, 1379, 1367, 1355, 1343, 
68
1330, 1318, 1306, 1294, 1283, 1271, 1259, 1247, 1235, 1223, 1211, 1200, 1188, 1176, 
69
1165, 1153, 1142, 1130, 1119, 1107, 1096, 1084, 1073, 1062, 1051, 1039, 1028, 1017, 
70
1006, 995, 984, 973, 962, 951, 940, 930, 919, 908, 897, 887, 876, 866, 855, 845, 
71
834, 824, 814, 804, 793, 783, 773, 763, 753, 743, 733, 723, 714, 704, 694, 685, 
72
675, 666, 656, 647, 637, 628, 619, 610, 601, 591, 582, 574, 565, 556, 547, 538, 
73
530, 521, 513, 504, 496, 487, 479, 471, 463, 454, 446, 438, 431, 423, 415, 407, 
74
400, 392, 384, 377, 370, 362, 355, 348, 341, 334, 327, 320, 313, 306, 299, 293, 
75
286, 280, 273, 267, 260, 254, 248, 242, 236, 230, 224, 218, 213, 207, 201, 196, 
76
190, 185, 180, 175, 169, 164, 159, 154, 150, 145, 140, 135, 131, 126, 122, 118, 
77
113, 109, 105, 101, 97, 93, 90, 86, 82, 79, 75, 72, 69, 65, 62, 59, 56, 53, 50, 
78
48, 45, 42, 40, 37, 35, 33, 30, 28, 26, 24, 22, 21, 19, 17, 16, 14, 13, 11, 10, 
79
9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 
80
8, 9, 10, 11, 13, 14, 16, 17, 19, 21, 22, 24, 26, 28, 30, 33, 35, 37, 40, 42, 45, 
81
48, 50, 53, 56, 59, 62, 65, 69, 72, 75, 79, 82, 86, 90, 93, 97, 101, 105, 109, 
82
113, 118, 122, 126, 131, 135, 140, 145, 150, 154, 159, 164, 169, 175, 180, 185, 
83
190, 196, 201, 207, 213, 218, 224, 230, 236, 242, 248, 254, 260, 267, 273, 280, 
84
286, 293, 299, 306, 313, 320, 327, 334, 341, 348, 355, 362, 370, 377, 384, 392, 
85
400, 407, 415, 423, 431, 438, 446, 454, 463, 471, 479, 487, 496, 504, 513, 521,
86
530, 538, 547, 556, 565, 574, 582, 591, 601, 610, 619, 628, 637, 647, 656, 666, 
87
675, 685, 694, 704, 714, 723, 733, 743, 753, 763, 773, 783, 793, 804, 814, 824, 
88
834, 845, 855, 866, 876, 887, 897, 908, 919, 930, 940, 951, 962, 973, 984, 995, 
89
1006, 1017, 1028, 1039, 1051, 1062, 1073, 1084, 1096, 1107, 1119, 1130, 1142, 
90
1153, 1165, 1176, 1188, 1200, 1211, 1223, 1235, 1247, 1259, 1271, 1283, 1294, 
91
1306, 1318, 1330, 1343, 1355, 1367, 1379, 1391, 1403, 1415, 1428, 1440, 1452, 
92
1465, 1477, 1489, 1502, 1514, 1526, 1539, 1551, 1564, 1576, 1589, 1601, 1614, 
93
1627, 1639, 1652, 1664, 1677, 1690, 1702, 1715, 1728, 1740, 1753, 1766, 1779, 
94
1791, 1804, 1817, 1830, 1843, 1855, 1868, 1881, 1894, 1907, 1919, 1932, 1945, 
95
1958, 1971, 1984, 1997, 2009, 2022, 2035 };
96
97
98
//Interrupt
99
//#pragma vector = TIMER0_A0_VECTOR
100
//__interrupt void TIMERA0(void)
101
//{
102
//   setup();
103
//    
104
//    thisNote = 0;
105
//    
106
//    delay(100);
107
//}
108
109
110
int main( void )
111
{
112
  // Stop watchdog timer to prevent time out reset
113
  WDTCTL = WDTPW + WDTHOLD;
114
  
115
  int warte;
116
  
117
  ADC12CTL0 = REF2_5V + REFON;                                  // interne Referenzspannung auf 2,5V
118
  DAC12_0CTL = DAC12SREF_0 + DAC12IR + DAC12AMP_7 + DAC12ENC;   // DAC12 initialisieren
119
  
120
  
121
//--------------- Clock einstellen --------------------
122
  BCSCTL1 &= ~XT2OFF;      // LFXT2 aktivieren
123
  BCSCTL1 |=  DIVA_3;      // High FrequencyMode und Teiler=8 -> 1 MHz
124
  
125
  do
126
  {
127
    IFG1 &= ~OFIFG;                        // Oscillator-Fault-Flag löschen
128
    for(warte = 0xFF; warte > 0; warte--); // warte 50 us
129
  }
130
  while((IFG1 & OFIFG) != 0);   // bis Oszillator eingeschwungen
131
  
132
  
133
//   für MCLK: Wähle XT2 und Teiler 8
134
//   für SMCLK: Wähle XT2 und Teiler 8
135
  BCSCTL2 |= SELM_2 + DIVM_3 + SELS + DIVS_3;
136
  
137
// . . . . . . DCO kleinste Frequenz . . . . . 
138
//  BCSCTL1 |= RSEL0;
139
//  BCSCTL1 &= ~RSEL2;
140
//  DCOCTL |= DCO0;
141
//  DCOCTL &= ~DCO1;
142
  
143
  // für MCLK: Teiler 4
144
  // für SMCLK: Teiler 4
145
//  BCSCTL2 |= DIVM_2 + DIVS_2;
146
    
147
       
148
//---------------- Timer konfig ------------------------
149
  
150
//  TACTL |= TACLR;
151
//  TACTL |= TASSEL_1 + MC_1 + ID_3;     // wähle ACLK, DIV=1 (automatisch)
152
////  
153
//  TACCTL0 = CCIE;       // Cap/Comp-Interrupt aktivieren
154
//  
155
//  TACCR0 = 8;          // von 0 bis 8 zählen; für 40 kHz
156
//  
157
//  __enable_interrupt();
158
  
159
   
160
  _bis_SR_register(SCG0);     // schalte DCO aus
161
  
162
  
163
  
164
  while(1)
165
  {
166
    setup();
167
    
168
    thisNote = 0;
169
    
170
    delay(100);
171
  }
172
}
173
174
175
176
//-------- unter Funktionen ----------
177
178
void tone(int Note)
179
{
180
  int laengetone = 0, z = 0;
181
  
182
  
183
  laengetone = 1000/Note;
184
  
185
  for(int i=0; i<=laengetone; i++)
186
    {
187
      z = i*Note;
188
      
189
      DAC12_0DAT = sin_vector[z]; 
190
    }
191
}
192
193
194
// alle meine Entchen: c d e f g g a a a g a a a g f f f f e e d d d d c
195
196
void setup()
197
{
198
  int anzahl = 0, werte = 0;
199
  int laengesetup = (sizeof(melody) / sizeof(int));
200
                
201
                
202
  for (int thisNote = 0; thisNote < laengesetup; thisNote++)
203
  {
204
    werte = 40000/melody[thisNote];
205
    
206
    anzahl = 1000/werte;
207
    
208
        
209
    tone (anzahl);
210
  }
211
  
212
}
213
214
215
216
void delay(unsigned int m_sec)
217
{
218
  while(m_sec--)
219
        {
220
    __delay_cycles(800);  // 1000 for 1MHz
221
        }
222
}


Bin für jede Hilfe sehr dankbar, da mir das echt Spaß macht und ich sehr 
gern weiter kommen möchte.

von J. S. (engineer) Benutzerseite


Lesenswert?

wer hat eigentlich festgelegt, dass man Musik aus Sinustönen 
zusammenbauen muss?

von Florian (Gast)


Lesenswert?

Jürgen S. schrieb:
> wer hat eigentlich festgelegt, dass man Musik aus Sinustönen
> zusammenbauen muss?

Genau das wollte ich in den vorherigen Post, bevor ich den Code gepostet 
habe, anmerken.

Ich mein so lerne ich natürlich auch was, aber wenn mir für die Lieder 
die ich später per SD Karte abspielen möchte, die Frequenzen fehlen, ist 
es nicht mehr möglich.


Und deshalb habe ich erstmal versucht, mittels einem Array wo die Werte 
in HEX abgelegt sind abzuspielen, um eine Melodien auszugeben.

Bekam aber leider nicht das gewünschte Ergebnis. Den Code habe ich ja 
weiter oben shcon gepostet.

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.