Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage FIFO (STM32 SAI Interface)


von Solocan Z. (solocan)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

für mich ist FIFO im Hardwarekontext ein Puffer, wo die Bits sequentiell 
reingespeichert werden und dann bei erwünschtem Befüllungsgrad wieder 
ausgelesen/weiterverarbeitet werden sollen.

Doch gerade bin ich dabei, das Serial Audio Interface(SAI) für ein STM32 
Controller aufzustellen und habe da doch Verständnisprobleme. Und zwar 
das SAI hat eine Pufferlänge von 8 Wörter, also 8x32bits, s. Bilder. 
Aber das Datenregister SAI_xDR, das als FIFO Buffer dienen sollte, ist 
nur 1 Wort groß (=32bits)

Die Interrupt Generation bezieht sich auch auf den Befüllungsgrad vom 
Register SAI_xDR, s.Bild. Worauf beziehen sich also diese 8 Wörter? Bzw. 
wie kann ich das handhaben, dass ich tatsächlich in 8-Wörter langes FIFO 
habe, das z.B. alle 4 Wörter ein DMA auslöst? (Was vom Datenblatt auch 
versprochen wird)

Im Datenblatt steht folgendes:

A write to this register loads the FIFO provided the FIFO is not full.
A read from this register empties the FIFO if the FIFO is not empty.

Also kontrolliert man über dieses Register nur das FIFO bzw. schiebt 
wortweise Daten rein und liest raus? Was ist die Adresse des 
FIFO-Puffers? Kann mir jemand bitte erklären, wie das ganze genau 
funktioniert?

Danke und viele Grüße

von bastler (Gast)


Lesenswert?

