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
>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!
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.
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.
>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.
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.
>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 ;-).
> 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?!
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...
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?
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...
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
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.
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.
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.
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.
>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?
>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.
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.
>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.
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!
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.
>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...
> 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...
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.