Forum: Mikrocontroller und Digitale Elektronik ATmega 328P Sprachausgabe - Frequenz von Interrupt bestimmen


von Tobias S. (t0b14s)


Lesenswert?

Hallo Leute,


ich bin gerade dabei mit einem ATmega 328P ein Projekt mit Sprachausgabe 
zu programmieren. Die Soundfiles werden von einer SD-Karte gelesen und 
sollen über PWM an einen Lautsprecher ausgegeben werden. Es wird immer 
ein Block mit 512Bytes von der SD-Karte gelesen und abwechselnd in 
Puffer1 und Puffer2 geschrieben.
An den ATmega ist ein 8MHz Quarz angeschlossen.

Ich verwende OCR0A für die Ausgabe an den Lautsprecher. Dort habe ich 
Fast PWM eingestellt und verwende keinen Prescaler. Die Init sieht 
folgendermaßen aus:
1
TCCR0A &= ~(1 << COM0A0);
2
TCCR0A |= (1 << COM0A1);
3
TCCR0A |= (1 << WGM00);
4
TCCR0A |= (1 << WGM01);
5
TCCR0A &= ~(1 << WGM02);
6
7
TCCR0B |= (1 << CS00);
8
TCCR0B &= ~(1 << CS01);
9
TCCR0B &= ~(1 << CS02);

Mit OCR1A soll ein Interrupt im 44100Hz-Takt aufgerufen werden, in dem 
dann die Bytes aus dem Puffer auf OCR0A geschrieben werden. Der Timer 
soll in CTC Mode laufen mit einem Prescaler von 8. Hier die 
Initialisierung dazu:
1
TCCR1A &= ~(1<<COM1A1);  
2
TCCR1A &= ~(1<<COM1A0);
3
TCCR1A &= ~(1<<WGM11);
4
TCCR1A &= ~(1<<WGM10);
5
6
TCCR1B &= ~(1<<WGM13);   
7
TCCR1B |= (1<<WGM12);
8
TCCR1B &= ~(1<<CS12);
9
TCCR1B |= (1<<CS11);
10
TCCR1B &= ~(1<<CS10);
11
12
OCR1A = ((8000000 / 8) / 44100 )- 1;
13
14
TIMSK1 |= (1<<OCIE1A);                    
15
TIFR1  |= (1<<OCF1A);

Wenn eine Datei von der SD-Karte abgespielt werden soll, dann kommt es 
mir so vor, als ob der Interrupt etwas zu schnell aufgerufen wird. Habe 
ich
hier einen Denkfehler bei der Initialisierung der Timer?

Die Tonhöhe der Sprache ist korrekt, aber ich glaube, dass die einzelnen 
Bytes zu schnell nacheinander ausgegeben werden. Hier die 
Interrupt-Routine:

1
ISR(TIMER1_COMPA_vect){
2
3
  if (useBuffer == 1 && buffer1Full == 1) {
4
5
    OCR0A= playBuffer1[sampleCount];
6
    sampleCount++;
7
  }
8
  else if(useBuffer == 2 && buffer2Full == 1){
9
10
    OCR0A= playBuffer2[sampleCount];
11
    sampleCount++;
12
  }
13
14
  if (sampleCount >= PLAY_BUFFER_SIZE ) {  
15
    if (lastByte == 1) {    
16
      filePlaying = 0 ;
17
      return;
18
    }
19
20
    sampleCount = 0;
21
22
    if (useBuffer == 1) {   
23
      useBuffer = 2;
24
      buffer1Full = 0;
25
26
    }
27
    else if(useBuffer == 2){
28
      useBuffer = 1;
29
      buffer2Full = 0;
30
31
    }
32
  }
33
}
Hat hier jemand eine Idee was falsch sein könnte?

von Boris O. (bohnsorg) Benutzerseite


Lesenswert?

Wie kommst du zu dem Schluss, die Tonhöhe sei korrekt, die Bytes kämen 
aber zu schnell? Mir ist nicht ganz klar, warum ein ATMega328P mit 8MHz 
Takt eine PWM von 44.1kHz stemmt? So etwas um die 30kHz halte ich für 
realistisch(er). Hast du vielleicht die Auflösung reduziert, um so hohe 
Sample-Raten zu bewerkstelligen? (Ich vermute nicht, denn dann müsstest 
du die Compare-Register neu/ anders/ überhaupt laden und den 
Wave-Generator-Modus nutzen.)

