Forum: Mikrocontroller und Digitale Elektronik STM32: externer ADC 400 kHz - Timer+SPI+DMA Optimierung


von Erik Z. (erik_wolfram)


Lesenswert?

Hallo,

ich versuche derzeit ein Programm von einem ATXMEGA auf einen STM32F4 zu 
übertragen um die Abtastrate eines externen ADC's von 200 kHz auf 400 
kHz zu erhöhen. (2 16-Bit ADC's im Daisy-Chain-Betrieb = 32 Bit mit 200 
/ 400 kHz)
Mit dem ATXMEGA bin ich mittlerweile sehr fit. Für den STM fehlt mir 
leider noch die Ertfahrung wie ich das ganze umsetzen kann.

Durch die hohe Abtastrate bin ich an DMA gebunden, da ich auch noch 
einfache Routinen parallel zur Abtastung ausführen möchte. Beim ATXMEGA 
habe ich zur Kopplungt von Timern und DMA das Event-System verwendet. 
Dieses fehlt mir jedoch beim STM und macht mir die Aufgabe schwerer.

Der ADC arbeitet als Slave. Zum Start einer Wandlung muss ein PWM-Signal 
angelegt werden. 1,4 µs nach dem Start werden die Ergebnisse mit dem 
anliegenden SCK-Signal ausgegeben. Dieses wird seitens des µC durch das 
Ausgeben von "Dummy-Bytes" erzeugt.

Ich schaffe es einen DMA zum senden von 4 * 8 Bit = 32 Bit über den SPI 
aufzusetzen. Die Werte werden damit unimttelbar aneinander gereiht(keine 
Pause zwischen den Bytes). Für die DMA-Übertragung der 4 Bytes muss 
jedoch jedes mal das Enabled-Flag gesetzt werden. Das mache ich momentan 
über ein Interrupt. Leider ist die Latenz des Interrupts für diese 
Anwendung zu hoch (ca. 450 ns bei 168 MHz µC-Takt).
Daher suche ich einen Weg Interrupts zu umgehen.

Ich möchte hier keinen Code besprechen, sondern suche eine "Taktik" um 
an dieses Problem und dessen Lösung heranzutreten.
Kann ich einen DMA geschickt über andere DMAs, Timers oder ähnliche 
Module enablend/disablen? Welche anderen Möglichkeiten gäbe es?


Gruß Erik

von Michael F. (startrekmichi)


Lesenswert?

f4 reference manual
 DMA controller
  DMA functional description
   Channel selection

So ziemlich jede Peripherie kann DMA-Requests auslösen, in deinem Fall 
müsstest du einfach nur den passenden Stream/Channel raussuchen und im 
SPI den DMA-Request für TX einschalten. Und schon wird der komplette 
Datensatz ohne CPU-Beteiligung gesendet, also so wie DMA eigentlich 
gedacht ist.

von Erik Z. (erik_wolfram)


Lesenswert?

Danke für die Antwort!
Vielleicht habe ich ein wenig an Erklärung gesparrt, um das ganze genau 
zu erläutern. Das mit dem DMA-Request ist mir klar - diesen nutze ich 
auch:

Ein Zyklus dauert 2,5 µs. Nach dem Startsignal (Timer/PWM) wird nach 1,4 
µs der DMA über das Timer-Interrupt enabled. Der DMA nutzt dann den 
SPI-TX als Request um 4 Bytes zu übertragen. (Also der Request wird hier 
schon verwendet um die Bytes unmittelbar nacheinander zu senden). Das 
TC-Interrupt des DMAs nutze ich dann, um diesen zu deaktivieren damit 
ich ihn beim nächsten Zyklus neu starten kann.

Ich würde das Enable/Disable auch gerne umgehen! Aber wie kann ich den 
DMA über einen anderen DMA dazu befähigen von neuem anzufangen?
Gibt es andere Möglichkeiten DMA ohne Interrupts neu zu starten?

von Michael F. (startrekmichi)


