Forum: Mikrocontroller und Digitale Elektronik STM32 SPI DMA - Overrun error


von ch (Gast)


Lesenswert?

Hallo,

ich habe einen STM32F4 Controller bei dem ich über SPI von einem 
externen ADC Daten abhole. Der ADC triggert einen externen Interrupt von 
dem Controller und in der ISR starte ich dann die Empfangsroutine mit 
HAL_SPI_Receive_DMA(). Sobald alle Daten da sind wird der DMA Interrupt 
ausgelöst und ich kann diese weiterverarbeiten.

Das läuft soweit auch schon jedoch kommt es nach einer gewissen Zeit zu 
einem SPI Overrun Error. Die DMA-Priorität ist bereits auf "Very high" 
und andere Streams des DMA haben nur eine "Low" Priorität.

Kann mir jemand einen Tipp geben an was das liegen kann? Offensichtlich 
kann der DMA die Daten nicht rechtzeitig auslesen und dadurch kommt es 
dann zum Overrun error?

Code kann ich leider nicht posten...

Vielen Dank
ch

von sdf (Gast)


Lesenswert?

>ADC triggert einen externen Interrupt von
>dem Controller und in der ISR starte ich dann die Empfangsroutine mit
>HAL_SPI_Receive_DMA().

Meiner Ansicht nach ist das falsch.
Wenn Du DMA und den ADC richtig aufgesetzt hast, wird der DMA Transfer
vom ADC getriggert, und zwar OHNE zwischengeschalteten Interrupt!
Sonst hätte DMA auch relativ wenig Sinn...
HAL_SPI_Receive_DMA() müsste die DMA scharf schalten - und zwar in dem 
Kontext, in dem Du auch den ADC aufsetzt.


Aber diese Theorie bitte nochmal überprüfen!

von sdf (Gast)


Lesenswert?

Anm: Sehe gerade, dass der ADC extern ist.
Mglw. braucht man da den Trigger-Interrupt tatsächlich, im Gegensatz zu 
einem internen ADC

von ch (Gast)


Lesenswert?

sdf schrieb:
> Meiner Ansicht nach ist das falsch.
> Wenn Du DMA und den ADC richtig aufgesetzt hast, wird der DMA Transfer
> vom ADC getriggert, und zwar OHNE zwischengeschalteten Interrupt!

Hab mich vllt nicht ganz eindeutig ausgedrückt. Es handelt sich um einen 
externen ADC welcher über SPI angebunden ist. Dieser hat ein "Data 
ready" signal welches nach der Wandlung gesetzt ist. Zu dem Zeitpunkt 
sind dann die Daten abrufbereit und daher starte ich dann damit den 
DMA-Transfer.

von sdf (Gast)


Lesenswert?

Hmm, müsste auch so gehen.

Siehe auch
Figure 36. Peripheral-to-memory mode
in RM0090

Die Frage ist, woher man das REQ_STREAM nimmt (von dem GPIO, der jetzt 
Deinen IRQ triggert?) und wie man das verknüpft.

von sdf (Gast)


Lesenswert?

>Zu dem Zeitpunkt
>sind dann die Daten abrufbereit und daher starte ich dann damit den
>DMA-Transfer.

Das habe ich schon so verstanden.
Wenn das so geht, wie ich denke, schaltest Du
wie oben bereits geschrieben, mit

HAL_SPI_Receive_DMA()

den Transfer scharf, d.h. Du setzt den Transfer auf -- er wird da noch 
nicht ausgeführt.

Der Transfer selber wird dann durch dass REQ_STREAM getriggert,
gemäss der vorher gesetzten DMA Config.

von sdf (Gast)


Lesenswert?

s/dass/das.

von ch (Gast)


Lesenswert?

sdf schrieb:
> Hmm, müsste auch so gehen.
>
> Siehe auch
> Figure 36. Peripheral-to-memory mode
> in RM0090
>
> Die Frage ist, woher man das REQ_STREAM nimmt (von dem GPIO, der jetzt
> Deinen IRQ triggert?) und wie man das verknüpft.

Nein, bei der Abbildung ist ja die "peripheral source" das SPI-Modul. An 
sich passt das schon so.

sdf schrieb:
> Das habe ich schon so verstanden.
> Wenn das so geht, wie ich denke, schaltest Du
> wie oben bereits geschrieben, mit
>
> HAL_SPI_Receive_DMA()
>
> den Transfer scharf, d.h. Du setzt den Transfer auf -- er wird da noch
> nicht ausgeführt.
>
> Der Transfer selber wird dann durch dass REQ_STREAM getriggert,
> gemäss der vorher gesetzten DMA Config.

Ja genau. Transferiert wird dann sobald die Bytes am SPI eingehen.

von sdf (Gast)


Lesenswert?