von Tobias S. (t0b14s)


Lesenswert?

>Wie kommst du zu dem Schluss, die Tonhöhe sei korrekt, die Bytes kämen aber zu 
schnell?

Die Stimme ("Fetzen", die aus dem Lautsprecher kommen) der Sprachausgabe 
klingt genau gleich, wie wenn ich die Dateien am PC abspiele. Es hört 
sich einfach an, als ob das nächste Byte schon geschrieben wird, bevor 
der Lautsprecher komplett darauf reagieren kann.

>Mir ist nicht ganz klar, warum ein ATMega328P mit 8MHz Takt eine PWM von 44.1kHz 
stemmt?

Ich habe die Gleichung aus dem Datenblatt folgendermaßen umgestellt:

OCR1A = ((F_CPU / PRESCALER) / SOLLFREQUENZ) - 1

Wenn ich den Wert von OCR1A leicht erhöhe, wird die Ausgabe schneller 
und die Stimme tiefer. Wenn ich den Wert niedriger einstelle, wird das 
Ganze langsamer und die Stimme höher.

Bei dieser Einstellung:
OCR1A = ((F_CPU / PRESCALER) / SOLLFREQUENZ) - 1 -4
wird die Sprache tatsächlich in richtiger Geschwindigkeit und 
vollständig ausgegeben, nur die Stimme ist deutlich zu hoch.

Es gibt auch schon AVR-Projekte, die mit 44,1kHz Sprachausgabe oder 
Musik ausgeben können.

>Hast du vielleicht die Auflösung reduziert, um so hohe Sample-Raten zu 
bewerkstelligen?

Welche Auflösung meinst Du? Außer den Zeilen oben, habe ich nichts 
anderes eingestellt.
Mir fällt langsam nichts mehr ein, was ich noch ändern könnte.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Das Ganze soll offenbar eine Auflösung von 8 bit haben, zumindest ist so 
ja der Timer0 eingestellt. Damit läuft er aber mit einer Frequenz von 8 
MHz / 2^8 = 31250 Hz. Dass OCR0A mit 44100 Hz neu geladen wird, ändert 
daran nichts und passt eben nicht.

von Tobias S. (t0b14s)


Lesenswert?

Danke erstmal für Eure Hilfe.

Ja, die Soundfiles haben 8 bit, 44100 Hz Abtastrate und sind einkanalig 
Mono.
Wenn ich versuche eine Datei mit 22050 Hz Abtastrate abzuspielen und die 
Sollfrequenz entsrpechend anpasse, dann scheint es das Ganze noch 
schlechter zu machen und die "Wortfetzen" werden noch kürzer.

von c-hater (Gast)


Lesenswert?

Tobias S. schrieb:

> Mir fällt langsam nichts mehr ein, was ich noch ändern könnte.

Du könntest erstmal die Tatsache ändern, dass du mit zwei Timern 
hantierst. Das ist völlig überflüssig. Dein Timer für die PWM-Ausgabe 
taktet mit 8000000Hz/256=31250Hz. Deine Eingabedaten sind (zumindest 
nach deiner Aussage) mit einer Samplefrequenz von 44100Hz gespeichert 
(hoffentlich wenigstens im korrekten Sampleformat, das müsste sein: 8Bit 
unsigned).

Die einfachste Methode wäre, die Eingabe-Dateien einfach mit einem PC 
und einem entsprechenden Programm auf die verfügbare Wiedergabefrequenz 
von 31250Hz zu resampeln. Dann brauchst du dich nicht mit den den 
Widrigkeiten der leicht gehobenen µC-Programmierung 
auseinanderzusetzen...

Wenn du aber dieses Herausforderung annehmen willst, musst du einfach 
nur eine DDS für die Ausgabe implementieren. Das ist wahrlich nicht so 
schwer.

Der triviale Ansatz wäre mit fixed-point-Mathematik. Deine 
TIMER1_COMPA-ISR müßte also bei jedem Aufruf nicht 1,0 Samples der 
Quelle entnehmen, sondern 44100/31250=1,4112. Das kann man mit einem 
1.7-FP-Format (also nur win Byte für den Faktor) schon recht gut 
abbilden, der absolute Fehler beträgt dann ca. +0.2%, d.h.: die 
Wiedergabefrequenz ist etwas zu hoch, für einen Normalsterblichen aber 
wohl kaum erkennbar zu hoch...