Lesenswert?

Erik Z. schrieb:
> Ich würde das Enable/Disable auch gerne umgehen!
Ach so, ok. Das könnte etwas kompliziert werden, weil du ja nach dem 4. 
Byte einen DMA-Request vom SPI bekommst, aber gar nicht mehr weiter 
senden willst.

Ein Workaround könnte sein, mit einem Timer die Bytes einzeln zu 
triggern. Dafür wird's dann wahrscheinlich vier Kanäle eines Timers 
brauchen, aber es könnte gehen. Der DMA muss dann natürlich im circular 
Mode sein. Effektiv hast du dann vier Trigger für einen DMA Channel, 
aber das macht vermutlich/hoffentlich nichts.

Du willst doch eigentlich die Daten vom ADC einlesen, oder? Hast du noch 
einen zweiten DMA am Laufen, der parallel die Daten vom SPI in den RAM 
schiebt?

von Erik Z. (erik_wolfram)


Lesenswert?

Die Idee hatte ich auch schon!
Ich kann ja auch theoretisch 2 x 16 Bit per SPI senden. Leider habe ich 
hierfür aber nicht den DMA mit dem Timer verknüpfen können (wollte nicht 
funktionieren).
Vermutlich benötige ich hier auch wieder Interrupts zum behandeln des 
Timers welches mich Zeit kostet, die ich nicht habe....

von Martin K. (martinko)


Lesenswert?

Hi,

Ich würde - wenn möglich - eine CPLD zwischen STM32 und externem ADC 
setzen der auf beiden Seiten eine passende Datenübertragung herstellt.

Gruß Martin

von m.n. (Gast)


Lesenswert?

Martin K. schrieb:
> eine CPLD zwischen STM32 und externem ADC

Ich dachte, hier wird nach der einfachsten Schaltung gesucht.

Erik Z. schrieb:
> Leider ist die Latenz des Interrupts für diese
> Anwendung zu hoch (ca. 450 ns bei 168 MHz µC-Takt).

Das kann ich so nicht glauben. Kannst Du Deine Interruptroutine zeigen?

von Erik Z. (erik_wolfram)


Lesenswert?

Wenn es möglich wäre würde ich externe Schaltungen oder gar einen FPGA 
ausschließen wollen.
Das ganze scheitert bis jetzt knapp am Timing, eher würde ich daher die 
Abtastrate senken wenn es absolut nicht möglich ist.

Mit den Interrupts habe ich nochmal geprüft. Ich hatte vergessen die 
Optimierung zu aktivieren. So komme ich jetzt auf ca. 150 ns bei 168 
MHz.
(Gemessen mit einer PWM-Ausgabe und einem Interrupt mit getoggeltem 
Ausgang)

Wenn ich den DMA im Interrupt aktiviere um am SPI etwas auszugeben 
verzögert sich die Ausgabe nochmals (ca 300 ns Gesamtverzögerung). Kann 
die Ursache hierfür am festen Takt des SPIs liegen, dass die Ausgabe 
erst mit dem nächsten Zyklus erfolgt?

Ich überlegte momentan, ob ich vielleicht eine "Pseudo-Clock" per PWM 
ausgebene und von außen an den STM32 anlege um den STM32 als Slave 
laufen zu lassen... dieser Idee werde ich heute man nachgehen!

[EDIT] Ich habe nochmal die Funktion für das Enable des DMA-Kanals 
ersetzt - jetzt komme ich auf 260 ns Verzögerung.

: Bearbeitet durch User
von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Hi Erik,

a) Wie wäre es, wenn der ADC den DMA triggerte?
Wenn der ADC ein "Sample Ready" Signal zur Verfügung stellt, könnte dies 
den DMA anwerfen.
Wenn das Dein ADC nicht kann, gäbe es sicher was passendes bei den 
üblichen Herstellern.

b) Alternativ kannst Du auch den kompletten Sample-Vorgang an einen 
Timer hängen, der dann wieder den DMA-Request auslöst.

