Guten Tag. Ich möchte einen Ton per PWM erzeugen, aber weiß nicht so recht wie ich das anstellen soll. PWM Frequenz ist immer konstant, das ist klar. Die Tonhöhe kann man ändern indem man das Puls-Pausen-Verhältnis ändert und quasi eine Sinus Frequenz ändert. Aber WIE kann man die Lautstärke regeln? Das verstehe ich gerade nicht :( Logischerweise auch über Puls-Pau-Verhältnis, aber darüber wird doch schon die Tonhöhe geregelt. Bitte, klärt mich auf. Danke Gruß Alex
Alex schrieb: > Aber WIE kann man die Lautstärke regeln? Das verstehe ich gerade nicht > :( Über den Modulationsgrad (falls man das bei PWM aus so nennt). Wenn dein aufmodulierter Sinus das Tastverhältnis von 0% bis 99% ändert ist er natürlich lauster als einer der es nur von 45% bis 55% ändert.
@ Alex (Gast)
>PWM Frequenz ist immer konstant, das ist klar.
Nein, das ist nicht allgemein klar. Denn es gibt 2 Möglichkeiten.
a) Der einfache Ansatz. Die PWM erzeugt DIREKT die gewünschte Frequenz.
- PWM-Frequenz = Tonhöhe (variabel)
- PWM-Tastverhältnis = "Tonfarbe" (Oberwellen)
- Lautstärke = konstant
- Kurvenform = konstant (Rechteck)
b) Der komplexere Ansatz. Die PWM + nachgeschalteter Filter arbeitet als
Audio-DAC
- PWM-Frequenz = Ausgabetakt (konstant, relativ hoch > 8kHz)
- PWM-Tastverhältnis = Amplitude des Audiosamples (Lautstärke)
- Kurvenform = beliebig (Audiosample)
Dir muss klar sein, dass die PWM nur MIttel zum Zweck ist, um damit so etwas ähnliches wie eine Spannung variabler Höhe zu erzielen. Eine externe Beschaltung (das kann auch ein gewöhlicher Lautsprecher sein) mittelt dir das Puls/Pausenverhältnis dahingehend aus, dass du durch Variation desselben genau dasselbe erreichen kannst, wie wenn du unterschiedlich hohe Spannungen an den Lautsprecher angelegt hättest. Wie entsteht nun ein Ton mit einer bestimmten Frequenz? Der entsteht dadurch, dass du die angelegte Spannung in der Zeit variierst. Zuerst legst du 0V an, dann 0.1V, dann 0.2V etc. etc. Die Membran des Lautsprechers folgt dieser Spannung, d.h. sie wird (na ja) genau so weit ausgelenkt, wie es die Spannung vorgibt. D.h. legst du eine Wechselspannung an, die pro Sekunde sagen wir mal 440 mal einen Spannungszyklus durchläuft der wie ein Sinus aussieht, dann folgt natürlich die Membran dieser Spannung und du hörst einen Ton. Hier ist es dann der berühmte Kammerton 'A'. Wie laut ist der Ton. Das hängt davon ab, wie weit die Membran ausgelenkt wird. Durchläuft die auf ihrem 'Weg' durch den Sinus nur kleine Auslenkungen, dann ist der Ton leise. Durchläuft sie große Auslenkungen, dann ist der Ton laut. Warum ist das so? Weil bei einer großen Auslenkung von der Membran dementsprechend ein größeres Luftvolumen hin und her geschoben wird. Wir hören das dann eben als lauter als wenn nur winzige Luftmengen von der Membran geschoben werden. Um einen leisen Ton zu erzeugen, legst du an die Membran zb abwechselnd die Spannungen 0.5V und 0.6V an (und das 440 mal pro Sekunde). Dementsprechend schnappt die Membran auch nur zwischen diesen beiden nahe beeinander liegenden Pegeln hin und her. Legst du 440 mal in der Sekunde abwechselnd die Spannungen 0.4V und 0.7V an, dann wird die Membran zwar genau so oft umgeschnappt wie vorher, aber eben auch dem Spannungspegel entsprechend weiter ausgelenkt. Der Ton wird lauter. (Und die Kurvenform ist durch die lediglich 2 möglichen Spannungen natürlich kein Sinus mehr, sondern eine Rechteckschwingung. Aber es geht ja auch ums Prinzip, und so brauch ich nicht mit haufenweisen Zahlen hantieren). Die PWM, die du erzeugst, ist nur Hilfsmittel um aus einem Digitalpin ein Analogon zu einer variablen Spannung erzeugen zu können. Mit der eigentlichen Tonerzeugung hat sie eher weniger zu tun.
:
Bearbeitet durch User
Vielen Dank! Ich denke, ich verstehe das irgendwo unterbewusst :) Also ich habe eine Vorstellung wie das funktioniert, muss aber den Gedanken im Kopf reifen lassen. Noch habe ich keine Ahnung wie ich das programmieren könnte (wenn ich das wollte) :) Gruß Alex
Hallo noch mal, das Prinzip habe ich durch das Ausprobieren verstanden. Nun möchte ich einen Ton aufzeichnen und wiedergeben. Welcher Ton das sein soll, ist erst mal egal. Ein Ton, wo der Taschenrechner vom Tisch fällt zum Beispiel. Wie kann ich das anstellen? Also nicht den Taschenrechner runter schmeißen, sondern den Ton richtig aufzeichnen und ein Sample davon erstellen. Um den Ton nachher wiedergeben zu können, muss ich zu einem bestimmten Zeitpunkt die passende Frequenz haben. Das bedeutet irgendwie den ADC Wert in PWM Wert umrechnen, der dem Ton entspricht. Habe extra keine bestimmte Frequenz ausgesucht, weil das Piepen oder Brummen langweilig ist. Kann mir da jemand auf die Sprünge helfen? Danke & Gruß Alex
@ Alex (Gast) >Nun möchte ich einen Ton aufzeichnen und wiedergeben. Das macht der Soundrecorder im Windows. Mit einem Mikrophon/Headset am PC kann man Töne aufzeichnen. >Wie kann ich das anstellen? Also nicht den Taschenrechner runter >schmeißen, sondern den Ton richtig aufzeichnen und ein Sample davon >erstellen. Eben dazu MUSSt du den Taschenrechner runterschmeißen! Wie sonst? Man kann auch Töne synthetisch erzeugen, dazu gibt es diverse PC-Programme. Damit aber natürliche Töne nachzumachen, ist gar nicht so leicht. >Um den Ton nachher wiedergeben zu können, muss ich zu einem bestimmten >Zeitpunkt die passende Frequenz haben. Falsche Denkweise. Bei der Arbeit mit Samples gibt man einfach den Kurvenverlauf des Audiosignals 1:1 wieder, welchen man vorher aufgenommen hat. Welche Frequenzen da drin stecken, spielt in dem Moment keine Rolle. >Kann mir da jemand auf die Sprünge helfen? http://elm-chan.org/works/sd20p/report.html Sample auf die SD-Karte kopieren, los gehts. Kann man bei ELV auch fertig kaufen. http://www.elv.de/mp3-sound-modul-msm-2-komplettbausatz.html Das Ding arbeitet mit einfachen PCM Samples als auch MP3, aber das kann man am PC einfach umwandeln. Wenn man es aber weniger BlackBox artig haben will und WIRKLICH sehen will, wie es funktioniert, kann man auch einfach einen größeren uC mit ausreichend Flash nehmen, 32kB sollten erstmal reichen. Dort kann man ein kleines Sample mit vielleicht 8 kHz Abtastfrequenz direkt in den Flash packen und direkt per PWM abspielen.
>Wenn man es aber weniger BlackBox artig haben will und WIRKLICH sehen >will, wie es funktioniert, kann man auch einfach einen größeren uC mit >ausreichend Flash nehmen, 32kB sollten erstmal reichen. Dort kann man >ein kleines Sample mit vielleicht 8 kHz Abtastfrequenz direkt in den >Flash packen und direkt per PWM abspielen. Hallo Frank, danke für deine Antwort. Das fertige Gerät interessiert mich natürlich nicht. Ich möchte den Sound schon selbst erzeugen (können). Meine PWM habe ich bei den Versuchen auf 50kHz eingestellt, also sollte ich den herunterfallenden Taschenrechner auch mit 50kHz abtasten, oder? Ich kann natürlich meine PWM Frequenz ändern, wenn es sich rausstellt, dann die andere Frequenz besser zum Rechnen/abspielen ist. Für ca. 0,5Sek, wo das Ding Krach macht, soll der Flash von 200kb reichen :) Mir ist halt noch unklar, wie ich die aufgenommene Audio Datei im Zahlen zerlege und vor allem, wie ich diese "Variable" abspiele :) Eigentlich war das die Frage. Danke & LG Alex
@Alex (Gast) >Hallo Frank, Wer ist das? >Meine PWM habe ich bei den Versuchen auf 50kHz eingestellt, also sollte >ich den herunterfallenden Taschenrechner auch mit 50kHz abtasten, oder? Du solltest ihn auffangen, damit er nicht kaputt geht ;-) >Ich kann natürlich meine PWM Frequenz ändern, wenn es sich rausstellt, >dann die andere Frequenz besser zum Rechnen/abspielen ist. >Für ca. 0,5Sek, wo das Ding Krach macht, soll der Flash von 200kb >reichen :) Ja. Auf einer CD sind es "nur" 44,1 kHz und 16 Bit/Sample. Für den Hausgebraucht reichen 22kHz und 8 Bit, mehr macht deine PWM meist nicht (Jaja, man kann 10 Bit und mehr machen, dann geht aber die Frequenz ganz schnell runter). >Mir ist halt noch unklar, wie ich die aufgenommene Audio Datei im Zahlen >zerlege und vor allem, wie ich diese "Variable" abspiele :) >Eigentlich war das die Frage. Sehr gut erkannt! Dazu gibt es mehrere Möglichkeiten. 1.) Wav ist aber ein einfaches Format mit einem praktisch Konstanten Header. Diesen kann man im Programm einfach überspringen und danach direkt auf die Tondaten zugreifen. Man muss nur halt im 8 Bit/22kHz/Mono Format speichern. Die Daten haben nur 22 kHz Abtastrate, die PWM sollte man aber so hoch wie möglich laufen lassen. Damit vereinfacht sich der Filter am Ausgang. 2.) Das Einbinden in das C/ASM Programm. a) Es gibt diverse Tools im Internet ala Bin to C source, muss man mal googlen. Die Wandlen beliebige Binärdaten in Textdateien um, welche eine Konstante initialisieren. Etwas so char meine_Daten[5000] = {0x08, 0x15, 0x00, .......}; http://stackoverflow.com/questions/8707183/script-tool-to-convert-file-to-c-c-source-code-array http://stahlworks.com/dev/?tool=bintosrc Kann man machen, aber erzeugt natürlich riesige Dateien, aber bei den heuten PCs ist das kein Problem. b) der clevere Weg ist das direkte Einbinden von Binärdaten in das Programm. Das geht über den Linker. Beim avr gcc geht das so. http://www.atmel.com/webdoc/avrlibcreferencemanual/FAQ_1faq_binarydata.html Wenn du einen anderen Compiler/uC hast, musst du dort in der Doku suchen.
Ach so, das Abspielen ist einfach. Man nutzt einen Timer, um mit der bekannten Abtastrate die Daten in das PWM-Modul zu schreiben. etwa so.
1 | char *pwm_data= &meine_daten; |
2 | |
3 | ISR(TIMER0_OVF_vect) { |
4 | OCR0A = *pwm_data++; |
5 | if (pwm_data == (&meine_daten + sizeof (meine_daten)) ){ |
6 | pwm_data = &meine_daten; // Ende erreicht, neu anfangen |
7 | }
|
8 | }
|
Vielen Dank Falk, hast mir sehr geholfen!
Ich werde jetzt im Internet irgendeine WAV Datei suchen.
>Diesen kann man im Programm einfach überspringen und danach direkt auf >die
Tondaten zugreifen.
Aber von welchem Programm ist hier die Rede? :) In meinem C Programm,
oder im Programm, dass aus der WAV Datei eine Variable(&meine_daten)
macht?
Oder ist die Variable "meine_daten" die WAV Datei selbst?
Sonst habe ich alles (glaube ich) verstanden :)
Danke dir noch mal
@Alex (Gast) >Ich werde jetzt im Internet irgendeine WAV Datei suchen. Ob du dort eine findest? >>Diesen kann man im Programm einfach überspringen und danach direkt auf >>die Tondaten zugreifen. >Aber von welchem Programm ist hier die Rede? :) In meinem C Programm, Ja. >oder im Programm, dass aus der WAV Datei eine Variable(&meine_daten) >macht? Nein. >Oder ist die Variable "meine_daten" die WAV Datei selbst? Ja! >Sonst habe ich alles (glaube ich) verstanden :) Na dann mal los! Viel Erfolg!
Ich hab mal fix das Einbinden getestet, es geht einfach. Die Datei foo.bin mit den Binärdaten sinnvollerweise in den Projektordner oder Ordner default im Projetverzeichnis kopieren. In der Kommandozeile mittels CD dort reingehen und diesen Befehl ausführen avr-objcopy --rename-section .data=.progmem.data,contents,alloc,load,readonly,data -I binary -O elf32-avr foo.bin foo.o Damit wird die Datei foo.o erzeugt.
1 | #include <avr/io.h> |
2 | #include <avr/pgmspace.h> |
3 | |
4 | extern const char _binary_foo_bin_start[] PROGMEM; |
5 | extern const char _binary_foo_bin_end[] PROGMEM; |
6 | |
7 | int main(void) { |
8 | int size, i; |
9 | |
10 | size = _binary_foo_bin_end - _binary_foo_bin_start; |
11 | |
12 | DDRC=0xFF; |
13 | |
14 | for (i=0; i<size; i++) { |
15 | PORTC = pgm_read_byte(&_binary_foo_bin_start[i]); |
16 | }
|
17 | }
|
Man muss nur noch das Objektfile dem Linker mitteilen, das geht in den Projektoptionen unter AVR Studio 4.18 so. Project -> Configuration Options -> Librabies -> Add Object Siehe Anhang. Im Atmelstudio 6.2 ist das im Fenster Solution Explorer unter Libraries -> rechte Maustaste -> Add Library -> Browse Libraries -> Browse -> foo.o auswählen Hmmm, aber irgendwie mag Atmelstudio meine foo.o nicht, obwohl sie vorhanden ist.
1 | Error 1 cannot find -lfoo.o 1 1 test_link_atmelstudio |
2 | Error 2 ld returned 1 exit status collect2.exe 0 0 test_link_atmelstudio |
Hat jemand eine Idee?
:
Bearbeitet durch User
Ich habe nun eine WAV Datei runtergeladen. Ist im Anhang.
Dann wollte ich die "nach Zahlen" konvertieren.
Als Programm habe ich GoldWave benutzt.
Ich kann das leider nur als txt und als Dezimalzahl abspeichern.
Wie soll ich denn die WAV Datei in mein Code rein bekommen? :))
Wollte sowas wie Falk geschrieben hat
>char meine_Daten[5000] = {0x08, 0x15, 0x00, .......};
machen.
Die geht das? :)
Danke schön
Danke Falk, ich schätze deine Mühe sehr. Das ist hier im Forum nicht üblich. Leider benutze ich keine AVRs und logischerweise kein Atmelstudio. Somit helfen mir deine mit Liebe gemachten Screenshots und Beschreibung nicht :(
Falk Brunner schrieb: > Error 1 cannot find -lfoo.o 1 1 test_link_atmelstudio > Error 2 ld returned 1 exit status collect2.exe 0 0 > test_link_atmelstudio > > Hat jemand eine Idee? Nicht als Library einbinden.
@Alex (Gast) >Ich habe nun eine WAV Datei runtergeladen. Ist im Anhang. >Dann wollte ich die "nach Zahlen" konvertieren. >Als Programm habe ich GoldWave benutzt. Falsches Format. >Die geht das? :) Schrub ich das nicht? Siehe Anhang. Damit kannst du in Zukunft auch andere Dateien umwandeln.
http://soundfile.sapp.org/doc/WaveFormat/ Also einfach die ersten 44 Bytes überspringen, dort beginnen die eigentlichen Tondaten.
Guten Abend, ich probiere immer sehr lange aus, bevor ich nach Hilfe schreie. Leider funktioniert der Ton nicht. hier ist der "code" einfach als Ablauf
1 | char Ton[4208] = { //22,0kHz 8bit |
2 | 0x52, 0x49, 0x46, 0x46, 0x68, 0x10, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, // ... usw 4208 Werte sind das insgesamt geworden |
3 | };
|
4 | |
5 | char *pwm_data= &Ton; |
6 | |
7 | /*
|
8 | Timer auf 22kHz initialisiert
|
9 | PWM auf 60kHz initialisiert
|
10 | Timer gestertet
|
11 | */
|
12 | |
13 | // Timerinterrupt
|
14 | {
|
15 | Pin toggeln um die Frequenz von Timer mit dem Oszilloscope zu kontrollieren |
16 | PWM_Duty = *pwm_data++; |
17 | if (pwm_data == (&Ton + sizeof (Ton)) ){ |
18 | pwm_data = &Ton; // Ende erreicht, neu anfangen |
19 | }
|
20 | }
|
so im Groben. Es kommt Ton raus, aber das ist eher Müll als Ton :( PWM Frequenz habe ich mit dem Oszi nachgeguckt, die läuft stabil mit 60kHz Der Pin im Timerinterrupt wechselt mit 22kHz (jede 45-igste uS) den Pegel. Was da genau passiert, warum das nicht funktioniert kann ich nicht mal sagen. Es geht halt alles viel zu schnell. Ich schätze in der Timerinterrupt routine ist etwas falsch, da der Duty Wert nach (beim) jeden " if (pwm_data == (&Ton + sizeof (Ton)) ){" auf 255 ist, was eigentlich nicht sein darf. Kann jemand die Routine noch mal angucken, bitte? Danke schön. Viele Grüße
Falk Brunner schrieb im Beitrag #4056951: >>Nicht als Library einbinden. > > Wie dann? Im AVR Studio geht es. Ich arbeite nicht mit dem AtmelStudio. Letztlich muss die foo.o beim Linker-Aufruf zusammen mit den anderen Object-Files genannt werden. Falk Brunner schrieb im Beitrag #4056910: > Error 1 cannot find -lfoo.o 1 1 test_link_atmelstudio > Error 2 ld returned 1 exit status collect2.exe 0 0 test_link_atmelstudio Das "-lfoo.o" bedeutet, dass nach einer Datei "libfoo.o.a" gesucht wird, also nach der Library "foo.o". Die hast du nicht gebaut, nur das Object-File "foo.o". Entweder "foo.o" als Object-File linken oder mit avr-ar aus "foo.o" das Library-File "libfoo.o.a" erzeugen.
@ Alex (Gast) >so im Groben. Das kanst du dir in die Haare schmieren. Poste vollständigen, ORIGINALEN Quelltext als Anhang. >Es kommt Ton raus, aber das ist eher Müll als Ton :( Wie sieht deine Aussenbeschaltung aus? Hast du einen Entkoppelkondensator in Reihe zum PWM-Ausgang? Denn dein uC macht keine negativen Spannungen, dein Kopfhörer hätte aber gern welche. Ausserdem muss man aufpassen, dass die PWM im gepufferten Modus arbeitet. Denn wenn man duch einen dummen Zufall duch das einschreiben eines neuen PWM-Wertes die PWM kurzzeitig abwürgt, kommen komische Signale zustande. >Was da genau passiert, warum das nicht funktioniert kann ich nicht mal >sagen. >Es geht halt alles viel zu schnell. >Ich schätze in der Timerinterrupt routine ist etwas falsch, da der Duty Darum sollte man diese im ORIGINAL sehen. >Wert nach (beim) jeden " if (pwm_data == (&Ton + sizeof (Ton)) ){" >auf 255 ist, was eigentlich nicht sein darf. >Kann jemand die Routine noch mal angucken, bitte? Deine Fragmente nützen niemandem etwas, siehe Netiquette.
Falk B. schrieb: > Ich hab mal fix das Einbinden getestet, es geht einfach. > > Die Datei foo.bin mit den Binärdaten sinnvollerweise in den > Projektordner oder Ordner default im Projetverzeichnis kopieren. > In der Kommandozeile mittels CD dort reingehen und diesen Befehl > ausführen > > avr-objcopy --rename-section > .data=.progmem.data,contents,alloc,load,readonly,data -I binary -O > elf32-avr foo.bin foo.o > > Damit wird die Datei foo.o erzeugt. >
1 | > #include <avr/io.h> |
2 | > #include <avr/pgmspace.h> |
3 | >
|
4 | > extern const char _binary_foo_bin_start[] PROGMEM; |
5 | > extern const char _binary_foo_bin_end[] PROGMEM; |
6 | >
|
7 | > int main(void) { |
8 | > int size, i; |
9 | >
|
10 | > size = _binary_foo_bin_end - _binary_foo_bin_start; |
11 | >
|
12 | > DDRC=0xFF; |
13 | >
|
14 | > for (i=0; i<size; i++) { |
15 | > PORTC = pgm_read_byte(&_binary_foo_bin_start[i]); |
16 | > } |
17 | > } |
... 10 Jahre später: C23 bringt #embed, womit man eine (Binär)Datei direkt im C-Code verwenden kann:
1 | #include <stddef.h> |
2 | #include <avr/io.h> |
3 | |
4 | static const __flash uint8_t foo_bin[] = |
5 | {
|
6 | #embed "foo.bin"
|
7 | };
|
8 | |
9 | int main () |
10 | {
|
11 | for (size_t i = 0; i < sizeof (foo_bin); ++i) |
12 | {
|
13 | PORTC = foo_bin[i]; |
14 | }
|
15 | }
|
Praktischerweise kann man auch andere Sachen mit ins Array packen, zum Beispiel eine abschließende \0 um einen C-String zu bekommen:
1 | static const __flash char foo_c[] = |
2 | {
|
3 | #embed "foo.c"
|
4 | , '\0' |
5 | };
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.