Das Gelbe vom Ei ist DDS mit Bresenham statt FP-Mathematik. Vorteil: 
zumindest im langfristigen Mittel stimmt die Wiedergabefrequenz exakt. 
Nachteil: Rechenzeitaufwand größer (möglicherweise sogar deutlich 
größer) als beim FP-Ansatz.

Ob der Bresenham in Frage kommt, ermittelt man durch eine 
Primfaktorzerlegung der beiden Frequenzen:

44100=2^2 * 5^2 * 3^2 * 7^2
31250=2^1 * 5^6

Dann eleminiert man das, was gleich ist und betrachtet nur noch, was 
ungleich ist:

44100=2^1 * 3^2 * 7^2 = 882
31250=5^4             = 625

Im konkreten Fall ziemlicher Mist, beides passt nicht in ein Byte, d.h: 
man müßte hier mit 16Bit rechnen. Außerdem: der geringe Grad an 
Übereinstimmung (also an eleminierbaren Primfaktoren) weist darauf hin, 
dass Bresenham hier nicht wirklich vorteilhaft wäre, weil die meiste 
Zeit auch nur derselbe Scheiss rauskommen würde, den auch ein Ansatz mit 
FP-Arithmetik zu viel geringeren Kosten liefern könnte...

Also DDS mit FP... Ggf optimiert. 0.8 wäre in dieser Anwendung bei 
entsprechender Programmierung ebenfalls möglich und würde den Fehler 
halbieren...

Man muss einfach nur programmieren können, das ist alles...

von Mampfred (Gast)


Lesenswert?

Wenn dein ATmega keine weiteren Funktionen während des "PLAY" ausführen 
muß, dann kannst du es auch ganz einfach mit einer Warteschleife machen.
Also, ein Byte an OCR0A übergeben, Warteschleife abwarten und das 
nächste Byte an OCR0A übergeben und wieder warten und so weiter und so 
fort...bis zum Ende.
Der Vorteil hierbei ist, das du dann auch jede beliebige Samplefrequenz 
dir durch verändern der Warteschleife einfach einstellen kannst und 
nicht abhängig bist von den Teilungsfaktoren eines Zählers.

Noch eine kleine Info, falls noch nicht so gemacht:
Ich gehe mal davon aus, du weist, das man am OCR0A Ausgangspin ein 
Widerstand und Kondensator noch benötigt, um ein ordentliches Signal für 
den Verstärker bzw. Lautsprecher zu bekommen. Besser wären 2 oder 3 
Solcher RC Filter um alle störenden Frequenzen auszufiltern.
wenn nicht könnte auch das eine Erklärung sein, wenn es sich Schei... 
anhört.

von Tobias S. (t0b14s)


Lesenswert?

> Du könntest erstmal die Tatsache ändern, dass du mit zwei Timern
> hantierst. Das ist völlig überflüssig. Dein Timer für die PWM-Ausgabe
> taktet mit 8000000Hz/256=31250Hz. Deine Eingabedaten sind (zumindest
> nach deiner Aussage) mit einer Samplefrequenz von 44100Hz gespeichert
> (hoffentlich wenigstens im korrekten Sampleformat, das müsste sein: 8Bit
> unsigned).

Der zweite Timer würde das Ganze flexibler machen was die Abtastrate der 
Dateien angeht. Ich habe es jetzt auch mal mit einem Timer versucht und 
gebe dabei immer bei Timer0 Overflow das nächste Byte raus aber das 
Ergebnis bleibt leider das selbe. Die Abtastrate der Datei ist dann auf 
31250 eingestellt.
Das Dateiformat ist 8Bit unsigned.

von Tobias S. (t0b14s)


Lesenswert?

> Wenn dein ATmega keine weiteren Funktionen während des "PLAY" ausführen
> muß, dann kannst du es auch ganz einfach mit einer Warteschleife machen.

Er muss eigentlich während des Abspielens nichts anderes machen aber es 
müssen ständig von der SD-Karte neue Blöcke gelesen und in die 
PlayBuffer geschrieben werden. Deshalb fällt glaube ich die Lösung mit 
der Warteschleife raus.