Solocan Z. schrieb:
> Hallo zusammen,
>
> für mich ist FIFO im Hardwarekontext ein Puffer, wo die Bits sequentiell
> reingespeichert werden und dann bei erwünschtem Befüllungsgrad wieder
> ausgelesen/weiterverarbeitet werden sollen.
> Doch gerade bin ich dabei, das Serial Audio Interface(SAI
> Aber das Datenregister SAI_xDR, das als FIFO Buffer dienen sollte, ist
> nur 1 Wort groß (=32bits)
>
> Die Interrupt Generation bezieht sich auch auf den Befüllungsgrad vom
> Register SAI_xDR, s.Bild. Worauf beziehen sich also diese 8 Wörter? Bzw.

Ich habe das DB jetzt nicht gelesen aber das FIFO, dein Puffer ist 8x32 
bit gross.
Die Daten die du erhältst schreibst du ins Datenregister DR. Von Dort 
aus werden sie in den Puffer geschrieben. Spätestens nach dem 
8.Datensatz ist der Puffer voll u du verlierst Daten, wenn du ihn nicht 
leerst. Sprich die Dma faul rum sitzt.


> wie kann ich das handhaben, dass ich tatsächlich in 8-Wörter langes FIFO
> habe, das z.B. alle 4 Wörter ein DMA auslöst? (Was vom Datenblatt auch
> versprochen wird)

Du kannst die DMA so einstellen, dass nachdem immer das Register DR 
geschrieben wurde die Daten immer transferiert werden durch die DMA, 
aber auch so dass sie das nur dann macht wenn du das DMA enable bit 
immer per Software aktivierst, wenn 4 Datensaetze gekommen sind. Macht 
für mich aber keinen Sinn. Lies dazu das Ref Man zur DMA am Besten.

> Im Datenblatt steht folgendes:
> A write to this register loads the FIFO provided the FIFO is not full.
> A read from this register empties the FIFO if the FIFO is not empty.
>
> Also kontrolliert man über dieses Register nur das FIFO bzw. schiebt
> wortweise Daten rein und liest raus? Was ist die Adresse des
> FIFO-Puffers?

Schau im Ref man ganz am Anfang nach der Basis Adresse memory 
organization. iwas 0x4001 xxxx bis 0x4001 yyyy.
Also 0x4001 + Offset SAI_BDR in deinem Bild.

 Kann mir jemand bitte erklären, wie das ganze genau
> funktioniert?
>
> Danke und viele Grüße

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Beim SAI darf das FIFO nie leer laufen, dann dann "Stockt" die 
Soundausgabe.

Um das zu gewährleisten gibt es ein FIFO in der Peripherie SAI.
Du musst nur das eine Datenregister beschreiben, jedes Schreiben tut die 
Peripherie automatisch in das FIFO legen.

Nun gibt es ein Interrupt, den kann man auslösen wenn z.B. nur noch 4 
Elemente im FIFO drin sind. -> Periphere möchte dass man mehr Daten 
liefert.

Die 4 bedeutet, dass man noch diese 4 Plätze Zeit hat um Daten zu 
liefern und der Datenstrom nicht abreißt.

Dann macht man das FIFO voll und verlässt den Interrupt.

So geht die "Händische" Programmierung.

Etwas komplexer ist die Soundausgabe mit DMA.
Hier wird die Schwelle den DMA triggern und dieser kopiert die Daten in 
den FIFO. Ist der DMA zu Ende, so generiert dieser ein Interrupt und nun 
muss man den nächsten DMA Auftrag für den nächsten RAM Bereich zur 
Soundausgabe senden aktivieren.

Während der DMA die Daten aus gibt kann man in einem zweiten RAM Bereich 
die neuen Sound-Daten vorbereiten. So werden im Prinzip immer 2 RAM 
Bereich für die Sound Ausgabe umgeschaltet.

von bastler (Gast)


Lesenswert?

Markus M. schrieb:

>
> Etwas komplexer ist die Soundausgabe mit DMA.
> Hier wird die Schwelle den DMA triggern und dieser kopiert die Daten in
> den FIFO. Ist der DMA zu Ende, so generiert dieser ein Interrupt und nun
> muss man den nächsten DMA Auftrag für den nächsten RAM Bereich zur
> Soundausgabe senden aktivieren.
>


Meiner Meinung nach geschieht die Portierung in das FIFO automatisch. 
Das sagt ja auch der englische Text weiter oben. Dazu benötigst du keine 
DMA.
Die DMA holt sich die Daten aus dem FIFO (Adresse der Quelle) und 
transportiert sie an die Zieladresse. Die Quell- und die Zieladresse 
muss man der DMA auch in der Initialisierung entsprechend bekannt geben.
Ich kann mich aber auch täuschen, da ich das DB nicht gelesen habe.

von bastler (Gast)


Lesenswert?

bastler schrieb:
> Markus M. schrieb:
>
>>
>> Etwas komplexer ist die Soundausgabe mit DMA.
>> Hier wird die Schwelle den DMA triggern und dieser kopiert die Daten in
>> den FIFO. Ist der DMA zu Ende, so generiert dieser ein Interrupt und nun
>> muss man den nächsten DMA Auftrag für den nächsten RAM Bereich zur
>> Soundausgabe senden aktivieren.
>>
>
> Meiner Meinung nach geschieht die Portierung in das FIFO automatisch.
> Das sagt ja auch der englische Text weiter oben. Dazu benötigst du keine
> DMA.
> Die DMA holt sich die Daten aus dem FIFO (Adresse der Quelle) und
> transportiert sie an die Zieladresse. Die Quell- und die Zieladresse
> muss man der DMA auch in der Initialisierung entsprechend bekannt geben.
> Ich kann mich aber auch täuschen, da ich das DB nicht gelesen habe.


Solocan Z. schrieb:
> SAI_FIFO.png


Ach hier steht es ja:
Das Schreiben des DR füllt das FIFO
und das Lesen entleert das FIFO

Also muss die DMA Quelladresse das SAI_BDR sein.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Lesen muss man bei SAI nur dann wenn man ein Sound einlesen möchte 
(LineIn, Mikrofon), geht es nur um Soudausgabe 
(Musik-Player/Pip-Ausgabe), dann kann man darauf verzichten.

Man kann die SAI Ausgabe / SAI Einlesen fast wie 2 getrennte Peripherie 
Einheiten betrachten und kann man getrennt voneinander nutzen.

von Solocan Z. (solocan)


Lesenswert?

Vielen Dank für die Antworten! Es geht schon in die Richtung, die ich 
verstehen wollte. Vieles deckt sich auch mit meinem Verständnis. Aber 
eine Frage bleibt für mich offen:

Muss ich jetzt die DMA alle 4 Wörter in einer Interruptroutine triggern 
oder soll SAI Peripherie jedes mal automatisch ein DMA starten?

Nehmen wir ein Beispiel: Ich möchte kontinuierlich ein 1ms langes Sample 
ausgeben: Wie macht man das am sinnvollsten?

Option A:

1) Signal definiert (Samplelänge=48 Wörter bei 48kHz und 1ms)

