Forum: Mikrocontroller und Digitale Elektronik Audio ausgeben mit µC


von A. Audio (Gast)


Lesenswert?

Hallo,

ich habe hier ein Arduino-Board mit einem ATmega328P, das ich jedoch in 
C programmiere.
Nun habe ich einen Lautsprecher, den ich gerne ansteuern möchte.
Einfache Rechtecksignale sind kein Problem, da habe ich mir bereits 
einige Funktionen geschrieben, mit denen ich (wenn auch nur sehr 
geringfügig) die Lautstärke bei gleichbleibender Frequenz ändern kann 
sowie +- 3Hz eine Frequenz ausgeben kann.
Bei den nächsten Schritten habe ich aber Probleme:
Wie kann ich zwei Töne gleichzeitig ausgeben (über einen Lautsprecher)?
Wenn ich zwei Frequenzen gleichzeitig über einen Pin ausgebe, dann 
bekomme ich üblicherweise nur den oberen Ton und ein "Rattern" (eher so 
ein Knacksen) auf Höhe des unteren Tons zu hören.
Wie macht man so etwas üblicherweise?
Und:
mein Ziel ist es, auch Audio-Dateien wiederzugeben.
Allerdings ohne externe SD Card.
Dafür muss ich natürlich die Qualität der Datei extrem vermindern, meine 
Schritte waren folgende:
mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen und als 
headerless raw-file in unsigned 8-bit gespeichert.
Um die dann direkt in den Code einbetten zu können, habe ich mithilfe 
von xdd (Linux commandline hex editor) den Inhalt in Hex-Werte 
umgewandelt und in ein gültiges C-Array gepackt.
Mein Problem ist jetzt, wie ich diese Werte ausgeben kann. Wie ermittle 
ich die benötigten Frequenzen?

Danke für jede Hilfe!

von Curby23523 N. (Gast)


Lesenswert?

Arduino und Atmega328 sind hier meiner Meinung nach völlig ungeeignet. 
Ein ARM mit I²S wäre hier z.B. angebrachter. Du begibst dich hier auf 
einn Irrweg.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

A. Audio schrieb:
> mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen

Da das Abtasttheorem nicht so einfach ausser Kraft zu setzen ist, wird 
auf die Art die höchste in der Datei wiederzugebende Frequenz bei etwa 
500Hz sein, abgesehen von dem Problem, die 1kHz Samplefrequenz vom 
Ausgang fernzuhalten.
Deswegen ist die niedrigste Samplefrequenz, die man so benutzt, bei ca. 
8kHz. Dann geht wenigstens Sprache oder der schlechteste 
Mittelwellensender der Welt.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

A. Audio schrieb:
> Wie ermittle ich die benötigten Frequenzen?

Du brauchst genau eine Frequenz, und die hast Du bereits festgelegt - 
das ist die Samplerate.

Deine Audiosamples musst Du mit genau dieser Datenrate an einen DAC 
übergeben, und aus dem DAC kommt dann Dein gewünschtes Audiosignal 
heraus.

Problem: Dein Atmega328 hat keinen eingebauten DAC.

Mit einer entsprechend höhergetakteten PWM lässt sich ein DAC 
nachbilden; Du brauchst, da Du 8-Bit-Samples hast, eine 8-Bit-PWM, was 
bedeutet, daß deren Frequenz das 256fache Deiner Samplerate betragen 
muss.

Bei 1 kHz Samplerate sind das also 256 kHz.

von Paul B. (paul_baumann)


Lesenswert?

Du kannst auch einen ganzen Port opfern und dort ein sog. R 2xR Netzwerk 
dran machen. Das ist sozusagen ein passiver D/A-Wandler. MIT gleichen 
SMD-Widerständen kriegt man das auch einigermaßen klein hin. Ich habe 
das mal für ein Lokomoteufelchen (Modelleisenbahn) gebraucht, damit der 
Lokführer Sprüche zum Besten geben konnte.

Ich muß gucken, wo ich das habe -auf dem Rechner hier ist es nicht.

MfG Paul

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das ist tatsächlich eine gute und pragmatische Idee.

Hier die Grundlagen dazu:

https://de.wikipedia.org/wiki/R2R-Netzwerk

von A. Audio (Gast)


Lesenswert?

Curby23523 N. schrieb:
> Arduino und Atmega328 sind hier meiner Meinung nach völlig
> ungeeignet.
> Ein ARM mit I²S wäre hier z.B. angebrachter. Du begibst dich hier auf
> einn Irrweg.

ARM sind schon bestellt, dauert aber noch. Die sind dann mein nächster 
Schritt.
Allerdings gibt es genug Videos, die unter Beweis stellen, dass das 
eigentlich auch mit einem ATmega gehen müsste. Der verwendete Code ist 
da eigentlich immer die Audio.h Lib aus der Arduino IDE.

Matthias S. schrieb:
> A. Audio schrieb:
>> mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen
>
> Da das Abtasttheorem nicht so einfach ausser Kraft zu setzen ist, wird
> auf die Art die höchste in der Datei wiederzugebende Frequenz bei etwa
> 500Hz sein, abgesehen von dem Problem, die 1kHz Samplefrequenz vom
> Ausgang fernzuhalten.
> Deswegen ist die niedrigste Samplefrequenz, die man so benutzt, bei ca.
> 8kHz. Dann geht wenigstens Sprache oder der schlechteste
> Mittelwellensender der Welt.

Das es grausig klingt, weiß ich auch. Die SD Card Module sind auch schon 
bestellt, ich wollte nur schon einmal vorab den Code fertigen. Und dafür 
müsste ja eigentlich erst einmal auch eine Sampling Rate von 1000Hz 
reichen, oder nicht? Man erkennt zumindest am PC einigermaßen, was 
gemeint ist. Es geht hier auch nur um kurze Töne von 1-2s.

Rufus Τ. F. schrieb:
> A. Audio schrieb:
>> Wie ermittle ich die benötigten Frequenzen?
>
> Du brauchst genau eine Frequenz, und die hast Du bereits festgelegt -
> das ist die Samplerate.
>
> Deine Audiosamples musst Du mit genau dieser Datenrate an einen DAC
> übergeben, und aus dem DAC kommt dann Dein gewünschtes Audiosignal
> heraus.
>
> Problem: Dein Atmega328 hat keinen eingebauten DAC.
>
> Mit einer entsprechend höhergetakteten PWM lässt sich ein DAC
> nachbilden; Du brauchst, da Du 8-Bit-Samples hast, eine 8-Bit-PWM, was
> bedeutet, daß deren Frequenz das 256fache Deiner Samplerate betragen
> muss.
>
> Bei 1 kHz Samplerate sind das also 256 kHz.

Wieso muss diese das 256-fache der Samplingrate betragen? Es müsste doch 
auch reichen, wenn PWM mit z.B. 32kHz läuft. Da lassen sich doch auch 
bequem 8-bit drauflegen, oder nicht? Und 32kHz wären eigentlich noch im 
Machbaren.

Paul B. schrieb:
> Du kannst auch einen ganzen Port opfern und dort ein sog. R 2xR
> Netzwerk
> dran machen. Das ist sozusagen ein passiver D/A-Wandler. MIT gleichen
> SMD-Widerständen kriegt man das auch einigermaßen klein hin. Ich habe
> das mal für ein Lokomoteufelchen (Modelleisenbahn) gebraucht, damit der
> Lokführer Sprüche zum Besten geben konnte.
>
> Ich muß gucken, wo ich das habe -auf dem Rechner hier ist es nicht.
>
> MfG Paul

Ich kann leider max. 2 Pins dafür opfern. Alle anderen sind bereits 
anderweitig verplant. Wobei das mit Sicherheit auch mal interessant 
wäre.

Rufus Τ. F. schrieb:
> Das ist tatsächlich eine gute und pragmatische Idee.
>
> Hier die Grundlagen dazu:
>
> https://de.wikipedia.org/wiki/R2R-Netzwerk

Aber wohl leider nicht möglich, da ich nicht so viele Pins frei habe.

von Paul B. (paul_baumann)


Lesenswert?

A. Audio schrieb:
> Ich kann leider max. 2 Pins dafür opfern. Alle anderen sind bereits
> anderweitig verplant.

Daraus schließe ich, daß der Kontroller auch noch eine Menge anderer 
Sachen machen muß bzw. soll. Denke aber daran, daß so eine Sprach- bzw. 
Musikausgabe den ganzen Mann fordert. Jede Unterbrechung oder 
Verzögerung ist dann sehr störend hörbar.

MfG Paul

von A. Audio (Gast)


Lesenswert?

Die anderen Sachen sind weder zeitkritisch noch allzu rechenaufwendig.
Das wird auch die Interrupts nicht verzögern, da ansonsten keinerlei 
Interrupts verwendet werden (nur in dem gleichen Interrupt noch andere 
Sachen; ein bisschen wie in IRMP und IRSND). Es gibt einfach einen 
Interrupt, in denen verschiedene Interrupt-Funktionen aufgerufen werden. 
Es wäre mir also lieb, wenn es möglich wäre, das schon über Software zu 
implementieren. Wie gesagt, die Arduino Lib schafft das ja auch 
irgendwie.
Was sonst vllt. noch möglich wäre, wäre ein zweiter µC, der nur für die 
Soundausgabe zuständig ist und den ich über SPI (das lässt sich ja, da 
schon benutzt, mit nur einem weiteren SS-Pin machen).
Oder was meint ihr sonst noch?

von mikro (Gast)


Lesenswert?

Wenn Du noch zwei Pins frei hast, kannst Du ein Schieberegister nehmen, 
z.B. 74xx595 und ein R2R-Netzwerk, z.B. 
https://www.digikey.de/product-detail/de/bourns-inc/4310R-R2R-103LF/4310R-R2R-103LF-ND/3741102

von A. Audio (Gast)


Lesenswert?