Schau Dir mal alle DMA-Request Quellen des STM32F4 an, irgendwas wird 
doch passen? :)

Grüße,
 Marcus

P.S.: ADC? STM32F4xx/Taktrate?
P.P.S.: ein Interface wie z.B. beim LTC14071 würde passen
P.P.P.S: und ja, vor zehn Jahren hätte ich da auch noch ein CPLD 
zwischengehängt, aber die STM32 sind wirklich ausreichend ausgestattet

von Gerd E. (robberknight)


Lesenswert?

Marcus H. schrieb:
> a) Wie wäre es, wenn der ADC den DMA triggerte?
> Wenn der ADC ein "Sample Ready" Signal zur Verfügung stellt, könnte dies
> den DMA anwerfen.

wie kannst du bei einem STM32F4 von einem GPIO aus direkt (ohne Umweg 
über Interrupt) einen DMA auslösen lassen?

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Gerd E. schrieb:
> Marcus H. schrieb:
>> a) Wie wäre es, wenn der ADC den DMA triggerte?
>> Wenn der ADC ein "Sample Ready" Signal zur Verfügung stellt, könnte dies
>> den DMA anwerfen.
>
> wie kannst du bei einem STM32F4 von einem GPIO aus direkt (ohne Umweg
> über Interrupt) einen DMA auslösen lassen?

In RM0090 - Chapter 10 - DMA was passendes zusammensuchen. Und schauen, 
was es im Web so gibt.
Im vorliegenden Fall hatte ich gar nicht an GPIO gedacht, sondern dass 
der externe ADC die SPI triggert und diese wiederum den DMA.
Oder der STM32 ist wieder Master und gibt einen Timertakt vor.

Im einfachsten Fall kann man bei den vorgegebenen Taktraten aber auch 
mit Interrupt arbeiten. In einem meiner Geräte (SSI USB 
Konverterplattform) pumpt der STM32F405 mit über 1MHz Daten in die 
GPIOs, getriggert durch externen Interrupt. GCC 4.8 -o3 168MHz.

Wenn der Interrupt selbst in -o3 zu viel Overhead hat, bieten sich ein 
paar Zeilen Maschinencode an. Sollte aber nicht notwendig sein.

von Erik Z. (erik_wolfram)


Lesenswert?

Jetzt hatte ich doch nicht die Zeit mich damit weiter zu beschäftigen 
und dann schon wieder neue (hilfreiche) Ratschläge hier!

Bei dem ADC handelt es sich um einen ADS8319 mit 500 kHz. Die 
SPI-Schnittstelle wird mit 42 MHz betrieben. Der ADC ist recht einfach 
(10-Pin Gehäuse) wobei die Spannungsversorgung und die SPI-Pins alle 
Ein- und Ausgänge belegen.
Über einen Pull-Up-Winderstand ist es jedoch möglich, dass der SDO 
(MISO) Ausgang nach beendeter Wandlung kurzzeitig auf low gezogen wird. 
Hier könnte man eventuell den DMA über einen parallelen Pin koppeln. -> 
nur müsste man diesen schnell deaktivieren um ein dauerhaftes Triggern 
während der Übertragung zu unterbinden.
Heute habe ich aber leider wenig/keine Zeit dafür, ich werde mich 
frühstens heute Abend nochmal daran setzten.
Wenn ich Fortschritte (Erkenntnisse) erziele werde ich hier berichten!