2) DMA und SAI so konfigurieren, dass es mir bei halber Befüllung ein 
Interrupt auslöst.

3) Schicke erst mal 8 Wörter mit einem DMA Request und befüll das FIFO 
vollständig.

4) Irgendwann wird das "Halbvoll-Interrupt" ausgelöst, wenn wieder die 
Hälfte leer ist und in dieser Interrupt-Routine werden die nächsten 4 
Wörter mit einem neuen DMA-Request geschickt und so weiter...

(Option A wäre also "interrupt-gesteuert und die Interruptroutine müsste 
alle 4 Wörter einen neuen DMA-Request starten, weil sich ja die 
DMA-Quelladdresse ändert.)

Option B:

1) Gleich wie oben.

2) DMA und SAI so konfigurieren, dass es mir bei halber Befüllung ein 
DMA-Request startet und nehme als Quelladdresse einen 4-Wörter langen 
selbsterzeugten Speicherbereich (&buf).

3) Schicke erst mal 8 Wörter mit einem DMA Request und befüll das FIFO 
vollständig.

4) Ich kopiere die nächsten 4 Wörter in den "&buf"

5) Bei halber Befüllung wird ein DMA-Request getriggert. DMA holt den 
Inhalt von &buf und schiebt in die Peripherie.

6) Wenn DMA-Transfer fertig ist, wird ein "DMA-Transfer complete" 
ausgelöst. In dieser Routine kopiere ich die nächsten 4 Wörter von 
meinem Sample in den "&buf".

7) Zurück zu  5).

Nachteil Option A: Alle 4 Wörter wird ein neuer DMA request mit 
geänderter Adresse gestartet. (viel Overhead)

Nachteil Option B: Ich muss alle 4 Wörter lang 4 Wörter in &buf 
kopieren. (Pointer kann ich ja nicht ändern, weil DMA guckt ja nur unter 
der gegebenen Adresse und nimmt das, was sich darunter befindet)


Also wie sollte ich vorgehen?

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Ne, das geht anders - und eimfacher.

SAI wird mit dem DMA verbunden.
Ein RAM Buffer mit den 48 Worten vorbereiten.
DMA starten.

Warten bis der DMA fertig ist -> Interrupt vom DMA nicht vom SAI.

Der DMA weiß von alleine dass der SAI noch platz im FIFO hat und schiebt 
den kompletten Buffer  da rein, ganz von alleine.

von Solocan Z. (solocan)


Lesenswert?

Wow, das klingt gut. Mal blöd gefragt: Wie macht man das? Wenn ich DMA 
starte mit 48 Wörtern, dann gibt's gleich die Warnung, FIFO voll und 
nach 8 Wörtern kommt nichts mehr an, oder?

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Der DMA wartet auf die Anforderung der Peripherie. Der ist klug genug 
das zu wissen.

von Solocan Z. (solocan)


Lesenswert?

Ok. Jetzt habe ich es soweit, dass DMA meinen beliebig langen Array 
automatisch in Schleife ins FIFO schiebt. Danke euch nochmal, ich bin 
definitiv ein Schritt weiter.

Nur, ich bekomme nun keinen "Transfer complete" callbacks. Kämpfe gerade 
daran, dass ich irgendwelche Callbacks vom DMA abzapfen kann. Kann es 
sein, dass im circular mode transfer nie beendet wird? Na ja 
wahrscheinlich komme ich wieder zurück mit einem neuen Thread..

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Auf der RAM Seite sollte man den Circular Mode nicht aktivieren, denn 
man will ja einen nächsten Ton-Abschnitt senden und nicht ein 
Endlos-Pipen haben - außer man will endlos pipen ;-)
Wenn dann der DMA fertig ist sollte es ein Interrupt geben und man kann 
den DMA erneut starten mit den nächsten Daten aus einem anderen RAM 
Bereich.

Ich hatte das einmal bei einem LPC4351 programmiert, sollte beim STM 
ähnlich funktionieren.

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.