> Noch eine kleine Info, falls noch nicht so gemacht:
> Ich gehe mal davon aus, du weist, das man am OCR0A Ausgangspin ein
> Widerstand und Kondensator noch benötigt, um ein ordentliches Signal für
> den Verstärker bzw. Lautsprecher zu bekommen. Besser wären 2 oder 3
> Solcher RC Filter um alle störenden Frequenzen auszufiltern.
> wenn nicht könnte auch das eine Erklärung sein, wenn es sich Schei...
> anhört.

Am Ausgang hängt eine Verstärkerschaltung mit LM386. Die Tonqualität der 
Schaltung ist momentan nicht das Problem. Es kommen einfach die Daten 
aus dem Ausgang nicht so, wie es eigentlich sein sollte.

von Stefan F. (Gast)


Lesenswert?

> Es hört sich einfach an, als ob das nächste Byte schon geschrieben
> wird, bevor der Lautsprecher komplett darauf reagieren kann.

Ich habe keine Erfahrung, wie sich so etwas anhört. Du denn? Lade doch 
mal zur Veranschaulichung eine kurze Audio-Aufzeichnung hoch.

von Tobias S. (t0b14s)


Angehängte Dateien:

Lesenswert?

Stefan U. schrieb:
>> Es hört sich einfach an, als ob das nächste Byte schon geschrieben
>> wird, bevor der Lautsprecher komplett darauf reagieren kann.
>
> Ich habe keine Erfahrung, wie sich so etwas anhört. Du denn? Lade doch
> mal zur Veranschaulichung eine kurze Audio-Aufzeichnung hoch.

Hier ist mal eine Aufnahme von der Ausgabe. Die Stimme sagt: "Diese 
Datei ist nur ein Test, um die Sprachausgabe mit dem Mikrocontroller zu 
prüfen. 1, 2, 3, 4, 5, 6, 7, 8, 9."

Würde mich mal interessieren was Ihr da raushören könnt.

von Stefan F. (Gast)


Lesenswert?

Ich höre da gar nichts vernünftiges raus.

Weisst du was? Vereinfache mal das Programm, indem du den ganzen Teil 
mit der SD Karte raus lässt.

Benutze stattdessen einen Dummy-Ton mit einer rechteck- oder 
dreieck-Schwingung als Byte-Array. Das ist ja noch recht einfach. Wie 
hört sich dessen Ausgabe an? Sieht sie auf einem Oszilloskop sauber aus?

von Karlmann (Gast)


Lesenswert?

Stefan U. schrieb:

> Ich höre da gar nichts vernünftiges raus.

Hast du die Datei vorher auf Viren geprüft?

von Stefan F. (Gast)


Lesenswert?

> Hast du die Datei vorher auf Viren geprüft?

Ääääh nein. Ich hoffe doch, dass Windows das automatisch tut. Muss aber 
zugeben, dass ich immer noch gewohnt bin, unter Linux zu arbeiten und 
mir daher um Viren keinen Kopf gemacht habe.

von Tobias S. (t0b14s)


Lesenswert?

> Benutze stattdessen einen Dummy-Ton mit einer rechteck- oder
> dreieck-Schwingung als Byte-Array. Das ist ja noch recht einfach. Wie
> hört sich dessen Ausgabe an? Sieht sie auf einem Oszilloskop sauber aus?

Ich hab hier leider kein Oszi zur Hand und könnte nicht prüfen, wie das 
Signal aussieht, das der µC rausgibt.

von Stefan F. (Gast)


Lesenswert?


von Tobias S. (t0b14s)


Angehängte Dateien:

Lesenswert?

Hier mal die Hörprobe vom Recktecksignal. Der Interrupt sollte mit 44,1 
kHz aufgerufen werden und ich schreibe immer abwechselnd 0 oder 128 auf 
den Ausgang.


> Und bestelle Dir gleich dieses Ding, für's nächste mal:
> 
https://de.aliexpress.com/item/Assembled-DSO138-2-4-TFT-Handheld-Pocket-size-Digital-Oscilloscope-Kit-DIY-Parts-Electronic-Learning-Set/32803186608.html

Taugen die Dinger denn was? Ich hab mir das auch schon überlegt aber der 
extrem günstige Preis hat mich dann doch abgeschreckt.

von Stefan F. (Gast)


Lesenswert?

> Hier mal die Hörprobe vom Recktecksignal.

Klingt gleichmäßig (was ein wichtiger Test war), aber

