Hallo,
der Kunde möchte ein Mess-System, welches die Messdaten mit einem
Clock-Stamp in einem RAM-Ringbuffer abspeichert.
Das Messsystem (STM32F05) soll eine SPI-Slave Schnittstelle haben, wo
der SPI-Master (Raspberry-PI3) die Daten als einen Block (bis 5KB)
rauslesen kann.
Dabei soll es möglich sein, dass der Master mit hoher Frequenz kleine
Blöcke abholt, oder mit kleiner Häufigkeit große Blöcke.
Das Protokoll ist ganz einfach: Master sendet eine CMD (1 Byte), danach
antwortet der Slave mit der aktuellen Buffer-Länge.
Dann schickt der Master eine andere CMD-ID, worauf der Slave den
kompletten Buffer ausgibt.
Mit DMA ist das schwierig: der Buffer ist nicht an einem Stück, sondern
als Ringbuffer angelegt. D.h. bei der obersten Adresse müsste der DMA
stoppen und von ganz vorne beginnen,
während der Master den Slave mit 500kHz taktet.
Frage: kennt jemand irgendwelche Vorlagen für solche Übertragungen?
Ich persönlich würde lieber einen Doppelbuffer verwenden mit fester
Größe, und diese abwechselnd übertragen.
Bei einem Takt von 500KHz sind das bei 8 Datenbits doch gerade mal ~60
kbyte/s.
Der STM32F0 kann doch mit 48 MHz getaktet werden, d.h. er hätte dann
wenigstens um die 800 Taktzyklen um den DMA neu zu starten wenn er das
Ende des Ringbuffers erreicht hat.
Außerdem gibt es - zumindest bei STM32F030 - einen circular mode. Evtl.
könnte man damit etwas anfangen?
Sende dem Master halt im der Puffer-Größe immer die Anzahl Bytes, die
sich mit einem einzelnen DMA-Transfer übertragen lassen. Wenn man so am
oberen Ende des Ringpuffers angekommen ist, kommt der Rest halt erst
beim nächsten Kommando. Oder du setzt den DMA im Transfer Finish
Interrupt neu auf, dafür hast du ja 768 Takte Zeit.
Markus M. schrieb:> Schon komisch das wir als Hobbyisten jemandem helfen müssen, der> etwas> an "Kunden" verkauft ;-)
Vielleicht gibts hier ja ne Gewinnbeteiligung?
Üblicherweise ist oben und unten bei einem Ringpuffer unsinnig.
Die meisten Ringpuffer verwenden vier Routinen.
1. Reset (leeren) [Anfang=Ende]
2. Eini (eg. Push)
3. Aussi (eg. Pop)
4. Jemand daheim?
Bolean oder
Anzahl (0 ... n-1)
Mehr ist da nicht dran. Üblicherweise wird auch nicht mehr gebraucht
Es kann natürlich, bei aufwändigen Implementationen noch ein Status
mitgeschleppt werden, also:
Überlauf
oder
Versuch leeren Puffer zu lesen
am liebsten aber
OK
Wo tatsächlich die Daten im Puffer rumhängen interessiert kein Schwein.
Übrigens: 500 KHz sind rund um den I²C unüblich.
Meist werden 100 oder 400 KHz verwendet. Manche Chips können auch 1 MHz,
aber die Rasterung oberhalb von 400 KHz ist wohl dem Implementierer
überlassen.
Sebastian S. schrieb:> Die meisten Ringpuffer verwenden vier Routinen.
Und welche ist dafür zuständig, einen kontinuierlichen Datenblock an das
DMA zu übergeben? Für jedes einzelne Byte "pop" aufzurufen ist doch sehr
ineffizient.
Sebastian S. schrieb:> Übrigens: 500 KHz sind rund um den I²C unüblich.
Niemand hat von I²C geredet.
@DeErr Sommer
Aus prinzipielle Gründen - schau' Dir mal eine Implementation an - ist
die Pop-Routine relativ einfach und somit auch schnell.
Die Alternative wäre ja: So lange den Status aufzurufen, bis die
gewünschte Menge für einen ganzen Block vorhanden ist.
Übrigens sollte es kein Problem sein einen Block-Copy Befehl zu
implementieren, solange gesichert ist, dass genügend Daten vorhanden
sind. Andernfalls besteht die Gefahr, dass sich das System selber
blockiert und jeglicher Vorteil verloren geht.
Eine weitere Möglichkeit wäre: Bei jedem Push den Status abfragen und
beim erreichen von ??? einen DMA-Transfer zu initialisieren. Das läuft
aber ebenfalls auf die ständige Abfrage des Status (beim Pushen), wenn
auch intern, heraus.
Sebastian S. schrieb:> Aus prinzipielle Gründen - schau' Dir mal eine Implementation an - ist> die Pop-Routine relativ einfach und somit auch schnell.
Selbst wenn sie nur 1 Takt braucht, sind das bei 5kB immer noch 5120
Takte mehr als wenn man DMA nutzen würde. Du willst nicht ernsthaft
behaupten dass die Nutzung von DMA nicht sinnvoll ist? Selbst wenn
Rechenzeit genug ist, spart man so zumindest doch eine Menge Energie.
Offensichtlich hat der TO ja schon entschieden, dass DMA sinnvoll ist.
Sebastian S. schrieb:> Die Alternative wäre ja: So lange den Status aufzurufen, bis die> gewünschte Menge für einen ganzen Block vorhanden ist.
Oder auf einen Interrupt warten und währenddessen den Core schlafen
legen oder andere Aufgaben bewältigen.
Sebastian S. schrieb:> Das läuft> aber ebenfalls auf die ständige Abfrage des Status (beim Pushen), wenn> auch intern, heraus.
Es sei denn das Befüllen geht auch per DMA.
Ich habe schon sehr schnelle Datenübertragungen mit DMA und Ringpuffern
implementiert. Damit kann man auf viele MByte/Sec kommen, indem man eben
nicht jedes einzelne Byte in Software (push/pop-Methoden) behandelt.
den gesamten Ringbuffer mit einem DMA transfer kann ich wohl nicht mit
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
erreichen (?)
So wie ich das sehe, kann ich die DMA_BufferSize einstellen und die
DMA_Memory0BaseAddr.
Aber er müsste ja nicht am Anfange (BaseAddr) beginnen, sondern irgendwo
in der Mitte (last Output-Index), bis zur Buffergröße inkrementieren,
dann auf die Base-Addr zurückspringen,...
Also geht es nur als 2 Blöcke.(?)
stm32-ianer schrieb:> den gesamten Ringbuffer mit einem DMA transfer kann ich wohl nicht> mit> DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;> erreichen (?)
Nicht so wirklich. Dabei wird immer der gleiche Puffer von Anfang-Ende
gelesen/geschrieben. Du kannst nicht in der Mitte anfangen und dann am
Anfang weitermachen. Einige größere STM32 wie der STM32F407 haben einen
Doppel-Puffer-Modus beim DMA, damit geht das.
pegel schrieb:> Warum nicht den Puffer am Stück übertragen und zusätzlich Start-> und> Endadresse, dann am Raspberry umschaufeln?
Die interne Datenstruktur nach außen zeigen? Wehe man will die später
noch ändern... Das Problem ist nicht so schwer zu bewältigen. Man muss
halt im Transfer Complete Interrupt schnell das DMA neu konfigurieren.
Ist zu schaffen.
Naja, man kann den Transfer ja in zwei DMA-Transfers teilen. Wie schon
oben angemerkt hat die CPU bei 500KHz SPI Takt 768 Zyklen Zeit den
nächsten DMA zu starten.
Das ist nun wahrlich keine Raketentechnik.
Man muss natürlich schauen wie die Messungen realisiert sind und ggfs.
an den Interruptprioritäten schrauben.