Forum: Mikrocontroller und Digitale Elektronik Vom stm32f0 große Mess-Datenmengen (bis 5kB) mit SPI rausholen


von stm32-ianer (Gast)


Lesenswert?

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.

von Markus M. (adrock)


Lesenswert?

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?

von Dr. Sommer (Gast)


Lesenswert?

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.

von Markus M. (adrock)


Lesenswert?

Schon komisch das wir als Hobbyisten jemandem bei trivialen Sachen 
helfen müssen, der etwas an "Kunden" verkauft ;-)

von Dr. Sommer (Gast)


Lesenswert?

Markus M. schrieb:
> Schon komisch das wir als Hobbyisten jemandem helfen müssen, der
> etwas
> an "Kunden" verkauft ;-)

Vielleicht gibts hier ja ne Gewinnbeteiligung?

von Sebastian S. (amateur)


Lesenswert?

Ü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.

von Dr. Sommer (Gast)


Lesenswert?

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.

von Sebastian S. (amateur)


Lesenswert?

@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.

von Dr. Sommer (Gast)


Lesenswert?

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.

von stm32-ianer (Gast)


Lesenswert?

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.(?)

von Dr. Sommer (Gast)


Lesenswert?

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.

von pegel (Gast)


Lesenswert?

Warum nicht den Puffer am Stück übertragen und zusätzlich Start- und 
Endadresse, dann am Raspberry umschaufeln?

von Dr. Sommer (Gast)


Lesenswert?

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.

von Markus M. (adrock)


Lesenswert?

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.

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.