> Der Interrupt sollte mit 44,1 kHz aufgerufen werden
> ich schreibe immer abwechselnd 0 oder 128 auf den Ausgang.

Dann hätte die Tonfrequenz 22kHz sein müssen und das kann ich nicht 
hören. Deinen Ton konnte ich jedoch sehr deutlich hören. Ich schätze 
2kHz.

Des weiteren solltest du den Ton in den Puffer ablegen, den du sonst für 
den Lesevorgang von der SD Karte genommen hättest, um zu sehen, ob der 
Zugriff auf das Array richtig abläuft.

von Mampfred (Gast)


Lesenswert?

Tobias S. schrieb:
> Er muss eigentlich während des Abspielens nichts anderes machen aber es
> müssen ständig von der SD-Karte neue Blöcke gelesen und in die
> PlayBuffer geschrieben werden. Deshalb fällt glaube ich die Lösung mit
> der Warteschleife raus.

Ok. Wenn es 44.100 kHz Samplefrequenz sein sollen dann könnte man auch 
mit dem Hauptoszillator das Ganze darauf "biegen".
44.100 kHz * 256 = 11.289MHz. Also entweder einen 11.0592MHz nehmen dann 
erhält man 43.200kHz, oder einen 12MHz nehmen, dann erhält man 46.875kHz 
bei entsprechendem Teilungsfaktor für den Zähler der den Interrupt 
auslöst. 11.0592 liegt dann näher dran. Den Unterschied zu den fehlenden 
900Hz hören dann nur noch die Toningenieure. :-)
Bei meinem Projekt habe ich einen 12MHz Quarz genommen und da ist der 
Unterschied bei Sprachausgaben die dann nur minimal schneller 
wiedergegeben werden kaum zu bemerken.
Somit wäre die Samplefrequenz erst einmal geregelt und der ATmega dann 
auch zum Einlesen weiterer Daten von der SD-Karte noch in Aktion.

von Mampfred (Gast)


Angehängte Dateien:

Lesenswert?

Habe mal aus dem Schaltplan meines Projektes die Audioausgabe hier 
angehangen. Da ist zu sehen wie man das Audiosignal gewinnen kann.
Nur zum Verständnis...

von Tobias S. (t0b14s)


Lesenswert?

> Ok. Wenn es 44.100 kHz Samplefrequenz sein sollen dann könnte man auch
> mit dem Hauptoszillator das Ganze darauf "biegen".

Es müssen nicht unbedingt 44,1 kHz sein. Morgen werde ich das Ganze mal 
mit 22050 Hz Samples versuchen.

Hast du bei deinem Projekt eine SD-Karte verwendet?
Bzw würdest du mir mal deinen Code schicken, damit ich das mal mit 
meinem verlgeichen kann? Bin offen für alles :)

von S. Landolt (Gast)


Lesenswert?

Kann man nicht jetzt erstmal mit der halben Auflösung arbeiten, d.h. 7 
bit?
Den Timer0 auf Modus 7, fast PWM mit top=OCR0A=180 für die 44.1 kHz, 
stellen und die halbierten Eingangswerte auf OCR0B geben; das 
Ausgangssignal liegt dann auf OC0B=PD5.

von c-hater (Gast)


Lesenswert?

Tobias S. schrieb:

> Der zweite Timer würde das Ganze flexibler machen was die Abtastrate der
> Dateien angeht.

Nein. Genau darum, wie man das auch ohne zweiten Timer löst, drehte sich 
mein Posting.

von Falk B. (falk)


Lesenswert?


von Mampfred (Gast)


Lesenswert?

Tobias S. schrieb:
> Hast du bei deinem Projekt eine SD-Karte verwendet?
> Bzw würdest du mir mal deinen Code schicken, damit ich das mal mit
> meinem verlgeichen kann? Bin offen für alles :)

Mein Projekt arbeitet mit einem externen Flash Speicher der per SPI 
angesteuert wird und eine Größe von 2 Mbyte (16MBit) hat.
Ich würde dir auch den Code zur Verfügung stellen. Das Problem ist nur, 
ich programmiere nicht in C sondern nur in Assembler.

