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!
Anm: Sehe gerade, dass der ADC extern ist. Mglw. braucht man da den Trigger-Interrupt tatsächlich, im Gegensatz zu einem internen ADC
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.