von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Da STM32 keine 32-bit SPI Transfers kann, hast Du ein Problem: Du kannst 
nicht den circular DMA mode verwenden! Damit musst Du in jeden Zyklus im 
DMA TC Interrupt den DMA Transfer neu aufsetzen. Bei 16-Bit Transfers 
koennest Du dagegen einen Circular DMA Transfer aufsetzten ohne dass Du
Kannst Du nicht die ADC Kette auftrennen und jeden ADC von jeweils 
einen SPI Kanal bedienen lassen? Ich habe ein aehnliche Problem wie 
folgt geloest:

 * ADC Signale
 * - SPI1 (oberer Kanal)
 *   TIM2 Periode: 4 us, CH3 aktiv: 3 us
 *   Compare started ueber DMA_CH1 SPI1
 *   SPI1 RX wird ueber DMA_CH2 ausgelesen
 * - SPI2 (unterer Kanal)
 *   TIM15 Periode: 4 us, CH3 aktiv: 3 us
 *   Compare started ueber DMA_CH5 SPI2
 *   SPI2 RX wird ueber DMA_CH4 ausgelesen
 *
 * - HTIF/CTIF DMA1_CH1 signalisiert Auswertethread.
 * - Thread akkumuliert SPI Werte.

von Gerd E. (robberknight)


Lesenswert?

Marcus H. schrieb:
>> wie kannst du bei einem STM32F4 von einem GPIO aus direkt (ohne Umweg
>> über Interrupt) einen DMA auslösen lassen?
>
> In RM0090 - Chapter 10 - DMA was passendes zusammensuchen.

ja, genau. 10.3.3, Table 42 und 43 um genau zu sein. Doch dort sehe ich 
wie schon geschrieben keine Möglichkeit, durch einen GPIO einen 
DMA-Request auszulösen. Bei anderen Controllern, z.B. Kinetis, geht das.

> Im vorliegenden Fall hatte ich gar nicht an GPIO gedacht, sondern dass
> der externe ADC die SPI triggert und diese wiederum den DMA.

Der ADC also als SPI-Master? Ist das nicht eher unüblich? Ich behaupte 
nicht schon hunderte ADCs gesehen zu haben, aber von denen die ich 
kenne, kann das kein einziger.

> Oder der STM32 ist wieder Master und gibt einen Timertakt vor.

genau. Doch wie bekommst Du dann das "Konvertierung fertig"-Signal in 
einen DMA-Request umgewandelt?

Erik schreibt ja daß er das an einen GPIO leiten könnte:

Erik Z. schrieb:
> Über einen Pull-Up-Winderstand ist es jedoch möglich, dass der SDO
> (MISO) Ausgang nach beendeter Wandlung kurzzeitig auf low gezogen wird.
> Hier könnte man eventuell den DMA über einen parallelen Pin koppeln.

Und ich wüsste gerne wie man daraus dann beim STM32 einen DMA-Request 
gemacht bekommt.

Wäre es evtl. möglich den SPI kontinuierlich und ohne Unterbrechnung per 
DMA Daten lesen zu lassen und danach dann per Software die tatsächlichen 
Daten aus dem Puffer rauszupicken? Den Chip Select könnte man z.B. von 
einem Timer per PWM-Ausgabe erzeugen lassen, damit sollte das den ADC 
nicht durcheinander bringen. Die Herausforderung dürfte nur sein, die 
Position der Nutzdaten im Puffer zuverlässig vorherzusagen. Oder 
übersehe ich hier was?

von Vincent H. (vinci)


Lesenswert?

Ich versteh das Problem ehrlich gesagt nicht ganz. Das Datenblatt gibt 
vor, dass der F4 für den Daisy Chain Mode als Receive Only Master zu 
betreiben ist. Man beachte die Pfeile des Clocks, der bei einer SPI 
ausschließlich vom Master ausgehn kann.

-> CONVST setzen
-> entweder auf IRQ reagieren oder tconv warten
-> Master Receive der Daten, getriggert durch
a) EVENT/IRQ (nur über Umwege möglich denk ich, etwa mit einem Input 
Capture auf den Flanken und einem etwas missbrauchten DMA request, der 
statt dem Input Capture halt SPI Daten schaufelt)
Dadurch missbraucht man einen eigentlichen Timer DMA request als "quasi" 
externen IRQ request, dens aber halt leider nicht gibt. Glücklicherweise 
gibts ja genug Timer...
b) Timer Update nach tconv Wartezeit