Aber ich versuche mal das Ganze in Worte zu fassen.
Hier erst mal die Initialisierung des verwendeten PWM Pins OC2A.
in Register TCCR2A kommt der Wert 10000011
in Register TCCR2B kommt der Wert 00000001 (Bedeutung der einzelnen Bits 
sind im Datenblatt z.B. ATmega1284 zu finden)
Dann lade ich den Mittelwert der PWM, also 127 und schreibe diesen erst 
einmal auf die PWM (Register OCR2A). Somit bekomme ich eine 
Mittenspannung als Analogausgang. Man möchte ja Sinus erzeugen und 
negative Sinuswellen gehen halt sonnst nicht. Deshalb erst einmal auf 
Halbe Ausgangsspannung setzen um eine negative Sinuswelle in den Bereich 
unterhalb der Mittenspannung bis zur 0 generiert zu bekommen.
Ab hier läuft die PWM ständig im Hintergrund. Also es wird ab hier 
ständig die halbe Analogspannung erzeugt. Wenn der ATmega nun einen 
Befehl erhält (bei mir über TWI), ein Sample aus dem externen Flash 
abzuspielen, dann wird ein Byte des abzuspielenden Samples über die SPI 
aus dem Externen Flash ausgelesen und in OCR2A gespeichert. Dadurch 
ändert sich nun die PWM- analog Spannung von der Mittelspannung gesehen 
entweder nach unten, oder oben. Die Größe der Änderung ist dann 
natürlich abhängig von dem Wert den ich in OCR2A gespeichert habe.
Jetzt kann man eine Warteschleife, oder ein Interrupt ausgelöst durch 
einen weiteren Zähler (Timer Overflow) programmieren, der die 
"Haltezeit" dieses einzelnen PWM Bytes beinhaltet. Die Wartezeit einer 
Warteschleife, bzw. die Timer Overflow Zeit muß dann der verwendeten 
Samplefrequenz bzw. der Periodenzeit der verwendeten Samplefrequenz 
entsprechen.
Wenn man einen IRQ nimmt, könnte man dann auch während dieser 
"Haltezeit" weitere Daten von der SD-Karte ins SRAM lesen.
Ist die Periodenzeit bzw. die "Haltezeit" des PWM mit einem bestimmten 
Wert abgelaufen (Warteschleife zu ende, oder IRQ ausgelöst), wird das 
nächste Byte in OCR2A geschrieben, bis der Bytezähler des Samples die 0 
erreicht hat. Zum Schluss, wenn das Sample komplett ausgegeben wurde, 
wird dann wieder die 127 in OCR2A geschrieben um eine Halbe 
Analogspannung wieder auf der PWM (analog) Leitung zu haben.

Das ist das Ganze Prinzip wie ich es realisiert habe.
Die Samples kommen bei mir über die UART vom PC in den Externen Flash 
Speicher. Bei dir wäre dann halt die SD-Karte der Samplespeicher.
Da ich nur Sprachausgabe und keine Musikausgabe mache, reicht es mir 
auch vollkommen aus, wenn die Samples 8-Bit Auflösung haben.
Hoffe das hilft dir weiter. Denn es ist ganz toll, wenn ein ATmega 
anfängt zu sprechen... :-)

Noch eine Idee von mir zur Sprachausgabe per ATmega. Habe aber keine 
Ahnung ob das überhaupt so geht! Aber bitte entscheidet selber...
Wenn man eine Hardware hat, die ein bisschen mehr Speicherplatz zur 
Verfügung stellt, könnten dann auch solche Sachen wie z.B. das Vorlesen 
eines Textes, der über eine RS232 eines PC´s auf die UART des ATmega 
geleitet wird, machen. Man müßte halt 26 Buchstaben + Ö,Ä,Ü usw. als 
Sample im Speicher ablegen. Wenn dann in ASCII das "A" gesendet wird, 
würde das Sample für "A" abgespielt werden, bei "B" das Sample für "B" 
usw. Würde sich bestimmt ganz ulkig anhören...

von Tobias S. (t0b14s)


Lesenswert?