Ist denn ein Schieberegister schnell genug? Ich habe zwar etliche hier 
herumliegen, aber ehrlich gesagt noch nie intensiver damit gearbeitet.

Ich habe jetzt allerdings folgendes:

___. . . . . ._____
|. . | . . . . . . |. . . .|
|. . |______|. . . .|____

|--------------||-------------|
 32kHz

Wenn ich jetzt in 256 verschiedenen Stufen die Breite des high-pegels 
bei einer Frequenz von 32kHz verändere, stimmt das dann?
Jetzt wird nämlich ein Ton ausgegeben, der aber aufgrund seiner Kürze 
und Ungenauigkeit (600Hz) nicht einfach zu identifizieren ist.
Durch die sehr kleinen (und damit eher schlechten) Lautsprecher wird das 
wohl vermutlich auch noch einmal verfälscht. Ansonsten bleibt mir wohl 
nichts anderes übrig, als auf das SD Module zu warten (nicht wundern, 
diese Pins habe ich bereits einberechnet; die kann ich deshalb nicht für 
die Soundausgabe nutzen).
Kann das aber theoretisch stimmen?

PS: Es ist ganz schön schwer, hier drinnen mit Zeichen zu zeichnen, wenn 
das Ergebnis ganz anders aussieht als im Editor.

von A. Audio (Gast)


Lesenswert?

A. Audio schrieb:
> _. . . . . ._____
> |. . | . . . . . . |. . . .|
> |. . |______|. . . .|____
>
> |--------------||-------------|
>  32kHz

> PS: Es ist ganz schön schwer, hier drinnen mit Zeichen zu zeichnen, wenn
> das Ergebnis ganz anders aussieht als im Editor.

Jetzt kam sogar noch etwas anderes heraus als in der Vorschau gezeigt 
wurde. -.-

Noch ein Versuch, ansonsten hoffe ich, dass ihr wisst, was ich meine:

256 verschiedene Breiten
|--| . . .|----|
_......._____
|..|......|....|
|..|______|....|____

|---------||--------|
32kHz

von A. Audio (Gast)


Lesenswert?

A. Audio schrieb:
> 256 verschiedene Breiten
> |--| . . .|----|
> _......._____
> |..|......|....|
> |..|______|....|____
>
> |---------||--------|
> 32kHz

Ich gebe es auf...
Und entschuldigt bitte die mehrfachen Beiträge, die letzten beiden 
können gerne gelöscht werden.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

A. Audio schrieb:
> PS: Es ist ganz schön schwer, hier drinnen mit Zeichen zu zeichnen, wenn
> das Ergebnis ganz anders aussieht als im Editor.

Da gibts einen Tipp, der über jedem Eingabefeld für einen neuen Beitrag 
steht:
Formatierung
Wenn man da drauf klickt:
https://www.mikrocontroller.net/articles/Formatierung_im_Forum

die 'pre' tags wären brauchbar, dann kommts so raus, wie mans eintippt. 
Aber in eckige Klammern...

: Bearbeitet durch User
von A. Audio (Gast)


Lesenswert?

Stimmt, ich habe nicht daran gedacht, Code-Tags dafür zu nutzen.
1
> 256 verschiedene Breiten
2
|--| . . .|----|
3
4
___......._____
5
|..|......|....|
6
|..|______|....|____
7
8
|---------||--------|
9
  32kHz      32kHz

Jetzt aber.
Danke für den Tipp.
Auch wenn die "Skizze" wohl vermutlich nicht allzu hilfreich ist.

von Maxim B. (max182)


Lesenswert?

A. Audio schrieb:
> Wie kann ich zwei Töne gleichzeitig ausgeben (über einen Lautsprecher)?
> Wenn ich zwei Frequenzen gleichzeitig über einen Pin ausgebe, dann
> bekomme ich üblicherweise nur den oberen Ton und ein "Rattern" (eher so
> ein Knacksen) auf Höhe des unteren Tons zu hören.

Hier hilft DDS.
Aber zuerst was wichtiges:
Arduino kannst du natürlich benutzen, aber nur als Board. Programmieren 
in Arduino-Umgebung bringt hier nichts, Programm wird zu langsam und es 
fehlt vieles in Arduino, was du brauchst.

Deshalb nimm so etwas wie AVR Studio 4.19, noch ISP-Programmer, und noch 
ein bißchen "C" studieren.

Wie kannst du einen Ton mit sehr, sehr genauer Frequenz ausgeben:
1. richte ein Timer so, daß du Interrupts mit ca. 20 bis 50 kHz hast.
2. nimm zwei Variablen. Eine wird Frequenzwert haben, weitere zählen, 
beide als unsigned long. Wenn wir Interrupt-Frequenz als Fd und 
Soll-Frequenz als Fv notieren, kannst du Frequenzwert so berechnen: K = 
(Fv/Fd)*2^32

In Interrupt wird immer gemacht:
1
volatile unsigned long wert, freq;
2
wert = Fv*24294967296/Fd;
3
ISR:
4
freq += wert;

weiter kannst du obere z.B. 8 bit von freq nehmen und als 
Frequenzausgang benutzen. Z.B. in PORTB schreiben, wo R-2R-Matrix 
angeschlossen wird.
So einfach bekommst du Sägezahn. Willst du Sinus, so mach eine 
Sinustabelle, z.B. so:
1
const unsigned char sinus_table[256] PROGMEM = {
2
  128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, 173, 
3
  176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, 213, 215, 
4
  218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244, 
5
  245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 
6
  255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, 
7
  245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, 
8
  218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179, 
9
  176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137, 134, 131, 
10
  128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 097, 093, 090, 088, 085, 082, 
11
  079, 076, 073, 070, 067, 065, 062, 059, 057, 054, 052, 049, 047, 044, 042, 040, 
12
  037, 035, 033, 031, 029, 027, 025, 023, 021, 020, 018, 017, 015, 014, 012, 011, 
13
  010, 009, 007, 006, 005, 005, 004, 003, 002, 002, 001, 001, 001, 000, 000, 000, 
14
  000, 000, 000, 000, 001, 001, 001, 002, 002, 003, 004, 005, 005, 006, 007, 009, 
15
  010, 011, 012, 014, 015, 017, 018, 020, 021, 023, 025, 027, 029, 031, 033, 035, 
16
  037, 040, 042, 044, 047, 049, 052, 054, 057, 059, 062, 065, 067, 070, 073, 076, 
17
  079, 082, 085, 088, 090, 093, 097, 100, 103, 106, 109, 112, 115, 118, 121, 124
18
};

weiter wird gemacht:
1
PORTB = pgm_read_byte (&sinus_table[(unsigned char)(freq>>24)]);
und Sinus ist da!

Genauigkeit von Frequenz (abgesehen von Quarz-Toleranz) ist Fd/2^32, bei 
Fd = 50 kHz wird das 0,00001164... Hz.

Nun deine Aufgabe, zwei Frequenzen auszugeben: nimm zweimal wert und 
freq, und addiere, was du aus der Sinus-Tabelle nimmst.

Willst du statt Sinus etwas anderes, kein Problem: mache eine andere 
Tabelle.

: Bearbeitet durch User
von Thomas E. (picalic)


Angehängte Dateien:

Lesenswert?

Servus,

man braucht keine R2R-Matrix und viele Portausgänge!
Mit einem PWM-Ausgang und Pulsbreitenmodulation, wie oben in den 
ASCII-Skizzen schon gezeigt, geht das wunderbar und kann sich auch hören 
lassen.
Wenigstens ein simples RC-Filter sollte man aber schon dafür spendieren, 
sonst klingt es wirklich grausig.
Hier habe ich das mal simuliert - ein 1kHz Sinuston wird mit 31.25kHz in 
Impulslängen zerlegt. Die Spannung an V3 wäre das Signal, das per 
Timer-PWM (z.B. ein 8-Bit Timer mit 8 MHz Clock) ausgegeben werden kann.
Die WAV-Datei ist das Signal vom out.

von Mark S. (voltwide)


Lesenswert?

Ja, das funktioniert. Habe ich auch schon mal so gemacht.

Beitrag #5363799 wurde vom Autor gelöscht.
von A. Audio (Gast)


Lesenswert?

Maxim B. schrieb:
> A. Audio schrieb:
>> Wie kann ich zwei Töne gleichzeitig ausgeben (über einen Lautsprecher)?
>> Wenn ich zwei Frequenzen gleichzeitig über einen Pin ausgebe, dann
>> bekomme ich üblicherweise nur den oberen Ton und ein "Rattern" (eher so
>> ein Knacksen) auf Höhe des unteren Tons zu hören.
>
> Hier hilft DDS.
> Aber zuerst was wichtiges:
> Arduino kannst du natürlich benutzen, aber nur als Board. Programmieren
> in Arduino-Umgebung bringt hier nichts, Programm wird zu langsam und es
> fehlt vieles in Arduino, was du brauchst.
>
> Deshalb nimm so etwas wie AVR Studio 4.19, noch ISP-Programmer, und noch
> ein bißchen "C" studieren.

Wie anfangs gesagt, ich nutze bereits C.
DDS scheitert, wenn ich das richtig nachgelesen habe, an einem fehlenden 
DA-Wandler.

> Wie kannst du einen Ton mit sehr, sehr genauer Frequenz ausgeben:
> 1. richte ein Timer so, daß du Interrupts mit ca. 20 bis 50 kHz hast.
> 2. nimm zwei Variablen. Eine wird Frequenzwert haben, weitere zählen,
> beide als unsigned long. Wenn wir Interrupt-Frequenz als Fd und
> Soll-Frequenz als Fv notieren, kannst du Frequenzwert so berechnen: K =
> (Fv/Fd)*2^32
>
> In Interrupt wird immer gemacht:volatile unsigned long wert, freq;
> wert = Fv*24294967296/Fd;
> ISR:
> freq += wert;
> weiter kannst du obere z.B. 8 bit von freq nehmen und als
> Frequenzausgang benutzen. Z.B. in PORTB schreiben, wo R-2R-Matrix
> angeschlossen wird.
> So einfach bekommst du Sägezahn.