Das ganze ist mehr oder weniger ohne einzigen CPU Cycle zu 
bewerkstelligen.

: Bearbeitet durch User
von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Gerd E. schrieb:
> Marcus H. schrieb:
>>> wie kannst du bei einem STM32F4 von einem GPIO aus direkt (ohne Umweg
>>> über Interrupt) einen DMA auslösen lassen?
>>
>> In RM0090 - Chapter 10 - DMA was passendes zusammensuchen.
>
> ja, genau. 10.3.3, Table 42 und 43 um genau zu sein. Doch dort sehe ich
> wie schon geschrieben keine Möglichkeit, durch einen GPIO einen
> DMA-Request auszulösen. Bei anderen Controllern, z.B. Kinetis, geht das.
>
>> Im vorliegenden Fall hatte ich gar nicht an GPIO gedacht, sondern dass
>> der externe ADC die SPI triggert und diese wiederum den DMA.
>
> Der ADC also als SPI-Master? Ist das nicht eher unüblich? Ich behaupte
> nicht schon hunderte ADCs gesehen zu haben, aber von denen die ich
> kenne, kann das kein einziger.

MAH-> ADC ist nicht SPI Master, sondern er triggert den DMA - 
"irgendwie". Wenn wir keine passende DMA-Request finden, dann wäre doch 
der Timer unser Freund. Es spricht doch sicher nichts dagegen, dass der 
STM32 die Samplerate vorgibt?


>
>> Oder der STM32 ist wieder Master und gibt einen Timertakt vor.
>
> genau. Doch wie bekommst Du dann das "Konvertierung fertig"-Signal in
> einen DMA-Request umgewandelt?
>
> Erik schreibt ja daß er das an einen GPIO leiten könnte:
>
> Erik Z. schrieb:
>> Über einen Pull-Up-Winderstand ist es jedoch möglich, dass der SDO
>> (MISO) Ausgang nach beendeter Wandlung kurzzeitig auf low gezogen wird.
>> Hier könnte man eventuell den DMA über einen parallelen Pin koppeln.
>
> Und ich wüsste gerne wie man daraus dann beim STM32 einen DMA-Request
> gemacht bekommt.
>
> Wäre es evtl. möglich den SPI kontinuierlich und ohne Unterbrechnung per
> DMA Daten lesen zu lassen und danach dann per Software die tatsächlichen
> Daten aus dem Puffer rauszupicken? Den Chip Select könnte man z.B. von
> einem Timer per PWM-Ausgabe erzeugen lassen, damit sollte das den ADC
> nicht durcheinander bringen. Die Herausforderung dürfte nur sein, die
> Position der Nutzdaten im Puffer zuverlässig vorherzusagen. Oder
> übersehe ich hier was?

MAH->Yep, siehe oben.


Nun haben wir ja eine Menge Ansätze gesehen.
Wie man das in jedem einzelnen Fall aufzieht, ergibt sich aus den 
Leistungsanforderungen an die Baugruppe. Der STM32F4 kann, teilweise 
über seltsame Klimmzüge, die wildesten Dinger. Allerdings muss ich 
zugeben, dass bei mir so eine Lösung meistens über Tage (und Nächte im 
Halbschlaf) im Hinterkopf reift.

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

@Erik Z.: Mmmh? Warum der Rückzieher?

von Erik Z. (erik_wolfram)


Lesenswert?

Der Rückzieher war ein etwas kleineres Problem, was ich unmittelbar nach 
dem Einstellen der Frage lösen konnte -> daher gleich gelöscht.

Momentan nutze ich jetzt doch erstmal die Interrupts, auch wenn ich 
damit unzufrieden mit der CPU-Auslastung bin (geschätzt 30 %). Den 
geistigen Blitz hatte ich bis jetzt noch nicht, und daher versuche ich 
es erstmal auf diesem Wege.
Je mehr man sich mit der Materie beschäftigt umso mehr Gefühl bekommt 
man für die potentiellen Möglichkeiten!

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.