Hi.
Folgende Konstellation. Ich sende dem Mikrocontroller über die serielle
Schnittstelle 1 Byte große Werte mit 1 MBaud/s also 125000 kByte/s.
Die Daten sind Amplitudenwerte einer Audiodatei mit 44100 Hz
Samplingfrequenz. Der Timer im µC soll also alle 1/44100 s einen
Amplitudenwert ausgeben.
Der µC Takt ist 16 MHz.
Wenn ein Byte empfangen wird, wird der USART Receive Interrupt
ausgelöst, indem folgendes steht:
1
ISR(USART_RXC_vect)
2
{
3
//Ausgabe mit Buffer
4
_inline_fifo_put(&infifo,UDR);
5
}
Der Interrupt schreibt mit der FIFO Funktion die Daten die ankommen in
die Warteschlange. (Die FIFO Funktion hab ich hierher:
http://www.rn-wissen.de/index.php/FIFO_mit_avr-gcc)
Der Timer zählt immer bis 362, was bei mir auf die Samplingfrequenz von
44100 Hz abgestimmt ist.
Der Interrupt wird bei Output Compare Match ausgelöst und sieht so aus:
1
voidinit_timer(void)
2
{
3
// Timer 1 konfigurieren
4
TCCR1B=(1<<CS10)|(1<<WGM12);// Prescaler 1
5
6
// Output Compare Match Interrupt aktivieren
7
TIMSK|=(1<<OCIE1A);
8
9
// Output Compare Register auf 362 setzen
10
// 362 Wartezyklen
11
OCR1AH=0b00000001;
12
OCR1AL=0b01101001;
13
}
14
15
ISR(TIMER1_COMPA_vect)
16
{
17
/* Interrupt Aktion alle
18
(16000000Hz)/ 362 = 44100 Hz
19
*/
20
PORTC_temp=fifo_get_nowait(&infifo);
21
if(PORTC_temp!=-1)
22
{
23
PORTC=PORTC_temp;
24
}
25
}
Wenn ein Byte in der Warteschlange ist (also
fifo_get_nowait(&infifo)!=-1) wird der Wert an PORTC ausgegeben, an dem
ein DAC hängt.
Das Problem ist, dass am Ausgang des DAC nur etwa mit 300 Hz Daten
herauskommen. Ich vermute mal, dass sich die Interrupts gegenseitig
behindern...
Aber im Moment bin ich ratlos wie ich, das Problem lösen soll. Sind die
FIFO Funktionen vielleicht zu zeitintensiv? Wie könnte ich das
beschleunigen? Bzw wie gebe ich dem Timer Interrupt quasi eine
Priorität, dass er auf jedenfall immer ausgelöst wird? Denn es sieht ja
so aus als würde der UART Interrupt immer so schnell ausgelöst, dass der
Timer Interrupt keine Zeit zum zünden hat? Kann man den Timer Interrupt
irgendwie in eine Warteschlange stellen und direkt auslösen sobald das
Programm aus dem UART Interrupt zurückkehrt?
Ich habe schon versucht in der UART Interrupt Routine am Anfang sei();
einzufügen. Aber dabei hat sich der µC die ganze Zeit resettet, also das
kanns nicht sein...
Jemand ne Idee? Wäre echt super!
Grüße.
Die FIFO Funktionen sind schon sehr heftig. Allgemein geschrieben, aber
heftig. Besser du schreibst dir neue, die ein wenig 'leichter' sind und
zumindest die Schreibtfunktion direkt in die ISR. Ein Funktionsaufruf in
einer ISR ist teuer.
Statisches globales Array anlegen, 2 Zähler dazu und den Ringbuffer
implementieren. Wenn du deinem µC helfen willst, dann machst du die
Arraygröße als 2-er Potenz (also 128 Bytes, 256 Bytes, etc)
Wieviel Speicher hast du denn?
Bei 125000 kByte/s rein und 44kByte/s raus ist die FIFO doch ratzfatz
voll.
Also Speicher hab ich gar keinen. Ich hab ein Matlab Script was mir eine
wav-Datei zerpflückt...das ist auch nur zum rumspielen, muss nicht toll
sein.
Aber in Matlab hab ich ne Funktion zum Daten senden. Die kann ich
maximal alle 1ms aufrufen. D.h. ich muss mit der Funktion 44100/1000 =
44,1 Byte pro aufruf verschicken, d.h. ich sollte mit 256Byte Speicher
im µC auskommen.
Ist nur die Frage wie ich schnelle FIFO-Funktionen implementiere...Denn
ich wüsste jetzt nicht was ich anders machen soll als bei dieser
fertigen Version von Roboternetz.
>D.h. ich muss mit der Funktion 44100/1000 =>44,1 Byte pro aufruf verschicken, d.h. ich sollte mit 256Byte Speicher>im µC auskommen.>Ist nur die Frage wie ich schnelle FIFO-Funktionen implementiere...Denn>ich wüsste jetzt nicht was ich anders machen soll als bei dieser>fertigen Version von Roboternetz.
Nimm einen Double Buffer und keinen FIFO. Dann reduziert
sich das rechnen gewaltig. Und wenn du schneller sendest
als du abspielen kannst musst du noch ein Handshake machen
was dem Sender sagt das der Receiver keine Daten mehr aufnehmen
kann.
@ dieter (Gast)
>Also Speicher hab ich gar keinen. Ich hab ein Matlab Script was mir eine>wav-Datei zerpflückt...das ist auch nur zum rumspielen, muss nicht toll>sein.
Was um alles in der Welt lässt dich glauben, so ein wäre einfach mal so
locker mit Matlab zu machen?
>Ist nur die Frage wie ich schnelle FIFO-Funktionen implementiere...
Selber, mit Köpfchen.
>Denn>ich wüsste jetzt nicht was ich anders machen soll als bei dieser>fertigen Version von Roboternetz.
Dann ist das Vorhaben zwei Nummern zu groß für dich. Für den Anfang pack
einfach mal ein paar wenige WAV-Daten in den Flash des AVR und gib sie
aus. Dann vielleicht die Daten aus einem seriellen, externen Flash per
SPI lesen, ala AT25xx. Und wenn das läuft, kannst du über UART und 1
Mbit/s nachdenken.
MfG
Falk
@Falk:
Deinen ersten Satz versteh ich nicht.
Die Sache ist die...es funktioniert ja schon wenn ich die Daten über
USART sende und direkt ausgebe, höre ich die Audiodatei. Nur leider
etwas zu schnell bzw zu langsam, weil 125000Byte/s nicht glatt durch
44100 Hz teilbar ist. So weicht beim direkten streamen die
Abspielsamplingfrequenz leicht von der Originalen ab.
Ich habe vorher immer alles mit Assembler gemacht, aber wollte mir jetzt
mal C genauer ansehen, nur ist das Problem halt, dass ich schlecht
einschätzen kann wie lange was dauert. Bei Assembler ist das ja kein
Problem.
Wie wäre denn folgendes? Wenn ich die neue FIFO Funktion habe, könnte
ich doch einfach manuell in der USART Receive Interrupt Routine am ende
Prüfen, ob mein Timer schon das Output Compare überholt hat. Ich muss ja
nur verhindern, dass der Timer nach Output Compare Match gecleart wird.
Das Flag für das Match müsste ja erhalten bleiben. Also Flag prüfen und
dann aus dem FIFO ausgeben und Timer zurücksetzen. Das müsste doch
klappen oder?
dieter schrieb:> Ich hab ein Matlab Script was mir eine> wav-Datei zerpflückt...
Sicher daß das auch schnell genug geht?
Schreib erstmal ein kleines Testprogramm auf dem AVR, welches für 1s
einfach nur die ankommenden Bytes zählt und dann die Zahl anzeigt, z.B.
auf einem LCD.
Peter
@Peter: Ja funktioniert tatsächlich ;)
Hab jetzt folgendes. Timer Interrupt weg, dafür Abfrage, ob Output
Compare Match stattgefunden hat in UART Interrupt Service Routine:
1
ISR(USART_RXC_vect)
2
{
3
//Ausgabe mit Buffer
4
_inline_fifo_put(&infifo,UDR);
5
6
//NEU!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
if(TCNT1&(1<<OCF1A))
8
{
9
TIFR&=(0<<OCF1A);
10
TCNT1=0;
11
PORTC=fifo_get_nowait(&infifo);
12
}
13
}
Und es läuft tatsächlich, sogar mit den alten FIFO Routinen! Wer hätte
das gedacht? :)
> TIFR &= (0<<OCF1A);
Diese Zeile dürfte so gut wie gar nichts bewirken;)
>OCF1A is automatically cleared when the Output Compare Match A Interrupt >Vector
is executed.
>Alternatively, OCF1A can be cleared by writing a logic one to its bit >location.
>Stimmt, so ist es richtig:> TIFR &= (1<<OCF1A);
Nein, ist es nicht. Interrupt Flags werden gelöscht
wenn man eine 1 reinschreibt. Das machst du aber nicht.
So müsstest du das eigentlich machen
TIFR |= (1<<OCF1A);
Ist aber auch falsch.
Der richtige Weg ist
TIFR = (1<<OCF1A);
Wenn du TIFR erst einliest, OCF1A reinoderst und dann wieder
zurückschreibst weisst du ja nicht ob nicht vieleicht auch
OCF1B schon gesetzt war in TIFR. Schreibst du das ganze dann zurück
löschst du evtuell noch andere Interrupt Flags.
@ dieter (Gast)
>Die Sache ist die...es funktioniert ja schon wenn ich die Daten über>USART sende und direkt ausgebe, höre ich die Audiodatei.
Ach so? Komisch, in deinem ersten Posting klang das GANZ anders!
"Das Problem ist, dass am Ausgang des DAC nur etwa mit 300 Hz Daten
herauskommen. Ich vermute mal, dass sich die Interrupts gegenseitig
behindern..."
> Nur leider>etwas zu schnell bzw zu langsam, weil 125000Byte/s nicht glatt durch>44100 Hz teilbar ist. So weicht beim direkten streamen die>Abspielsamplingfrequenz leicht von der Originalen ab.
Schön, dass du das auch schon mitteilst. Lies mal was über
Netiquette.
>Ich habe vorher immer alles mit Assembler gemacht, aber wollte mir jetzt>mal C genauer ansehen, nur ist das Problem halt, dass ich schlecht>einschätzen kann wie lange was dauert.
Ja.
> Bei Assembler ist das ja kein Problem.
Ja.
>Wie wäre denn folgendes? Wenn ich die neue FIFO Funktion habe, könnte>ich doch einfach manuell in der USART Receive Interrupt Routine am ende>Prüfen, ob mein Timer schon das Output Compare überholt hat.
Ist eine Möglichkeit, wenn gleich nicht die beste.
> Ich muss ja>nur verhindern, dass der Timer nach Output Compare Match gecleart wird.
gecleart, soso.
>Das Flag für das Match müsste ja erhalten bleiben. Also Flag prüfen und>dann aus dem FIFO ausgeben und Timer zurücksetzen.
Nö, der Timer läuft schön weiter, sonst wird es arg ungleichmässig.
Ausserdem gibt es den CTC-Modus, das Mittel der Wahl.
> Das müsste doch klappen oder?
Mehr oder weniger. Wenn man es wirklich gut machen will, muss der Timer
alleine laufen, damit der UART das Timing nicht zerstükeln kann
(Jitter). Bei 44100 Hz sind das 22,67 us. Dein UART spuckt 125.000
Byte/s aus, also 8us. Das ist fast dreimal so schnell. Wahrscheinlich
sendest du 16 Bit WAVs, wenn gleich das hier unsinnig ist, die 16 Bit
bekommst du nie und nimmer mit einem einfachen DAC wiedergegeben. 8 Bit
reichen locker. Der einfachste Ansatz wäre, die Datenrate direkt aus dem
UART abzuleiten, dann brauchst du auch keinen Timer-Interrupt. Da du ja
Matlab hast, kannst du ja dort eine Sampling rate conversion machen,
ohne Echtzeit.
Wenn man es richtig machen will, braucht man ausserdem noch eine
Flußsteuerung, denn der UART MUSS die Daten schneller liefern als sie
ausgegeben werden, er muss aber auch ab und an kurz anhalten, damit dein
FIFO im AVR nicht überläuft. Und damit bist du mitten in den
Problemen von digitaler Audiowiedergabe.
MFG
Falk