Mit Sägezahn meinst du vermutlich ein Rechteck-Signal, oder?
Weiterhin verstehe ich irgendwie deine Rechnung nicht ganz.
Weshalb *2^32?
Ich habe es bisher immer so gemacht:
counter = INTERRUPTS_PER_S  frequency  2;
Dann in der ISR counter heruntergezählt und bei 0 den Pin getoggled.
Um einen Wert auszugeben habe ich dazu folgendes ergänzt:
counter = INTERRUPTS_PER_S / frequency;
value_counter = INTERRUPTS_PER_S  value  0xFF  counter  255/256.;
Beides in der ISR heruntergezählt und bei 0 von value_counter den Pin 
auf low gesetzt und bei counter == 0 den value_counter wieder 
eingestellt.
Mein Problem ist, dass INTERRUPTS_PER_S bei großen Frequenzen natürlich 
zu klein ist. Wenn du mir deinen Ansatz noch einmal erklärst, kann ich 
das vllt. einbauen. Wobei ich dabei ehrlich gesagt auch die Gefahr sehe, 
dass die Werte zu groß werden. Was genau ist denn dann eigentlich freq 
für ein Wert, warum verwende ich nur die obersten 8 bits?

> Willst du Sinus, so mach eine
> Sinustabelle, z.B. so:const unsigned char sinus_table[256] PROGMEM = {
>   128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167,
> 170, 173,
>   176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211,
> 213, 215,
>   218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241,
> 243, 244,
>   245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255,
> 255, 255,
>   255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249,
> 248, 246,
>   245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224,
> 222, 220,
>   218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185,
> 182, 179,
>   176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137,
> 134, 131,
>   128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 097, 093, 090, 088,
> 085, 082,
>   079, 076, 073, 070, 067, 065, 062, 059, 057, 054, 052, 049, 047, 044,
> 042, 040,
>   037, 035, 033, 031, 029, 027, 025, 023, 021, 020, 018, 017, 015, 014,
> 012, 011,
>   010, 009, 007, 006, 005, 005, 004, 003, 002, 002, 001, 001, 001, 000,
> 000, 000,
>   000, 000, 000, 000, 001, 001, 001, 002, 002, 003, 004, 005, 005, 006,
> 007, 009,
>   010, 011, 012, 014, 015, 017, 018, 020, 021, 023, 025, 027, 029, 031,
> 033, 035,
>   037, 040, 042, 044, 047, 049, 052, 054, 057, 059, 062, 065, 067, 070,
> 073, 076,
>   079, 082, 085, 088, 090, 093, 097, 100, 103, 106, 109, 112, 115, 118,
> 121, 124
> };
> weiter wird gemacht:PORTB = pgm_read_byte (&sinus_table[(unsigned
> char)(freq>>24)]);und Sinus ist da!

Hier ist das Prolbem, dass ich nicht den ganzen PORTB frei habe. Und ich 
will auch nicht einfach nur einen konstanten Ton erzeugen. Es geht 
darum, zwei Töne auf einem Lautsprecher ausgeben zu können, also quasi 
einen Akkord und letztendlich auch darum, ein (kleines) Audiofile 
abspielen zu können.

von A. Audio (Gast)


Lesenswert?

A. Audio schrieb:
> counter = INTERRUPTS_PER_S  frequency  2;
> Dann in der ISR counter heruntergezählt und bei 0 den Pin getoggled.
> Um einen Wert auszugeben habe ich dazu folgendes ergänzt:
> counter = INTERRUPTS_PER_S / frequency;
> value_counter = INTERRUPTS_PER_S  value  0xFF  counter  255/256.;

Das heißt natürlich:
1
counter = INTERRUPTS_PER_S / frequency / 2;
und
1
value_counter = INTERRUPTS_PER_S / (value / 0xFF * counter * 255/256.);

Und noch ein Problem:
Wenn man eine MP3-Datei im raw-format abspeichert, dann ist die 0 ja in 
der Mitte, also unsigned bei 127.
Entsprechend müsste ich ja eigentlich bei 127 0 ausgeben und bei 126 -1. 
Aber ich kann über PWM ja nicht -1 ausgeben. Gibt es dafür noch eine 
Lösung? So hat man natürlich immer, wenn eigentlich 0 ausgegeben werden 
soll 127 und somit ein leichtes Rauschen.

von Thomas E. (picalic)


Lesenswert?

A. Audio schrieb:
> DDS scheitert, wenn ich das richtig nachgelesen habe, an einem fehlenden
> DA-Wandler.

Du solltest die "Klangsynthese" von der Ausgabe getrennt betrachten. Ob 
Du nun die Werte an einen integrierten DAC, Port mit R2R-Netzwerk oder 
PWM-Generator übergibst, ändert doch nichts an der Berechnung der 
Wellenform.
Dementsprechend scheitert auch das DDS-Verfahren nicht, weil kein 
integrierter DAC vorhanden ist.

A. Audio schrieb:
> Mein Problem ist, dass INTERRUPTS_PER_S bei großen Frequenzen natürlich
> zu klein ist.

Die Interrupts werden bei DDS nicht häufiger, wenn die Frequenz des 
Audio-Signals erhöht wird. Die Interrupts laufen  fest mit der 
Sample-Frequenz (z.B. 32kHz), egal, ob Du einen 1kHz oder 5kHz Ton (oder 
auch beide gleichzeitig) erzeugen willst.

: Bearbeitet durch User
von Thomas E. (picalic)


Lesenswert?

A. Audio schrieb:
> value_counter = INTERRUPTS_PER_S / (value / 0xFF  counter  255/256.);

Mir scheint, Du willst die 8-Bit PWM per Software in Interrupts erzeugen 
- dazu bräuchtest Du ja dann tatsächlich eine Interrupt-Frequenz von 
256*PWM-Frequenz - dieser Ansatz ist natürlich gleich zum Scheitern 
verurteilt!
Die PWM muss natürlich mit Hardware-Unterstützung gemacht werden (Timer 
mit Output Compare).
Bin jetzt nicht so der Spezialist, was die Möglichkeiten der 
AVR-Peripherie angeht. Beim PIC würde ich das PWM Modul direkt benutzen 
- da muss dann jeweils nur der aktuelle Ausgabewert in ein Register 
geschrieben werden.
Wenn das so beim AVR nicht geht(*), wäre die zweitbeste Methode eben ein 
Interrupt beim Match des Timers mit dem OCR-Register. Das sind dann aber 
auch nur jeweils ein Interrupt pro PWM-Zyklus und nicht 256.

A. Audio schrieb:
> So hat man natürlich immer, wenn eigentlich 0 ausgegeben werden
> soll 127 und somit ein leichtes Rauschen.

Kein Rauschen, sondern die PWM-Frequenz mit mehr oder weniger großer 
Amplitude, je nach Charakteristik des nachgeschalteteten Tiefpasses. 
Wenn die PWM-Frequenz aber über der Hörgrenze liegt (> 20kHz), dürften 
die Reste der PWM-Frequenz auf dem Signal nicht stören.

(*) Edit: habe gerade mal ins kurz ins Datenblatt geguckt! Natürlich hat 
der Controller auch brauchbare Hardware-PWM Peripherie. Geht also auch 
voll Hardwareseitig!

: Bearbeitet durch User
von A. Audio (Gast)


Lesenswert?

Thomas E. schrieb:
> A. Audio schrieb:
>> DDS scheitert, wenn ich das richtig nachgelesen habe, an einem fehlenden
>> DA-Wandler.
>
> Du solltest die "Klangsynthese" von der Ausgabe getrennt betrachten. Ob
> Du nun die Werte an einen integrierten DAC, Port mit R2R-Netzwerk oder
> PWM-Generator übergibst, ändert doch nichts an der Berechnung der
> Wellenform.
> Dementsprechend scheitert auch das DDS-Verfahren nicht, weil kein
> integrierter DAC vorhanden ist.
>
> A. Audio schrieb:
>> Mein Problem ist, dass INTERRUPTS_PER_S bei großen Frequenzen natürlich
>> zu klein ist.
>
> Die Interrupts werden bei DDS nicht häufiger, wenn die Frequenz des
> Audio-Signals erhöht wird. Die Interrupts laufen  fest mit der
> Sample-Frequenz (z.B. 32kHz), egal, ob Du einen 1kHz oder 5kHz Ton (oder
> auch beide gleichzeitig) erzeugen willst.

Ich probiere es einmal aus.
Allerdings folgendes Problem:

Maxim B. schrieb:
> volatile unsigned long wert, freq;
> wert = Fv*24294967296/Fd;
> ISR:
> freq += wert;

Dafür reichen uint32_t nicht aus. Das bräuchte min. 64 Bits. Und dann 
ist das freq>>24 natürlich sinnlos. Weshalb überhaupt * 24294967296? 
24294967296 != 2^32, und ich dachte 2^32 soll man verwenden? Wobei man 
damit natürlich erst recht nicht mit einem uint32_t hinkommt.

Thomas E. schrieb:
> A. Audio schrieb:
>> So hat man natürlich immer, wenn eigentlich 0 ausgegeben werden
>> soll 127 und somit ein leichtes Rauschen.
>
> Kein Rauschen, sondern die PWM-Frequenz mit mehr oder weniger großer
> Amplitude, je nach Charakteristik des nachgeschalteteten Tiefpasses.
> Wenn die PWM-Frequenz aber über der Hörgrenze liegt (> 20kHz), dürften
> die Reste der PWM-Frequenz auf dem Signal nicht stören.

Ich verstehe nur irgendwie das Prinzip dahinter nicht.
Wenn ich den Wert 127 analog ausgebe mit 255 Stufen, dann bin ich ja bei 
50% * U. Dabei steht 127 doch eigentlich für keinerlei Ton. Und bei 0 
bin ich dafür bei 0% * U. Also kein Ton, obwohl ja eigentlich sehr wohl 
ein Ton da sein müsste mithilfe einer negativen Spannung, oder nicht?