>Nein, bei der Abbildung ist ja die "peripheral source" das SPI-Modul. An
>sich passt das schon so.

Ja, nein.
Die Frage ist ja, ob Du einen separaten Interrupt brauchst, um DMA
in Bewegung zu setzen. oder nicht.
Ich behaupte nein, sehe aber im Moment nicht, wie man REQ_STREAM
mit einem Trigger-GPIO verbinden könnte.
Das habe ich in RM0090 auf die Schnelle nicht gefunden.

Ich vermute, dass Dein Problem aus einer Race-Condition
zwischen Trigger-Interrupt und DMA Operation resultiert.


AN4031 DMA controller description
kennst Du?
Hab's nicht selbst gelesen, aber vielleicht hilft das.

Frag' ansonsten halt Deinen ST FAE ;-).

von sdf (Gast)


Lesenswert?

> Transferiert wird dann sobald die Bytes am SPI eingehen.

Soll das heissen, dass der ADC der SPI Master ist?
Ansonsten würde ich in dem Satz einen weissen Schimmel erkennen...

Und wo beobachtest Du den "SPI Overrun"? Controller Seite oder ADC?

Was heisst Overrun genau?
Vermutlich, dass Du in's SPI Daten-Ausgangsregister schreibst, solange 
ein Transfer im Gang ist?!

von ch (Gast)


Lesenswert?

sdf schrieb:
> Soll das heissen, dass der ADC der SPI Master ist?

Nein, der STM32 ist Master. Der ADC (ADS131E04) ist Slave. Der Slave 
meldet lediglich, dass das Sampling fertig ist und der STM liest dann 
die Daten aus.

Ich habe nun die FIFO aktiviert und jetzt läuft der Controller bereits 
2h ohne einen SPI Overrun. Damit wäre es evtl. gelöst, jedoch weiss ich 
die Ursache trotzdem nicht...

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Na dein DMA ist nicht der einzige Busmaster der im SRAM schreiben will.
Die CPU nutzt ja auch noch Variablen und vor allem den Stack im SRAM.

Daher haben die größeren STM32 auch bis zu 3 getrennte SRAM und auchnoch 
CCM.

Ab und zu war die CPU eben schneller und der DMA bekommt nicht schnell 
genug SRAM Zugriff.
Die DMA interne FIFO kann das dann puffern.

Welche Samplerate hat dein ADC überhaupt?

von ch (Gast)


Lesenswert?

Mw E. schrieb:
> Na dein DMA ist nicht der einzige Busmaster der im SRAM schreiben
> will.
> Die CPU nutzt ja auch noch Variablen und vor allem den Stack im SRAM.

Das hört sich logisch an. Danke für den Tipp.

Die Abtastfrequenz beträgt 8 kHz und es werden 4 Kanäle (sind dann 15 
bytes) übertragen. Also nicht wirklich viel eigentlich...

von John Doe (Gast)


Lesenswert?

Mw E. schrieb:
> Na dein DMA ist nicht der einzige Busmaster der im SRAM schreiben will.
> Die CPU nutzt ja auch noch Variablen und vor allem den Stack im SRAM.
>
> Daher haben die größeren STM32 auch bis zu 3 getrennte SRAM und auchnoch
> CCM.
>
> Ab und zu war die CPU eben schneller und der DMA bekommt nicht schnell
> genug SRAM Zugriff.
> Die DMA interne FIFO kann das dann puffern.
>
> Welche Samplerate hat dein ADC überhaupt?


https://www.st.com/resource/en/application_note/dm00046011.pdf

von Jim M. (turboj)


Lesenswert?

ch schrieb:
> HAL_SPI_Receive_DMA()
 > [...]
> SPI Overrun Error

Eventuell nutzt der irgendeine interne SPI Funtktion (Auto-TX o.ä.) zum 
Senden. Da kann der Fifo überlaufen, wenn er den RAM nicht schnell genug 
beschreiben kann.

Abhilfe: Sowohl TX als auch RX mit DMA machen. Der TX hat dabei 
niedrigere Priorität als RX, so dass immer zuerst RX und dann TX 
stattfindet.

Benötigt einen zusätzlichen DMA Kanal und einen Puffer für die Dummy TX 
Daten.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Währenddessen so ein ext ADC die Daten über MISO sendet ignoriert er eh 
meist was er auf MOSI bekommt.
Daher darf das schon derselbe Buffer sein ;)
Manchmal muss eben an den Anfang ein Header, aber den ließt der DMA FIFO 
aus bevor der RX DMA das überschreibt.

ch schrieb:
> Die Abtastfrequenz beträgt 8 kHz und es werden 4 Kanäle (sind dann 15
> bytes) übertragen. Also nicht wirklich viel eigentlich...