> Aber ich versuche mal das Ganze in Worte zu fassen.
> Hier erst mal die Initialisierung des verwendeten PWM Pins OC2A.
> in Register TCCR2A kommt der Wert 10000011
> in Register TCCR2B kommt der Wert 00000001 (Bedeutung der einzelnen Bits
> sind im Datenblatt z.B. ATmega1284 zu finden)
> Dann lade ich den Mittelwert der PWM, also 127 und schreibe diesen erst
> einmal auf die PWM (Register OCR2A). Somit bekomme ich eine
> Mittenspannung als Analogausgang. Man möchte ja Sinus erzeugen und
> negative Sinuswellen gehen halt sonnst nicht. Deshalb erst einmal auf
> Halbe Ausgangsspannung setzen um eine negative Sinuswelle in den Bereich
> unterhalb der Mittenspannung bis zur 0 generiert zu bekommen.
> Ab hier läuft die PWM ständig im Hintergrund. Also es wird ab hier
> ständig die halbe Analogspannung erzeugt. Wenn der ATmega nun einen
> Befehl erhält (bei mir über TWI), ein Sample aus dem externen Flash
> abzuspielen, dann wird ein Byte des abzuspielenden Samples über die SPI
> aus dem Externen Flash ausgelesen und in OCR2A gespeichert. Dadurch
> ändert sich nun die PWM- analog Spannung von der Mittelspannung gesehen
> entweder nach unten, oder oben. Die Größe der Änderung ist dann
> natürlich abhängig von dem Wert den ich in OCR2A gespeichert habe.
> Jetzt kann man eine Warteschleife, oder ein Interrupt ausgelöst durch
> einen weiteren Zähler (Timer Overflow) programmieren, der die
> "Haltezeit" dieses einzelnen PWM Bytes beinhaltet. Die Wartezeit einer
> Warteschleife, bzw. die Timer Overflow Zeit muß dann der verwendeten
> Samplefrequenz bzw. der Periodenzeit der verwendeten Samplefrequenz
> entsprechen.


Danke für die ausführliche Antwort.
Ich habe mein Programm jetzt auch zum Laufen gebracht. Das Problem lag 
nicht an den Timern, sondern an den Lesepuffern. Es wurde zum Teil der 
Puffer2 nicht mit Daten befüllt und deshalb hat die Hälfte der Bytes 
gefehlt. Die Soundqualität werde ich mal mit Hilfe Deiner Vorschläge 
versuchen zu verbessern. Ab und zu habe ich auch noch ein leichtes 
knacken in der Ausgabe.

> Noch eine Idee von mir zur Sprachausgabe per ATmega. Habe aber keine
> Ahnung ob das überhaupt so geht! Aber bitte entscheidet selber...
> Wenn man eine Hardware hat, die ein bisschen mehr Speicherplatz zur
> Verfügung stellt, könnten dann auch solche Sachen wie z.B. das Vorlesen
> eines Textes, der über eine RS232 eines PC´s auf die UART des ATmega
> geleitet wird, machen. Man müßte halt 26 Buchstaben + Ö,Ä,Ü usw. als
> Sample im Speicher ablegen. Wenn dann in ASCII das "A" gesendet wird,
> würde das Sample für "A" abgespielt werden, bei "B" das Sample für "B"
> usw. Würde sich bestimmt ganz ulkig anhören...

Ich glaube nicht, dass man da etwas verstehen würde. Die Stimme würde 
mehr oder weniger das buchstabieren, was eingetippt wird.
Ich habe meine Samples in Linux mit dem Befehlszeilenprogramm pico2wave 
erstellt und anschließend mit sox in das passende Format gewandelt. Die 
Qualität der Sprache ist erstaunlich gut. Und wenn man die Befehle in 
ein Skript packt, werden gleich alle Samples auf einmal erstellt und 
gewandelt.

von Mampfred (Gast)


Lesenswert?

Tobias S. schrieb:
> Ich habe meine Samples in Linux mit dem Befehlszeilenprogramm pico2wave
> erstellt und anschließend mit sox in das passende Format gewandelt. Die
> Qualität der Sprache ist erstaunlich gut. Und wenn man die Befehle in
> ein Skript packt, werden gleich alle Samples auf einmal erstellt und
> gewandelt.

Für die Erstellung der Samples verwende ich Audacity. Da ist alles drin 
was man so zur Soundbearbeitung benötigt.
Da ich auch nur die Sampledaten selbst und nicht die Filekennung, Größe 
usw. die ja auch sonnst mit in das File abgespeichert wird (z.B. WAV), 
speichere ich das Endprodukt als RAW File. Dann habe ich wirklich nur 
die reinen Sounddaten und kann dann einfach die erstellte RAW-Datei Byte 
für Byte in den Flash Speicher schieben.

Freut mich wenn es jetzt funzt. :-)

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.