> (*) Edit: habe gerade mal ins kurz ins Datenblatt geguckt! Natürlich hat
> der Controller auch brauchbare Hardware-PWM Peripherie. Geht also auch
> voll Hardwareseitig!

Ich habe auch noch einmal ins Datenblatt geschaut und ein wenig 
gegooglet. Tatsächlich ist es möglich, auch Duty-Cycles einzustellen mit 
OCRnA und OCRnB. Daran habe ich gar nicht gedacht, dass ich ja auch 
einfach zwei Register kombinieren kann. Danke für den Hinweis.
Ich ging ehrlich gesagt davon aus, dass es nur möglich ist, 
Rechtecksignale mit 50% Duty zu erstellen. Auf die Kombination von 
zweien bin ich nicht gekommen.

von Thomas E. (picalic)


Lesenswert?

A. Audio schrieb:
> Weshalb überhaupt * 24294967296?

keine Ahnung, wie er auf diese Zahl kommt. Gibt ja auch als Hex-Zahl 
(5A817C800) nicht viel Sinn. Ich würde sagen: klammere Dich nicht an 
diese Zahl sondern versuche, das Prinzip zu verstehen. Wenn Du nicht 
unbedingt eine extrem fein granulierbare Frequenzeinstellung benötigst, 
reicht evtl. auch eine 16-Bit Rechnung.
Das Prinzip ist, daß man in der ISR mit fester Ausgabe-Samplerate (z.B. 
alle 32µs) den Lesezeiger für den Samplespeicher (z.B. Sinus-Daten) um 
einen gewissen Betrag weiterstellen muss, abhängig von der gewünschten 
Tonhöhe. Wenn man mit ganzen Zahlen rechnet, geht das normalerweise nur 
in ganzzahligen Schritten, d.h. die Sinuswelle könnte man nur mit 
einfacher, doppelter, dreifacher... Frequenz ausgeben.
Für andere Verhältnissse (z.B. *1,5 oder *0,75) braucht man 
Nachkommastellen. Man könnte mit float rechnen, aber das will man aus 
Performance-Gründen hier eher nicht. Also benutzt man 
Festkomma-Arithmetik und nimmt eine lange Integer-Variable, von der die 
oberen Bits als Vorkommastellen (->Sinus-Sampleadresse) und die 
niedrigeren Bits als Nachkommastellen behandelt werden.

A. Audio schrieb:
> Also kein Ton, obwohl ja eigentlich sehr wohl
> ein Ton da sein müsste mithilfe einer negativen Spannung, oder nicht?

Durch eine simple Gleichspannung entsteht nie ein Ton. Der Ton entsteht 
durch eine zyklische Änderung der Spannung, und dabei ist dann aber 
egal, ob sich die Spannung im Bereich -1 und +1 Volt ändert, oder 
zwischen 0V und 2V. Auch ein DAC, wie er in vielen µCs eingebaut ist, 
oder ein R2R-Netzwerk am Port-Ausgang kann i.d.R. keine negative 
Spannung ausgeben. Da aber ein Tonsignal positive und negative 
Halbwellen hat, setzt man hier den "Ruhepegel" eben auf die Hälfte der 
maximal möglichen Ausgangsspannung. So kann sich die Spannung relativ 
dazu in beide Richtungen ändern. Der Verstärker gibt nur den verstärkten 
Wechelspannungs-Anteil an den Lautsprecher, dafür ist also egal, wo der 
absolute Ruhepegel liegt.

: Bearbeitet durch User
von chris (Gast)


Lesenswert?

Autor: A. Audio (Gast)
Datum: 24.03.2018 15:13
>Hier ist das Prolbem, dass ich nicht den ganzen PORTB frei habe. Und ich
>will auch nicht einfach nur einen konstanten Ton erzeugen. Es geht
>darum, zwei Töne auf einem Lautsprecher ausgeben zu können, also quasi
>einen Akkord und letztendlich auch darum, ein (kleines) Audiofile
>abspielen zu können.

PWM und DDS eignet sich gut dafür:

Beitrag "17 Kanal Avr Synthesizer in Asm"

von Klaus (Gast)


Lesenswert?

A. Audio schrieb:
> mein Ziel ist es, auch Audio-Dateien wiederzugeben.
> Allerdings ohne externe SD Card.
> Dafür muss ich natürlich die Qualität der Datei extrem vermindern, meine
> Schritte waren folgende:

Schau mal, wie so etwas in der klassischen digitalen Telefonie gemacht 
wird. Die Signale werden mit einer Auflösung von etwa 14 Bit mit 8kHz 
gesampelt. Dann werden sie mit einer PCM-Codierung (A-law oder µ-Law) 
auf 8 Bit komprimiert. Das gibt auf eine serielle umgerechnet eine 
Datenrate von 64 kBit pro Sekunde oder einen Speicherbedarf von 8 kByte. 
Audacity kann solche Daten aus eigentlich jedem anderen Format 
herstellen.

Wie kann man das möglichst simple wiedergeben? Als DAC benutzt man PWM. 
Um mit der Qualität in die Nähe der Aufnahme zu kommen, sollte die PWM 
bei einer Frequenz von 8 kHz eine Auflösung von 11-12 Bit haben, mehr 
als 14 bringt aber auch nichts. Jetzt muß man nur im PWM Interrupt alle 
125µs (die 8kHz) ein Sample von PCM auf linear decodieren und auf die 
PWM geben.

Das decodieren geht leichter, als es die Formeln vermuten lasse. Da die 
PCM-Samples nur 8 Bit haben, kann man für das Decodieren eine Tabelle 
verwenden. Das sind 256 16Bit Integer, also ein halbes k. Diese kann man 
sich leicht vorher, sowohl für A-Law als auch für µ-Law auf dem PC 
ausrechnen. Das Decodieren ist dann nur noch ein Tabellenzugriff, das 
schafft wirklich jeder µC.

Das Ausgangssignal hat die klassische Telefonbandbreite von 300Hz bis 
3,2kHz. Das Signal aus der PWM hat aber eine Problem, es enthält hohe 
Anteile an der PWM-Frequenz von 8kHz, die hörbar ist. Für einen DAC mit 
einer PWM braucht man sowieso ein Filter, hier sollte es eine höhere 
Ordnung haben.

Die Belastung des µC ist marginal: nächstes Sample lesen, 
Tabellenzugriff und Schreiben des PWM Registers. Und das alle 125µs. Und 
die Qualität ist eher besser als ein Walkman. Ich hab sowas mal gebaut 
und das PWM_Signal gegenphasig auf ein paar parallelgeschaltete 
Busbuffer gegeben. Dann ein paar LC-Filter und einen Lautsprecher, so 
ein Class-D für Arme. Ich hielt das Ergebniss für brauchbar.

Ist länger geworden, als ich gedacht hatte

MfG Klaus

von Thomas E. (picalic)


Lesenswert?

Klaus schrieb:
> sollte die PWM
> bei einer Frequenz von 8 kHz eine Auflösung von 11-12 Bit haben,

Wozu denn eine so hohe Auflösung? Bei 8kHz ist das Sampleraster schon 
derart grob, daß einen da eine hohe Auflösung (> 60dB SNR) bei der 
Qualität auch nicht mehr groß 'rausreisst. Ich würde da eher auf eine 
höhere Abtastfrequenz gehen und die PWM-Frequenz möglichst erhöhen, 
damit man keinen so steilflankigen Tiefpass nachschalten muss. Für 
Telefonqualität reicht doch SNR = 48 dB (8 Bit Aufösung) locker. Evtl. 
wäre es sogar in der Praxis besser, noch ein Bit zugunsten einer höheren 
PWM-Frequenz zu opfern. Ein 7-Bit Timer mit 16 MHz gibt 125kHz, davon 
bleibt selbst mit einem simplen, 1-stufigen RC-Filter nicht mehr viel 
übrig.
Also, mein "Rezept" für brauchbare Qualität mit geringem Aufwand wäre: 
8-Bit samplen, aber dafür >= 16kHz Abtastrate (relativ weit weg vom 
Nutzsignal) und noch höhere PWM Frequenz (sehr weit weg vom Nutzsignal).

Beim Telefon wird wahrscheinlich mit einer höheren Auflösung gesampled, 
um den Dynamikbereich zu erhöhen, d.h. es soll ja auch noch etwas zu 
verstehen sein, wenn der Sprecher mal etwas leisere Töne anschlägt. Zur 
Ausgabe eines Signaltons braucht es das aber nicht, da man den immer mit 
optimaler (Voll-)Aussteuerung generieren kann.

: Bearbeitet durch User
von A. Audio (Gast)


Lesenswert?

Thomas E. schrieb:
> A. Audio schrieb:
>> Weshalb überhaupt * 24294967296?
>
> keine Ahnung, wie er auf diese Zahl kommt. Gibt ja auch als Hex-Zahl
> (5A817C800) nicht viel Sinn. Ich würde sagen: klammere Dich nicht an
> diese Zahl sondern versuche, das Prinzip zu verstehen. Wenn Du nicht
> unbedingt eine extrem fein granulierbare Frequenzeinstellung benötigst,
> reicht evtl. auch eine 16-Bit Rechnung.
> Das Prinzip ist, daß man in der ISR mit fester Ausgabe-Samplerate (z.B.
> alle 32µs) den Lesezeiger für den Samplespeicher (z.B. Sinus-Daten) um
> einen gewissen Betrag weiterstellen muss, abhängig von der gewünschten
> Tonhöhe. Wenn man mit ganzen Zahlen rechnet, geht das normalerweise nur
> in ganzzahligen Schritten, d.h. die Sinuswelle könnte man nur mit
> einfacher, doppelter, dreifacher... Frequenz ausgeben.
> Für andere Verhältnissse (z.B. *1,5 oder *0,75) braucht man
> Nachkommastellen. Man könnte mit float rechnen, aber das will man aus
> Performance-Gründen hier eher nicht. Also benutzt man
> Festkomma-Arithmetik und nimmt eine lange Integer-Variable, von der die
> oberen Bits als Vorkommastellen (->Sinus-Sampleadresse) und die
> niedrigeren Bits als Nachkommastellen behandelt werden.