Das sollte eigentlich noch nicht zu Problemen führen, der FIFO gehört 
aber trotzdem imemr aktiviert.
Für mein Wirkleistungsmessgerät lese ich einen ADC mit 152kS/s aus bei 
4Byte.
Da wirds dann Eng mit IRQ detektieren und dann den DMA anstoßen.
Funktioniert aber bestens und eine GUI kanns auch noch bespaßen.

von тролхантэр (Gast)


Lesenswert?

Normalerweise macht der DMA alles selbst, sofern die externe Quelle 
einen Trigger pro Transfer bereitstellt.
Falls du nun einen 24Bit ADC hast, der einen Ready gibt, dann aber 3 
Bytes gelesen haben will, ist der DMA das falsche Tool.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Doch den DMA brauch ich schon für die 4 Bytes (2 Kanäle zu 16Bit).
Mit noch einem IRQ für den SPI RX gabs dann Probleme.
Durch den ADUM zum trennen der Signale (der ADC misst an 230V rum) kann 
ich den SPI nur auf 10MHz aufdrehen, dadurch wirds schon recht knapp.

Aber zeig mir doch mal wie ich bei einem F405 den DMA extern triggern 
lasse, in den requesttabellen gibts nur Timertrigger, aber das is was 
andees als durch einen ext Pin triggern zu lassen.
Der ADC hat durchaus ein rdy Signal, nur nutz ich das eben nur für einen 
EXTI.

von sdf (Gast)


Lesenswert?

>Normalerweise macht der DMA alles selbst, sofern die externe Quelle
>einen Trigger pro Transfer bereitstellt.

Das habe ich eben anfangs auch gedacht.
Scheint aber nicht so zu sein.
Normale GPIOs triggern zu lassen geht wohl nicht.
Die entsprechenden Tabellen un RM0090 ("DMA request mapping")
enthalten jedenfalls keine GPIOs.
Allenfalls könnte man vielleicht einen UART oder Timer Control Pin
als Trigger zweckentfremden.

Habe jedenfalls nix gefunden, wie man die REQ_STREAM Signale, die in 
RM0090 dargestellt sind mit einem GPIO verbinden könnte.

Die Frage ist, ob DMA in diesem Kontext überhaupt einen Sinn hat.
Wenn man einen Interrupt braucht um den DMA scharf zu schalten und dann 
noch einen DMA-End-of-Transfer Interrupt ist das doch ziemlich fraglich.
Auf diese Weise lässt sich ja auch nicht mehr als ein Datenwort pro DMA 
Transfer übertragen - oder übersehe ich da was?

von sdf (Gast)


Lesenswert?

>Abhilfe: Sowohl TX als auch RX mit DMA machen. Der TX hat dabei
>niedrigere Priorität als RX, so dass immer zuerst RX und dann TX
>stattfindet.
>
>Benötigt einen zusätzlichen DMA Kanal und einen Puffer für die Dummy TX
>Daten.

Sollte unnötig sein.
STM32F4 SPI kann auch unidirektional SPI.

von ch (Gast)


Lesenswert?

sdf schrieb:
> Die Frage ist, ob DMA in diesem Kontext überhaupt einen Sinn hat.
> Wenn man einen Interrupt braucht um den DMA scharf zu schalten und dann
> noch einen DMA-End-of-Transfer Interrupt ist das doch ziemlich fraglich.
> Auf diese Weise lässt sich ja auch nicht mehr als ein Datenwort pro DMA
> Transfer übertragen - oder übersehe ich da was?

Ja. Zumindest bei mir. Ich habe jetzt einen ext. Interrupt zum DMA 
scharf schalten und dann einen Interrupt wenn alle Daten da sind. 
Insgesamt sind das 15 Bytes. Ohne DMA hätte ich ja 15 Interrupts bei 
denen ich jeweils händisch das empfangene Byte in den RAM schreiben 
muss.

von sdf (Gast)


Lesenswert?

>Ohne DMA hätte ich ja 15 Interrupts bei
>denen ich jeweils händisch das empfangene Byte in den RAM schreiben
>muss.

Nicht notwendigerweise. Die 15Bytes könntest Du ja auch in einer 
Schleife auslesen - wobei es natürlich stimmt, dass die Geschwindigkeit 
dann durch den SPI Takt bestimmt wird... da könnten 15 Interrupts 
durchaus schneller sein.

Hast Du eigentlich

>Allenfalls könnte man vielleicht einen UART oder Timer Control Pin
>als Trigger zweckentfremden.

schon mal in Betracht gezogen?
Bin mir aber nicht sicher, ob das die HW überhaupt hergibt.
Aber: Transferadressen kann man ja offensichtlich unabhängig von Stream 
und Channel festlegen..., d.h. es könnte gehen...

Die Frage von gestern:

>Und wo beobachtest Du den "SPI Overrun"? Controller Seite oder ADC?
>
>Was heisst Overrun für Dich genau?

hast Du, wenn ich nichts übersehen habe, noch nicht beantwortet.

von ch (Gast)


Lesenswert?

sdf schrieb:
> Nicht notwendigerweise. Die 15Bytes könntest Du ja auch in einer
> Schleife auslesen - wobei es natürlich stimmt, dass die Geschwindigkeit
> dann durch den SPI Takt bestimmt wird... da könnten 15 Interrupts
> durchaus schneller sein.

Der SPI ist relativ langsam - muss ja an sich nur fertig werden bis die 
nächsten Daten kommen. Und der Controller hat genug zu tun...

sdf schrieb:
> Hast Du eigentlich
>
>>Allenfalls könnte man vielleicht einen UART oder Timer Control Pin
>>als Trigger zweckentfremden.
>
> schon mal in Betracht gezogen?

Nein, hab ich nicht. Kann mir aber auch kaum vorstellen wie das gehen 
soll. Zudem ist die Hardware schon fertig - müsste also dann auf dem 
bisher verwendeten GPIO gehen...

sdf schrieb:
> Die Frage von gestern:
>
>>Und wo beobachtest Du den "SPI Overrun"? Controller Seite oder ADC?
>>
>>Was heisst Overrun für Dich genau?
>
> hast Du, wenn ich nichts übersehen habe, noch nicht beantwortet.

Auf der Controller-Seite. Das SPI hat ja Error-Flags und die Interrupts 
für Errors sind aktiv und wenn das passiert ist eben ein Interrupt 
gekommen und das Flag war gesetzt.

Jedoch läuft der Debugger mittlerweile fast 48h und es ist nicht mehr 
aufgetreten. Ist wohl wirklich durch die FIFO soweit entschärft worden 
dass dies nicht mehr auftritt :-)

Nochmals Danke für den Tipp!

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Ich hab nochmal nachgelesen.
Es ist bei STM32 nicht möglich den DMA so zu triggern, dass er xyz Bytes 
abholt.
Der DMA Trigger holt immer nur eine Transfersize ab, also uint8/16/32.

Kennt denn wer einen µC der das kann?
Das interessiert mich jetzt einfach mal.

Es gibt ja schon DMA freundliche ADC wie zB den MCP3910.
Einmal den SPI Header hingeschickt und nCS low lassen und clk senden, 
schon kommen immer die Datenbytes.
Einen IRQ/ready Pin gibts auch.

von sdf (Gast)


Lesenswert?

>Es ist bei STM32 nicht möglich den DMA so zu triggern, dass er xyz Bytes
>abholt.
>Der DMA Trigger holt immer nur eine Transfersize ab, also uint8/16/32.

Sicher?

Wozu bräuchte man dann

DMA stream x number of data register (DMA_SxNDTR) (x = 0..7)

?

Bits 15:0NDT[15:0]: Number of data items to transfer
Number of data items to be transferred (0 up to 65535). This register 
can be written only
when the stream is disabled. When the stream is enabled, this register 
is read-only,
indicating the remaining data items to be transmitted. This register 
decrements after each
DMA transfer.


MEMSIZE und PSIZE werden separat konfguriert


Das Problem in diesem Kontext ist eher, dass es den Trigger nur für 
interner Peripherie gibt...

von sdf (Gast)


Lesenswert?

> Hast Du eigentlich
>
>>Allenfalls könnte man vielleicht einen UART oder Timer Control Pin
>>als Trigger zweckentfremden.
>
> schon mal in Betracht gezogen?

>Nein, hab ich nicht. Kann mir aber auch kaum vorstellen wie das gehen
>soll. Zudem ist die Hardware schon fertig - müsste also dann auf dem
>bisher verwendeten GPIO gehen...

Ja, wenn dieser als Timer-Capture Trigger geeignet wäre.
Timer so programmieren, dass jedes Timer Capture event einen DMA 
REQ_STREAM_x triggert, gemäss Table 42/43. DMAx request mapping
in RM0090.

Für DMA Timer Stream/Channel Kombi auswählen, aber Peripheral Address 
des SPI Datenregisters, so dass nicht der aktuelle Timer-Count geDMA't
wird sondern das SPI Datenregister...

Oder eine UART Steuerleitung, wenn der UART anderweitig gebraucht wird.

Vielleicht ginge es ja so, oder ähnlich.

Könnte alles so einfach sein, wenn man einen GPIO als REQ_StREAM Trigger 
konfigurieren könnte...

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Pro trigger wird dieses Register um eins runtergezählt.
Also wenn du zB 42 Bytes per UART empfangen willst.
Pro empfangenen Byte triggert der UART 1x dem DMA.
Nach 42 Bytes gehen die Daten dann ins Nirvana.

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.