Okay, jetzt habe ich es, denke ich, verstanden.
Einfach:
1
uint32_t counter = 0;
2
uint32_t value = sampling_rate * 65536 / 20000;
3
counter += value;
4
if(counter < size)
5
    analog_output(counter>>16);
6
else
7
    stop();
Müsste soweit eigentlich auch stimmen.

> A. Audio schrieb:
>> Also kein Ton, obwohl ja eigentlich sehr wohl
>> ein Ton da sein müsste mithilfe einer negativen Spannung, oder nicht?
>
> Durch eine simple Gleichspannung entsteht nie ein Ton. Der Ton entsteht
> durch eine zyklische Änderung der Spannung, und dabei ist dann aber
> egal, ob sich die Spannung im Bereich -1 und +1 Volt ändert, oder
> zwischen 0V und 2V. Auch ein DAC, wie er in vielen µCs eingebaut ist,
> oder ein R2R-Netzwerk am Port-Ausgang kann i.d.R. keine negative
> Spannung ausgeben. Da aber ein Tonsignal positive und negative
> Halbwellen hat, setzt man hier den "Ruhepegel" eben auf die Hälfte der
> maximal möglichen Ausgangsspannung. So kann sich die Spannung relativ
> dazu in beide Richtungen ändern. Der Verstärker gibt nur den verstärkten
> Wechelspannungs-Anteil an den Lautsprecher, dafür ist also egal, wo der
> absolute Ruhepegel liegt.

Okay, das ist soweit klar.
Mein Problem ist aber, dass, wenn ich 0x80 (=128) länger ausgebe, einen 
konstanten Ton erhalte. Und bei dem, was ich ausgebe, kommt nur 
ziemlicher Schwachsinn heraus. Nur bei 0x00 wird nichts ausgegeben.
Die Werte gebe ich so aus:
1
void analog_init()
2
{
3
  TCCR0B = (1 << CS02);
4
  TCCR0A = (1 << WGM01);
5
  OCR0A = 0xFF;
6
  OCR0B = 0x80;
7
  TIMSK0 |= (1 << OCIE0A) | (1 << OCIE0B);
8
}
9
10
void analog_output(uint8_t value)
11
{
12
  OCR0B = audio_[(uint8_t)(rate_counter >> 16)];
13
}

Dabei ist audio_ das Array mit den Werten aus der mp3, die ich ausgeben 
will.

chris schrieb:
> Beitrag "17 Kanal Avr Synthesizer in Asm"

Mein Problem ist das ASM. Ich kann zwar die Grundlagen, aber es ist für 
mich sehr anstrengend.
Ich kann aber ja einmal durchschauen, ob ich nicht doch etwas finde, das 
ich verstehe und wiederverwenden kann.

Klaus schrieb:
> A. Audio schrieb:
>> mein Ziel ist es, auch Audio-Dateien wiederzugeben.
>> Allerdings ohne externe SD Card.
>> Dafür muss ich natürlich die Qualität der Datei extrem vermindern, meine
>> Schritte waren folgende:
>
> Schau mal, wie so etwas in der klassischen digitalen Telefonie gemacht
> wird. Die Signale werden mit einer Auflösung von etwa 14 Bit mit 8kHz
> gesampelt. Dann werden sie mit einer PCM-Codierung (A-law oder µ-Law)
> auf 8 Bit komprimiert. Das gibt auf eine serielle umgerechnet eine
> Datenrate von 64 kBit pro Sekunde oder einen Speicherbedarf von 8 kByte.
> Audacity kann solche Daten aus eigentlich jedem anderen Format
> herstellen.
>
> Wie kann man das möglichst simple wiedergeben? Als DAC benutzt man PWM.
> Um mit der Qualität in die Nähe der Aufnahme zu kommen, sollte die PWM
> bei einer Frequenz von 8 kHz eine Auflösung von 11-12 Bit haben, mehr
> als 14 bringt aber auch nichts. Jetzt muß man nur im PWM Interrupt alle
> 125µs (die 8kHz) ein Sample von PCM auf linear decodieren und auf die
> PWM geben.
>
> Das decodieren geht leichter, als es die Formeln vermuten lasse. Da die
> PCM-Samples nur 8 Bit haben, kann man für das Decodieren eine Tabelle
> verwenden. Das sind 256 16Bit Integer, also ein halbes k. Diese kann man
> sich leicht vorher, sowohl für A-Law als auch für µ-Law auf dem PC
> ausrechnen. Das Decodieren ist dann nur noch ein Tabellenzugriff, das
> schafft wirklich jeder µC.
>
> Das Ausgangssignal hat die klassische Telefonbandbreite von 300Hz bis
> 3,2kHz. Das Signal aus der PWM hat aber eine Problem, es enthält hohe
> Anteile an der PWM-Frequenz von 8kHz, die hörbar ist. Für einen DAC mit
> einer PWM braucht man sowieso ein Filter, hier sollte es eine höhere
> Ordnung haben.
>
> Die Belastung des µC ist marginal: nächstes Sample lesen,
> Tabellenzugriff und Schreiben des PWM Registers. Und das alle 125µs. Und
> die Qualität ist eher besser als ein Walkman. Ich hab sowas mal gebaut
> und das PWM_Signal gegenphasig auf ein paar parallelgeschaltete
> Busbuffer gegeben. Dann ein paar LC-Filter und einen Lautsprecher, so
> ein Class-D für Arme. Ich hielt das Ergebniss für brauchbar.
>
> Ist länger geworden, als ich gedacht hatte
>
> MfG Klaus

Ich werde es mir einmal ansehen, danke für den Tipp.
Allerdings habe ich ja derzeit noch Probleme mit der Ausgabe.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

A. Audio schrieb:
> Mein Problem ist aber, dass, wenn ich 0x80 (=128) länger ausgebe, einen
> konstanten Ton erhalte.

Du hast nicht verstanden, wie die DAC-Nachbildung mit PWM funktioniert, 
d.h. Du hast das zur Glättung nötige RC-Glied (Tiefpass) am Ausgang 
weggelassen.

Das, was Du als Wert der PWM übergibst, entspricht einem Spannungspegel 
am Ausgang - 0 ist der geringste, 255 der größte und 128 der mittlere 
Pegel.

von Maxim B. (max182)


Lesenswert?

A. Audio schrieb:
> Maxim B. schrieb:

> Mit Sägezahn meinst du vermutlich ein Rechteck-Signal, oder?
Oder. Sägezahn ist kein Rechteck.

> Weiterhin verstehe ich irgendwie deine Rechnung nicht ganz.
> Weshalb *2^32?
Weil max. Zahl in unsigned long 2^32-1 ist.
Bei neueren Toolchains geht auch mit __uint24, für so eine Verwendung 
ist Genauigkeit von 2^24 ausreichend.


> Ich habe es bisher immer so gemacht:
> counter = INTERRUPTS_PER_S  frequency  2;
> Dann in der ISR counter heruntergezählt und bei 0 den Pin getoggled.
Das ist kein DDS, das ist etwas anderes. Viel weniger genau, was 
Frequenz betrifft. Z.B. 0,01 cent Genauigkeit kann man so nicht 
erreichen - gut wenn es unterhalb 2-3 cent bleibt.

>> warum verwende ich nur die obersten 8 bits?
Weil das reicht. Du kannst statt char auch int verwenden, nur mehr 
Speicherplatz und etwas langsamer. Wenn mehr als 2 Töne gleichzeitig, 
dann auch bei F_CPU = 20 MHz muß du Fd unter 32 kHz herabsetzen.


> Hier ist das Prolbem, dass ich nicht den ganzen PORTB frei habe.
Dann nimm ein Register, so wie 574, damit alle Ausgänge synchron 
funktionieren - und nimm die Pins, die du frei hast. Es wird nur ein 
bißchen langsamer. Oder mit SPI und 595-Register.

Z.B. so ist das möglich (zum Frage, warum obere byte nur auf 240 
beschränkt: so ist bequemer, gängige Obertöne für Wave-Tabelle zu 
berechnen: 16' = 480, 8' = 240, 5 1/3' = 160, 2/3' = 20):
1
// main.h
2
#ifndef MAIN_H
3
#define MAIN_H 1
4
5
#ifndef  F_CPU  // Quarz 20 MHz.
6
#define F_CPU 20000000UL    // CPU_F fuer Verzoegerungen
7
#endif 
8
9
#ifndef FLAGG
10
  #define FLAGG GPIOR0 /* fuer Ereignisse */
11
  #define F_PULS 0  /* Puls 32 KHz, timer 3 interrupt */
12
13
  #define FLAG_REG GPIOR1
14
  #define F_GED16  0
15
  #define F_PR8  1
16
#endif
17
18
#define SPI_MOSI  PB5
19
#define SPI_MISO  PB6
20
#define SPI_SCK    PB7
21
#define SPI_SS    PB4
22
#define SPI_DDR    DDRB
23
#define SPI_PORT  PORTB
24
25
//static inline unsigned char SPI_MasterTransmit(unsigned char data)
26
static inline void SPI_MasterTransmit(unsigned char data)
27
{
28
  /* Start transmission */
29
  SPDR = data;
30
  /* Wait for transmission complete */
31
  while(!(SPSR & (1<<SPIF)));
32
//  return SPDR;
33
}
34
35
static inline void SPI_init(void){
36
  SPI_PORT |= (1<<SPI_SS)|(1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<SPI_MISO);
37
  SPI_DDR |= (1<<SPI_SS)|(1<<SPI_MOSI)|(1<<SPI_SCK);
38
  SPI_DDR &= ~(1<<SPI_MISO);
39
  SPCR = (1<<SPE)|(1<<MSTR);
40
  SPSR = 1;
41
  SPI_MasterTransmit(~0);
42
}
43
44
#endif  /* MAIN_H */
45
46
47
// main.c
48
49
#include <avr/io.h>  // Muss immer sein
50
#include <stdio.h>  // Die Ein- und Ausgabefunktionen, 
51
// Typen und Makros
52
#include <stdlib.h>  // Funktionen zur Umwandlung von Zahlen, 
53
// fuer Speicherverwaltung und aehnliche Aufgaben.
54
#include <string.h>  // Funktionen fuer Zeichenketten
55
#include <inttypes.h>     // Fuer Datentyp
56
#include <avr/interrupt.h>   // Fuer Interrupts
57
#include <avr/eeprom.h>    // Fuer EEPROM
58
#include <avr/pgmspace.h>   // Fuer Flash
59
#include <util/delay.h>    // Fuer Verzoegerungen
60
61
#include "main.h"   // Allgemeine Programmablauf
62
#include "timer3.h"   // Timer 3
63
64
const __flash signed int ged_16[480] = {
65
0,4,8,12,16,20,23,27,30,33,36,38,40,42,44,45,
66
47,48,48,49,49,50,50,50,50,50,50,50,50,49,49,49,
67
49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,49,
68
49,49,49,49,48,48,48,47,47,47,46,46,46,46,45,45,
69
45,45,45,46,46,46,46,47,47,47,48,48,48,49,49,49,
70
49,49,50,50,50,50,50,50,50,49,49,49,49,49,49,49,
71
49,49,49,49,50,50,50,50,50,50,50,50,49,49,48,48,
72
47,45,44,42,40,38,36,33,30,27,23,20,16,12,8,4,
73
0,-4,-8,-12,-16,-20,-23,-27,-30,-33,-36,-38,-40,-42,-44,-45,
74
-47,-48,-48,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,
75
-49,-49,-49,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,
76
-49,-49,-49,-49,-48,-48,-48,-47,-47,-47,-46,-46,-46,-46,-45,-45,
77
-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-48,-48,-48,-49,-49,-49,
78
-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-49,-49,
79
-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-48,-48,
80
-47,-45,-44,-42,-40,-38,-36,-33,-30,-27,-23,-20,-16,-12,-8,-4,
81
0,4,8,12,16,20,23,27,30,33,36,38,40,42,44,45,
82
47,48,48,49,49,50,50,50,50,50,50,50,50,49,49,49,
83
49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,49,
84
49,49,49,49,48,48,48,47,47,47,46,46,46,46,45,45,
85
45,45,45,46,46,46,46,47,47,47,48,48,48,49,49,49,
86
49,49,50,50,50,50,50,50,50,49,49,49,49,49,49,49,
87
49,49,49,49,50,50,50,50,50,50,50,50,49,49,48,48,
88
47,45,44,42,40,38,36,33,30,27,23,20,16,12,8,4,
89
0,-4,-8,-12,-16,-20,-23,-27,-30,-33,-36,-38,-40,-42,-44,-45,
90
-47,-48,-48,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,
91
-49,-49,-49,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,
92
-49,-49,-49,-49,-48,-48,-48,-47,-47,-47,-46,-46,-46,-46,-45,-45,
93
-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-48,-48,-48,-49,-49,-49,
94
-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-49,-49
95
};
96
97
const __flash signed int pr_8[480] = {
98
0,4,8,12,16,20,23,27,30,33,36,38,40,42,44,45,
99
47,48,48,49,49,50,50,50,50,50,50,50,50,49,49,49,
100
49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,49,
101
49,49,49,49,48,48,48,47,47,47,46,46,46,46,45,45,
102
45,45,45,46,46,46,46,47,47,47,48,48,48,49,49,49,
103
49,49,50,50,50,50,50,50,50,49,49,49,49,49,49,49,
104
49,49,49,49,50,50,50,50,50,50,50,50,49,49,48,48,
105
47,45,44,42,40,38,36,33,30,27,23,20,16,12,8,4,
106
0,-4,-8,-12,-16,-20,-23,-27,-30,-33,-36,-38,-40,-42,-44,-45,
107
-47,-48,-48,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,
108
-49,-49,-49,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,
109
-49,-49,-49,-49,-48,-48,-48,-47,-47,-47,-46,-46,-46,-46,-45,-45,
110
-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-48,-48,-48,-49,-49,-49,
111
-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-49,-49,
112
-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-48,-48,
113
-47,-45,-44,-42,-40,-38,-36,-33,-30,-27,-23,-20,-16,-12,-8,-4,
114
0,4,8,12,16,20,23,27,30,33,36,38,40,42,44,45,
115
47,48,48,49,49,50,50,50,50,50,50,50,50,49,49,49,
116
49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,49,
117
49,49,49,49,48,48,48,47,47,47,46,46,46,46,45,45,
118
45,45,45,46,46,46,46,47,47,47,48,48,48,49,49,49,
119
49,49,50,50,50,50,50,50,50,49,49,49,49,49,49,49,
120
49,49,49,49,50,50,50,50,50,50,50,50,49,49,48,48,
121
47,45,44,42,40,38,36,33,30,27,23,20,16,12,8,4,
122
0,-4,-8,-12,-16,-20,-23,-27,-30,-33,-36,-38,-40,-42,-44,-45,
123
-47,-48,-48,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,
124
-49,-49,-49,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,
125
-49,-49,-49,-49,-48,-48,-48,-47,-47,-47,-46,-46,-46,-46,-45,-45,
126
-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-48,-48,-48,-49,-49,-49,
127
-49,-49,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-49,-49
128
};
129
130
131
__uint24 ton_0 = 0;
132
__uint24 ton_1 = 0;
133
134
volatile __uint24 kton_0 = 184549; // a1
135
volatile __uint24 kton_1 = 109734; // c1
136
137
138
ISR(TIMER3_COMPA_vect, ISR_NAKED){ // Timer 0 interrupt.
139
140
  FLAGG |= (1<<F_PULS);  // Flag fuer Puls einsetzen
141
  
142
  reti(); // da ISR_NAKED
143
}
144
145
/**********************************************************/
146
// Hauptprogramm
147
148
int main(void){
149
DDRA = ~0;
150
DDRC = ~0;
151
SPI_init();
152
TIMER0_init();
153
FLAG_REG = (1<<F_GED16)|(1<<F_PR8)|(1<<F_GED8)|(1<<F_PR4)|(1<<F_FL4)|(1<<F_QU3)|(1<<F_PR2)|(1<<F_QU1_2);
154
155
while(1){ 
156
  signed int aa=0;
157
  unsigned int bb;
158
159
if( FLAGG & (1<<F_PULS) ){
160
  FLAGG &= ~(1<<F_PULS); // Flag fuer Puls = 0 
161
162
  SPI_PORT |= (1<<SPI_SS);
163
164
  ton_0 += kton_0;
165
  // max MSB = 240
166
  if((unsigned char)(ton_0 >> 16) >= 0xf0) ton_0 -= 0xf00000;
167
  bb = (ton_0 >> 8)&& 0x1ff;
168
  aa = 0;
169
170
  if(FLAG_REG & (1<<F_GED16)) aa = ged_16[bb];
171
  if(FLAG_REG & (1<<F_PR8)) aa += pr_8[bb];
172
  
173
  SPI_PORT &= ~(1<<SPI_SS);
174
  aa = aa >> 1;
175
  aa = aa >> 1;
176
  aa = aa >> 1;
177
  SPDR = aa;
178
179
180
  ton_1 += kton_1;
181
  // max MSB = 240
182
  if((unsigned char)(ton_0 >> 16) >= 0xf0) ton_0 -= 0xf00000;
183
  bb = (ton_1 >> 8)&& 0x1ff;
184
  aa = 0;
185
186
  if(FLAG_REG & (1<<F_GED16)) aa = ged_16[bb];
187
  if(FLAG_REG & (1<<F_PR8)) aa += pr_8[bb];
188
189
  while(!(SPSR & (1<<SPIF)));
190
  aa = aa >> 1;
191
  aa = aa >> 1;
192
  aa = aa >> 1;
193
  SPDR = aa;
194
195
}
196
}
197
return 0;
198
}

: Bearbeitet durch User
von Thomas E. (picalic)


Lesenswert?

A. Audio schrieb:
> Dabei ist audio_ das Array mit den Werten aus der mp3, die ich ausgeben
> will.

Nur mal zur Klarstellung: Eine MP3-Datei kannst Du so nicht direkt 
ausgeben! Die musst Du vorher schon dekodieren und die Samples als 
Wellenform abspeichern.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ich vermute/hoffe, daß das hier impliziert, daß in ein Wave-Format 
gewandelt wurde:

A. Audio schrieb:
> Dafür muss ich natürlich die Qualität der Datei extrem vermindern, meine
> Schritte waren folgende:
> mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen und als
> headerless raw-file in unsigned 8-bit gespeichert.

von Maxim B. (max182)


Lesenswert?

>> mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen und als
>> headerless raw-file in unsigned 8-bit gespeichert.
Das kann AVR nicht schaffen. Dafür zu schwache Leistung
Wenn Datei im voraus auf dem Computer so vorbereitet, dass AVR wirklich 
nur WAV 8-bit bekommt - dann ist das möglich. Aber auch an Grenze.

Es gibt zwar eine Variante mit ATtiny85 oder 861A und PWM, aber die 
haben PWM-Taktfrequenz bis 64 MHz! Das hat ATmega328P nicht.

Dazu noch: was soll bedeuten "auf 1000Hz heruntersamplen"? Theorie sagt, 
dann ist Klang max. 500 Hz möglich. Praxis sagt aber, dass 500 Hz zu 
viele Verzerrungen haben wird, praktisch gelingt es mehr oder weniger 
sauber mit höchstens 200 Hz. Braucht jemand wirklich so einen Klang???

: Bearbeitet durch User
von Klaus (Gast)


Angehängte Dateien:

Lesenswert?

A. Audio schrieb:
> Ich werde es mir einmal ansehen, danke für den Tipp.
> Allerdings habe ich ja derzeit noch Probleme mit der Ausgabe.

Ich hab meinen Ansatz mal skiziert. Statt 7404 hab einen HC240 achtfach 
Buffer genommen. Die Kondensatoren sind im Bereich 1µF, die Spulen hab 
ich aus der Grabbelkiste. Ich bin nicht so der Filterexperte, ich hab 
probiert statt zu rechnen. Das funktioniert natürlich mit jedem PWM 
Signal, solange die Filterfrequenz und die PWM Frequenz passen.

MfG Klaus

von A. Audio (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> A. Audio schrieb:
>> Mein Problem ist aber, dass, wenn ich 0x80 (=128) länger ausgebe, einen
>> konstanten Ton erhalte.
>
> Du hast nicht verstanden, wie die DAC-Nachbildung mit PWM funktioniert,
> d.h. Du hast das zur Glättung nötige RC-Glied (Tiefpass) am Ausgang
> weggelassen.
>
> Das, was Du als Wert der PWM übergibst, entspricht einem Spannungspegel
> am Ausgang - 0 ist der geringste, 255 der größte und 128 der mittlere
> Pegel.

Maxim B. schrieb:
>> Weiterhin verstehe ich irgendwie deine Rechnung nicht ganz.
>> Weshalb *2^32?
> Weil max. Zahl in unsigned long 2^32-1 ist.
> Bei neueren Toolchains geht auch mit __uint24, für so eine Verwendung
> ist Genauigkeit von 2^24 ausreichend.
>
>> Ich habe es bisher immer so gemacht:
>> counter = INTERRUPTS_PER_S  frequency  2;
>> Dann in der ISR counter heruntergezählt und bei 0 den Pin getoggled.
> Das ist kein DDS, das ist etwas anderes. Viel weniger genau, was
> Frequenz betrifft. Z.B. 0,01 cent Genauigkeit kann man so nicht
> erreichen - gut wenn es unterhalb 2-3 cent bleibt.
>
>>> warum verwende ich nur die obersten 8 bits?
> Weil das reicht. Du kannst statt char auch int verwenden, nur mehr
> Speicherplatz und etwas langsamer. Wenn mehr als 2 Töne gleichzeitig,
> dann auch bei F_CPU = 20 MHz muß du Fd unter 32 kHz herabsetzen.

Thomas E. schrieb:
> A. Audio schrieb:
>> Dabei ist audio_ das Array mit den Werten aus der mp3, die ich ausgeben
>> will.
>
> Nur mal zur Klarstellung: Eine MP3-Datei kannst Du so nicht direkt
> ausgeben! Die musst Du vorher schon dekodieren und die Samples als
> Wellenform abspeichern.

Rufus Τ. F. schrieb:
> Ich vermute/hoffe, daß das hier impliziert, daß in ein Wave-Format
> gewandelt wurde:
>
> A. Audio schrieb:
>> Dafür muss ich natürlich die Qualität der Datei extrem vermindern, meine
>> Schritte waren folgende:
>> mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen und als
>> headerless raw-file in unsigned 8-bit gespeichert.

Ja. Die Samples wurden raw ohne Header in unsigned 8-bit values 
abgespeichert.

Maxim B. schrieb:
>>> mp3-Datei auf mono umstellen, auf 1000Hz heruntersamplen und
> als
>>> headerless raw-file in unsigned 8-bit gespeichert.
> Das kann AVR nicht schaffen. Dafür zu schwache Leistung
> Wenn Datei im voraus auf dem Computer so vorbereitet, dass AVR wirklich
> nur WAV 8-bit bekommt - dann ist das möglich. Aber auch an Grenze.
>
> Es gibt zwar eine Variante mit ATtiny85 oder 861A und PWM, aber die
> haben PWM-Taktfrequenz bis 64 MHz! Das hat ATmega328P nicht.
>
> Dazu noch: was soll bedeuten "auf 1000Hz heruntersamplen"? Theorie sagt,
> dann ist Klang max. 500 Hz möglich. Praxis sagt aber, dass 500 Hz zu
> viele Verzerrungen haben wird, praktisch gelingt es mehr oder weniger
> sauber mit höchstens 200 Hz. Braucht jemand wirklich so einen Klang???

Sampling Rate auf 1000Hz stellen.
Natürlich klingt es grottig.
Allerdings wollte ich zumindest die Software schon einmal fertigstellen, 
während das SD Card module noch auf dem Weg zu mir ist.
Gibt da ja offensichtlich genug zu tun.

Klaus schrieb:
> A. Audio schrieb:
>> Ich werde es mir einmal ansehen, danke für den Tipp.
>> Allerdings habe ich ja derzeit noch Probleme mit der Ausgabe.
>
> Ich hab meinen Ansatz mal skiziert. Statt 7404 hab einen HC240 achtfach
> Buffer genommen. Die Kondensatoren sind im Bereich 1µF, die Spulen hab
> ich aus der Grabbelkiste. Ich bin nicht so der Filterexperte, ich hab
> probiert statt zu rechnen. Das funktioniert natürlich mit jedem PWM
> Signal, solange die Filterfrequenz und die PWM Frequenz passen.
>
> MfG Klaus

Danke.
Allerdings habe ich gerade keinerlei freie Inverter da und diskret 
nachbauen ist vermutlich bei 7 Invertern ein wenig zu aufwendig. Wofür 
sind die denn eigentlich da in der Schaltung?

Allerdings habe ich jetzt auch einmal einen Tiefpass-Filter mit 
einfachem RC-Glied eingebaut (den habe ich tatsächlich vergessen). Mit 
Filter ist allerdings eigentlich einfach nur alles leiser. Egal ob 
"nichts" (=0x80) oder anderes. Bei 0x80 kommt somit auch weiterhin ein 
ziemlich hohes Piepen (wobei laut Berechnungen und auch Multimeter die 
Frequenz bei 62kHz liegt und somit eigentlich unhörbar sein müsste) und 
der Rest ist einfach nur unglaublich leise. Und ein Operationsverstärker 
wird die 0.2-0.3A nicht überleben.
Ich habe Kondensatoren von 10nF bis 2.2mF probiert. Je größer, desto 
leiser natürlich. Aber wahlweise man hört gar nichts mehr - weder von 
der eigentlichen Ausgabe noch von dem "Nullpunkt" bei 0x80 - oder eben 
immer dieses hohe Piepsen.
Gibt es irgendeine Möglichkeit, wie man nur das Piepsen (ca. 62.7kHz, 
2.5V) herausfiltert?
Oder soll ich einfach, wenn ich gerade nichts ausgebe, die Spannung dann 
einfach doch auf konstant 0V setzen?

Thomas E. schrieb:
> Klaus schrieb:
>> sollte die PWM
>> bei einer Frequenz von 8 kHz eine Auflösung von 11-12 Bit haben,
>
> Wozu denn eine so hohe Auflösung? Bei 8kHz ist das Sampleraster schon
> derart grob, daß einen da eine hohe Auflösung (> 60dB SNR) bei der
> Qualität auch nicht mehr groß 'rausreisst. Ich würde da eher auf eine
> höhere Abtastfrequenz gehen und die PWM-Frequenz möglichst erhöhen,
> damit man keinen so steilflankigen Tiefpass nachschalten muss. Für
> Telefonqualität reicht doch SNR = 48 dB (8 Bit Aufösung) locker. Evtl.
> wäre es sogar in der Praxis besser, noch ein Bit zugunsten einer höheren
> PWM-Frequenz zu opfern. Ein 7-Bit Timer mit 16 MHz gibt 125kHz, davon
> bleibt selbst mit einem simplen, 1-stufigen RC-Filter nicht mehr viel
> übrig.
> Also, mein "Rezept" für brauchbare Qualität mit geringem Aufwand wäre:
> 8-Bit samplen, aber dafür >= 16kHz Abtastrate (relativ weit weg vom
> Nutzsignal) und noch höhere PWM Frequenz (sehr weit weg vom Nutzsignal).
>
> Beim Telefon wird wahrscheinlich mit einer höheren Auflösung gesampled,
> um den Dynamikbereich zu erhöhen, d.h. es soll ja auch noch etwas zu
> verstehen sein, wenn der Sprecher mal etwas leisere Töne anschlägt. Zur
> Ausgabe eines Signaltons braucht es das aber nicht, da man den immer mit
> optimaler (Voll-)Aussteuerung generieren kann.

Den Beitrag hier habe ich letztes Mal offensichtlich übersehen.
Das Problem ist, dass die Abtastrate eben genau so speicherraubend ist. 
Aber ich versuche sonst einfach einmal, ein kürzeres Beispiel zu nehmen, 
das ich aber einfach ganz oft wiederhole. Dann kann ich zumindest 
schauen, ob die Tonhöhe getroffen wird.

Danke euch allen auch noch einmal für eure Geduld.

von Klaus (Gast)


Lesenswert?

Thomas E. schrieb:
> Klaus schrieb:
>> sollte die PWM
>> bei einer Frequenz von 8 kHz eine Auflösung von 11-12 Bit haben,
>
> Wozu denn eine so hohe Auflösung? Bei 8kHz ist das Sampleraster schon
> derart grob, daß einen da eine hohe Auflösung (> 60dB SNR) bei der
> Qualität auch nicht mehr groß 'rausreisst. Ich würde da eher auf eine
> höhere Abtastfrequenz gehen und die PWM-Frequenz möglichst erhöhen,
> damit man keinen so steilflankigen Tiefpass nachschalten muss.

Es geht nicht darum was du würdest, es geht danach was im weltweiten 
Telefonnetz gemacht wird. Ich hab beschrieben, wie man leicht diese 
Signale erzeugen (audacity) und ausgeben kann. Die Dynamik kannst du 
auch gut brauchen, wenn du digital die Lautstärke ändern willst.

A. Audio schrieb:
> Wofür
> sind die denn eigentlich da in der Schaltung?

Die liefern den Strom für den Lautsprecher, ein Class-D Verstärker für 
Arme, wie ich schrieb. Das für den Class-D nötige Filter ist auch 
gleichzeitig das Filter für die PWM Frequenz.

MfG Klaus

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Application Note AVR314 bei Microchip beschreibt die Ausgabe von DTMF 
(Touch-) Tönen mit jedem beliebigen AVR. Das sind also immer 2 Töne. Das 
ist sehr viel einfacher als die meisten Vorschläge, die bis jetzt 
gemacht wurden.
Einfach mal anschauen.

von chris (Gast)


Lesenswert?

Klaus:
>Die Kondensatoren sind im Bereich 1µF, die Spulen hab
>ich aus der Grabbelkiste.

Hallo Klaus,
die genauen Werte für LC wären interessant. Könntest Du eine Aufnahme 
mit-/ohne LC Filter machen? Mich würde interessieren, ob man den 
Unterschied gut hört.

von Klaus (Gast)


Lesenswert?

chris schrieb:
> die genauen Werte für LC wären interessant. Könntest Du eine Aufnahme
> mit-/ohne LC Filter machen? Mich würde interessieren, ob man den
> Unterschied gut hört.

Ich hab den Aufbau nicht lauffähig. Aber ich kann dir sagen, ohne Filter 
hälst die 8kHz nicht aus, mit einer Stufe stört es, bei zwei Stufen ist 
es brauchbar. Die Lautstärke hat sich insgesamt kaum verändert.

Die Drosseln sind aus einem Projekt übrig geblieben und haben so 1 bis 2 
mHµ. Wenn man es richtig macht, könnte man wohl auch statt der zwei 
Drosseln pro Filterstufe eine Doppeldrossel nehmen. Ich bin nicht so der 
Filterspezialist, aber ich weiß, daß bei passiven Filtern die 
Ausgangsimpedanz des Treibers auf der einen und die Impedanz der Last, 
des Lautsprechers auf der anderen Seite eine wichtige Rolle spielt.

MfG Klaus

von Thomas E. (picalic)


Angehängte Dateien:

Lesenswert?

chris schrieb:
> die genauen Werte für LC wären interessant.

Da habt's was zum Experimentieren...

von Thomas E. (picalic)


Lesenswert?

Klaus schrieb:
> Es geht nicht darum was du würdest, es geht danach was im weltweiten
> Telefonnetz gemacht wird.

So? Und wer bestimmt das, wonach es geht? Ich kann mich nicht erinnern, 
gelesen zu haben, daß der TO aus seinem AVR ein Telefon bauen will. Ob 
das, was im weltweiten Telefonnetz gemacht wird, auch wirklich das Ideal 
für seine Anwendung ist, kann man wohl kaum so einfach bestimmen, wie Du 
es tust. Geht es ihm eher um Sprachverständlichkeit bei großem 
Dynamikumfang oder um einen  klirrarmen Sound eines akustischen Signals? 
Mit bloß 15 diskreten Werten im Bereich über der halben Amplitude wird 
jedenfalls ein Sinus z.B. schon ganz schön "verbogen", dh. es wird 
effektiv systembedingt ein Störsignal von min. 1/32 der Amplitude 
aufmoduliert. Und ist er gewillt, den Aufwand eines  steilflankigen 
Tiefpasses höherer Ordnung zu treiben?

von Klaus (Gast)


Lesenswert?

Thomas E. schrieb:
> So? Und wer bestimmt das, wonach es geht? Ich kann mich nicht erinnern,
> gelesen zu haben, daß der TO aus seinem AVR ein Telefon bauen will

Er hat gefragt, wie man einfach audio mit seinem µC ausgeben kann. Und 
ich hab gesagt, ich hab das mal so gemacht. Das ist alles. Er hat dann 
noch Einzelheiten abgefragt. Das du alles besser kannst, ist klar. 
Trotzdem habe ich das nicht berücksichtig, als ich das mal gemacht und 
werde es auch nicht tun. Was der TO nun macht, ist sein Ding.

MfG Klaus

von Thomas E. (picalic)


Lesenswert?

Klaus schrieb:
> Und
> ich hab gesagt, ich hab das mal so gemacht. Das ist alles.

Ja und? Habe ich irgendwo geschrieben, daß das falsch wäre?
Ich habe auch nur geschrieben, wie ich es machen würde, und zwar aus 
diesem oder jenem Grund. Ich kann leider im Gegensatz zu Dir nicht 
schreiben, daß ich das schon so gemacht habe, weil das gelogen wäre. Ich 
habe aber nicht etwas in der Art geschrieben: "ist egal, was der Klaus 
macht oder vorschlägt, das macht man anders!" Das wäre in meinen Augen 
besserwisserisch.

So, und jetzt schreibst Du "Es geht nicht darum was du würdest, es geht 
danach was im weltweiten Telefonnetz gemacht wird." und stellst mich als 
Besserwisser dar?

Oder habe ich da vielleicht etwas falsch interpretiert?

von Klaus (Gast)


Lesenswert?

Thomas E. schrieb:
> Oder habe ich da vielleicht etwas falsch interpretiert?

Nein

MfG Klaus

von Falk B. (falk)


Lesenswert?


von Maxim B. (max182)


Lesenswert?

Falk B. schrieb:
> Beitrag "Re: Arduino Nano, SD Card, PCM"

Sehr, sehr interessant! Vielen Dank!
Ich experimentiere auch in so einer Richtung, wenn auch ein bißchen 
anderes (ein Sampler. Ich brauche etwas für Beerdigungen, was bessere 
Orgelklang liefert als ein Yamaha-Keyboard, aber von AA-Batterien auch 
läuft). Aber hier sehe ich gute Material auch für mich.

von Joachim B. (jar)


Lesenswert?

Curby23523 N. schrieb:
> Arduino und Atmega328 sind hier meiner Meinung nach völlig ungeeignet

echt?

1-bit PWM hatten schon der apple2 und auch der CBM mit VIA6222

klar hört sich das nicht so toll an wie vom Soundchip, aber es geht.

A. Audio schrieb:
> Nun habe ich einen Lautsprecher, den ich gerne ansteuern möchte.

Beitrag "1-bit audio example program for Atmega"
http://forum.arduino.cc/index.php?topic=455680.0

von Bernd K. (prof7bit)


Lesenswert?

Du könntest statt R2R auch versuchen einen Delta Sigma DAC zu bauen.

von avr (Gast)


Lesenswert?

Bernd K. schrieb:
> Du könntest statt R2R auch versuchen einen Delta Sigma DAC zu bauen.

Aber nicht mit einem AVR und schon gar nicht in C. Für einigermaßen gute 
Audioqualität habe ich 30 MHz bei einem cortex M4 in Assembler benötigt 
und gut das doppelte in C.

von Thomas E. (picalic)


Lesenswert?

A. Audio schrieb:
> Bei 0x80 kommt somit auch weiterhin ein
> ziemlich hohes Piepen (wobei laut Berechnungen und auch Multimeter die
> Frequenz bei 62kHz liegt und somit eigentlich unhörbar sein müsste)

Da ist sicher etwas anderes faul! Wenn Du keine Fledermaus bist, kannst 
Du 62 kHz nicht hören!

A. Audio schrieb:
> und
> der Rest ist einfach nur unglaublich leise. Und ein Operationsverstärker
> wird die 0.2-0.3A nicht überleben.

Was denn für 0.2-0.3A? Woher kommen die und wie sieht Deine Schaltung 
hinter dem 62kHz Rechtecksignal überhaupt aus?

Wie gesagt, egal, wie schlecht Dein Filter ist, von 62kHz dürfte vom 
menschlichen Ohr nichts wahrgenommen werden - da muss ein anderer Fehler 
vorliegen!

Läuft Dein Programm vielleicht in einer Schleife und initialisiert die 
PWM ständig neu? Möglicherweise mit einer Rate, die im 
Tonfrequenzbereich liegt?

A. Audio schrieb:
> Das Problem ist, dass die Abtastrate eben genau so speicherraubend ist.

Du kannst auch mit nur 8kHz samplen - meine 16kHz waren ja nur der 
Vorschlag, um weiter von der Nutzfrequenz wegzukommen, damit der 
Tiefpass einfacher wird. Wenn ich's richtig verstanden habe, sollen die 
Daten später sowieso von einer SD-Karte kommen, da ist der 
Speicherverbrauch dann ja auch nicht mehr so kritisch. Wichtiger ist 
wohl, bei der PWM-Frequenz im unhörbaren Bereich zu bleiben.

von Eric B. (beric)


Lesenswert?

Thomas E. schrieb:
> A. Audio schrieb:
>> Bei 0x80 kommt somit auch weiterhin ein
>> ziemlich hohes Piepen (wobei laut Berechnungen und auch Multimeter die
>> Frequenz bei 62kHz liegt und somit eigentlich unhörbar sein müsste)
>
> Da ist sicher etwas anderes faul! Wenn Du keine Fledermaus bist, kannst
> Du 62 kHz nicht hören!

Ein Rechteck ist aber kein reiner Sinus. Zwischen allen mitschwingenden 
Harmonischen ist bestimmt was hörbares dabei.

von Thomas E. (picalic)


Lesenswert?

Eric B. schrieb:
> Ein Rechteck ist aber kein reiner Sinus. Zwischen allen mitschwingenden
> Harmonischen ist bestimmt was hörbares dabei.

Nein, da gibt es nichts unterhalb der Grundfrequenz, d.h. hier also 
nichts unter 62 kHz.

von Tom (Gast)


Lesenswert?

Hallo,

Es gab hier mal das Projekt von Ulrich Radig 
(Beitrag "MegaLOL").
Wenn mich nicht alles täuscht wurde damit genau das vom TO gewünschte 
bereits gemacht.

BG, Tom

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.