Hallo,
ich habe einen ADS7054 ADC angeschlossen an STM32L431RBI, den ich mit
400kSPS auslesen möchte, möglichst ohne uC-Last. Bevor ich wirklich an
der Platine rumteste, wollte ich das prinzipielle Vorgehen am
STM32f769-DISCO Evalboard ausprobieren, deswegen auch STM32F7 in der
Überschrift. Sollte ja dann übertragbar sein, denke ich.
Der ADC benötigt 18 clocks und SS-Puls, siehe Anhang. Das ist die
eigentliche Problematik, denn SPI von STM kann maximal 16 Bits
raustakten wenn DR-Register beschrieben wird.
Zunächst interessiert mich das Senden, also das Zusammenspiel von clock
und NSS. NSS in hardware funktioniert nur bis halbwort-Größe (0b1111 <<
SPI_CR2_DS_Pos). Habe testweise also DMA circular und NSS in hardware
ausprobiert, genau was ich brauche, aber eben nur 16 clocks und nicht
18.
Dann gelesen, dass üblicherweise NSS in Software genutzt wird. Also SPI
auf 8 bit umgestellt, SPI Tx DMA mit 3 Bytes geladen, SPI ISR Tx
complete benutzt um DMA neu zu starten und NSS zu pulsen. Funktioniert.
Jetzt brauche ich genau 400 kSPS. Das bedeutet, dass pro Sample 2,5 us
gebraucht werden. APBPeripheral clock für SPI ist 80 MHz (auf dem
STM32L431RBI), mit prescaler=4 sind es 20 MHz.
2,5 us * 20 MHz = 50 clocks. 50 clocks circular mit DMA zu generieren
wären 5 DMA-Aktionen und SPI Data size von 10 bit. Die empfangenen Daten
werden dann von einem anderen DMA in den Speicher geschrieben, z.B.
blockweise 1600 Bytes. Diese müssen aber aufwändig zusammengesetzt
werden weil sie durch data size von 10 bit fragmentiert sind. Mit diesem
Ansatz (DMA, kontinuierlich) sehe ich keine andere Möglichkeit als die
beschriebenen 5 x 10bit, um mit 400 kSPS auszulesen.
So, nächste Möglichkeit: Timer Update event nutzen, um DMA zu starten.
Das ist DEUTLICH flexibler in der Zeit, jede Abtastrate ist sehr einfach
zu realisieren. DMA wird dabei so konfiguriert, dass aus dem Speicher
ins SPI2->DR geschrieben wird. Nun aber kann man so wieder nur ein Byte
bzw. Halbwort übertragen, egal womit man DMA1_Stream7->NDTR belädt, denn
meinem Verständnis nach ist dieser DMA nun an den Timer gebunden und
haut sofort alles raus. SPI ist aber mit dem Raustakten des ersten
Wertes beschäftigt und ist erstmal blind. Und man muss sich wieder um
NSS kümmern.
Dann habe ich gelesen, dass man einenTimer mit mehreren DMA-Aktionen
überladen kann, mit OutputCompare, glaube ich. Wäre das der gängige Weg?
Gibt es vielleicht eine wirklich elegante Methode? Irgendwie fällt mir
nichts mehr ein.
Fuer ein aehnliche Problem mit eine F303 habe ich als Notiz:
* ADC Signale, Abtastung mit 200 kHz
* - SPI1 (oberer Kanal)
* TIM2 Periode: 4 us, CH3 aktiv: 3 us
* Compare startet ueber DMA_CH1 SPI1
* SPI1 RX wird ueber DMA_CH2 ausgelesen
Vielleicht hilfts weiter oder auf Nachfrage mehr
Uwe B. schrieb:> Fuer ein aehnliche Problem mit eine F303 habe ich als Notiz:> * ADC Signale, Abtastung mit 200 kHz> * - SPI1 (oberer Kanal)> * TIM2 Periode: 4 us, CH3 aktiv: 3 us> * Compare startet ueber DMA_CH1 SPI1> * SPI1 RX wird ueber DMA_CH2 ausgelesen>> Vielleicht hilfts weiter oder auf Nachfrage mehr
4 us => 250 kHz?
Mit "Compare startet ueber DMA_CH1 SPI1" kann man doch wieder nur max.
16 clocks takten, denn man kann per DMA in den SPI data register nur
einen Wert schreiben. DMA wird ja vom Timer und nicht von SPI ausgelöst.
Zumindest konnte ich durch Beladen des NDTR-Registers (number of data
register) trotzdem kein mehrfaches Senden auf SPI auslösen. Immer nur
ein Byte/Halbwort, je nachdem wie SPI eingestellt ist.
Für den ADC einfach 3x8 Bit via SPI und DMA senden/empfangen. Danach die
nicht gebrauchten Bits wegschmeißen.
Den DMA durch einen Timer Event Triggern.
Das Ganze kannst du sogar direkt so mit CubeMX konfigurieren und dann
schaust du dir den Code an wann welche Register gesetzt werden.
Tycho B. schrieb:> Hat bei mir beides nicht funktioniert, aber vielleicht mache ich auch> was verkehrt.
Ich denke beide deiner Wegen sind gut gedacht. Nun eine Fragmentierung
des Daten-Flusses wird immer eigenen Nachteile mit sich bringen.
Ich verstehe nicht warum ADS7054 so ein "komische" 18bit SPI hat.
Es gibt viele ähnliche 14/16bit ADCs, mit 16Bit SPI kommunizieren.
Wie auch sehr low Power (falls nötig) AD7980, AD7988.
Oder LTC2365, LTC2370, LTC2380 oder AD7915/7918.
ElektroFH schrieb:> Es gibt viele ähnliche 14/16bit ADCs, mit 16Bit SPI kommunizieren.> Wie auch sehr low Power (falls nötig) AD7980, AD7988.
Das lag nicht in meinem Einflussbereich. Es gibt wohl aber auch uC, die
nicht auf 16 clocks im SPI begrenzt sind. Hier im forum gelesen.
Es wird nur ein Byte abgesetzt da der Dma mit dem einen Request des
Timer nur eine
Transfer Unit zum Spi Fifo transferiert. Du müsstest mir dem einen Dma
Timer Request
einen Burst Transfer der gesamten 3 Bytes einleiten was dann bei 1 Byte
unit in 3 Dma Transfer
Zyklen endet. Aber Achtung das geht nur so lange gut wie der Spi Tx Fifo
platz hat
da hierbei kein Handshake mit dem eigentlichen Fifo und dessen platz
stattfindet.
Normal nutzt man dafür dann den txFifo Not full/has Space (oder ähnlich
gennant halt
sinngemäß) Request.
Das kann sich bei 3Byte noch ausgehen Musst du prüfen.
(Stm32 habe ich weniger mit zu tun bin bei Efm32 gelandet)
Ich kenne die verschiedenen Stm32 Dma' Implementierung nicht so gut in
Verbindung
mit der SPI Peripherie.
Mit dem Efm32 nutze ich für sowas den Spi AutoTx der getriggert wird und
hab nur
ein Dma Kanal für Rx oder stoße einen nötigen Tx Fifo Transfer
getriggert über einen
Dma Sync Descriptor an, der eigentliche Daten Transfer zum Spi Tx Fifo
erfolgt mit
dessen Request und eigenem Descriptor dafür.
Bei dem Nxp mit dem ich kurz arbeitete hat dessen Dma dafür neben den
requests des
Z.b. Peripherie Fifo auch einen nötige Trigger Bedingung, z.b extern das
Busy eines ADC
über Gpio oder Timer Events. Das geht dann auch mit langen Transfers >
Fifo size
Anständig.
Flomann schrieb:> Es wird nur ein Byte abgesetzt da der Dma mit dem einen Request des> Timer nur eine> Transfer Unit zum Spi Fifo transferiert.> da hierbei kein Handshake mit dem eigentlichen Fifo und dessen platz> stattfindet
So habe ich mir das auch gedacht, als nur ein Byte angekommen ist.
Flomann schrieb:> Normal nutzt man dafür dann den txFifo Not full/has Space (oder ähnlich> gennant halt
Dieses triggert dann DMA für die zweite Befüllung des SPI->DR-Registers.
"Not full/has Space" als trigger für DMA gibt es bei STM32 aber nicht,
nur als interrupt, siehe Anhang.
Es gibt die Möglichkeit einen Burst mit z.B. DMA2 Stream6 Channel0, da
nutzt man die verschiedenen OutputCompare von TIM1, so weit ich es
verstanden habe. Wie das aber genau funktioniert habe ich nicht
verstanden. Konfiguriert man auf den verschiedenen OutputCompare(nur auf
den Signalen, ohne sie nach Außen zu führen) zeitlich passende
verschiedene Überläufe, die nacheinander DMA auslösen, welches den
SPI->DR-Register befüllt? Ohne Handshake, nur durch ausmessen/ausrechnen
der Timings.
Würde wahrscheinlich funktionieren, das Problem mit NSS muss man dann
aber trotzdem über Interrupte lösen...
Hallo,
also erstmal mein Beitrag oben sicht ja am PC mit Firefox
furchtbar bzgl. Format aus. Am Lenovo Tab und Chrome mit
dem er erstellt wurde passte das eigentlich. ???? Naja
Bezüglich des SPI Requests für den DMA, das war eher allgemein
gemeint das der zugehöhrige SPI Request für ein ordentlichen
Datenfluss Handshake genuzt wird. Der Request selbst ist schon so
designed das dieser ausgelöst wird wen noch Platz im FIFO ist.
Das wollte ich jetzt im Detail nicht am genauem Namen festmachen.
Bei STM heist er dann einfach "SPIx_TX".
(Beim EFM32 z.b. USARTxTXEMPTY und USARTxTXBL(Req. bei Platz im FIFO))
Es ist ja etwas "ungewöhnlich" einen Request vom Timer zu nehmen
der dann den DMA veranlasst den SPI Fifo zu füllen, das ist halt
auserhalb des SPI Datenflus Handshake mit dem DMA und ggf.
problematisch.
(z.b. wenn zu transferierende Daten > Fifo Größe)
Im Prinzip wolltest du ja damit wohl ereichen das du Abhängig vom Timer
eine Abtastrate x hast. Die Wandlung wird ja bei dem Typ mit der
SPI Kommunikation durchgeführt. Das ganze quasi in HW Timing und
weniger SW Overhead?
Nur mit dem SPI_TX als Request und DMA in Circular Modus geht das ja
nicht
wirklich flexibel.
SPI und DMA Kanal aktiv und das läuft dann so vor dich hin.
Für deine gewünschten 400Ksmp/sec hast du ja dann auch mal in die
"Trickkiste" gegriffen und mit angepassten SPI-Clock, -Frame(10Bit) und
fünf Transfers gearbeitet um dies zu bewerkstelligen.
Mal eine Frage, dies lief sauber deinen Ansprüchen/Anforderungen
gerecht?
Das nötige "Formatieren/Packing" der Daten kann man ja noch machen.
Wenn dies deine Aufgabe erfüllt warum nicht?
Oder warum nicht (erstmal) einen einfachen konventionellen weg.
Timer -> TMR Interrupt -> Im Interrupt Wandeln mit Daten holen anstoßen.
Bei 3Byte/Zyklus muss es ja auch nichtmal unbedingt mit DMA für den SPI
sein zumal der SPI auch noch ein 4Byte FiFo hat.
Wäre das ein Problem, z.b. Latenz der ISR bzw. auch der sicher etwas
größerer Jitter als wenn die Wandlung rein in HW Timings angestoßen
wird.
Noch viele andere nötige Tasks? so das die ISR Latenz + Zeit in ISR für
Wandlung und Daten holen bei einem (kontinuierlichen?) 2,5us
Abtastzyklus
zu, zu hohen Auslastungen führt?
Nochmal was anderes:
Der DMA im F76x hat einen Burst Modus für 4,8,16 Transfers pro Request.
Annahme: Überall Byte Transfer Units, Anzahl Transfers = 4,
Request vom Timer welcher dann ein Burst mit 4 Transfers
einleitet zum SPI TX Fifo.
Das könnte vll. gehen da der SPI Fifo 4Byte groß ist sollte das
reinpassen.
Problem -> der L4xx DMA hat den "Burst" Modus so nicht, dessen
DMA implementierung ist im detail auch unterschiedlich wenn ich das
richtig sehe.
Achja bezüglich dem Hardware Chipselect "Problem" das kann ich aktuell
nicht ganz nachvollziehen. Mit den mir bekannten Controllern
war das i.R. so, das so lange Daten kontinuerlich in den SPI
Fifo geschrieben wurden diese Frames als Sequenz rausgehen und der
Chipselect enstprechend diese Zeit gesetzt blieb.(auser bei speziellen
aktivierten Modi die ein stroben/pulsen per Frame explizt vorsehen)
Beim EFM32 hat man z.b. aber auch noch konfigurations Möglichkeiten
z.b. mit dem CSHold können "Datenlücken" in SPI Bit Zeiten überbrückt
werden bevor das CS weggenommen wird. Macht das der STM32 nicht
auch so? wenn enstprechend parametriert. Zumindest sollte das
funktionieren sonst ist ein Abtastprozess mit dem Wandler und mit
möglichst
viel in "HW-(Timing)" und ohne SW intervention eh nicht wirklich
machbar.
Dann vll. doch die einfache "Konventionelle" Lösung?
Gruß
FloMann schrieb:> Im Prinzip wolltest du ja damit wohl ereichen das du Abhängig vom Timer> eine Abtastrate x hast. Die Wandlung wird ja bei dem Typ mit der> SPI Kommunikation durchgeführt. Das ganze quasi in HW Timing und> weniger SW Overhead?
Ja, genau
FloMann schrieb:> Mal eine Frage, dies lief sauber deinen Ansprüchen/Anforderungen> gerecht?
Ja, die Timings kann man sehr flexibel einstellen. SPI FIFO erlaubt
sogar ununterbrochenes Rausschieben der Bits. Das war mir vorher nicht
klar. Man muss also die zweite Befüllung gar nicht genau timen.
FloMann schrieb:> Oder warum nicht (erstmal) einen einfachen konventionellen weg.> Timer -> TMR Interrupt -> Im Interrupt Wandeln mit Daten holen anstoßen.> Bei 3Byte/Zyklus muss es ja auch nichtmal unbedingt mit DMA für den SPI> sein zumal der SPI auch noch ein 4Byte FiFo hat.> Wäre das ein Problem, z.b. Latenz der ISR bzw. auch der sicher etwas> größerer Jitter als wenn die Wandlung rein in HW Timings angestoßen> wird.> Noch viele andere nötige Tasks? so das die ISR Latenz + Zeit in ISR für> Wandlung und Daten holen bei einem (kontinuierlichen?) 2,5us> Abtastzyklus> zu, zu hohen Auslastungen führt?
Der ADC kann bis zu 1MSPS und IIR-Filter soll auch dazu. Ich will die
Auslastung verringern. Andererseits ist es auch eine schöne Aufgabe, um
die DMA-Innereien zu verstehen.
FloMann schrieb:> Achja bezüglich dem Hardware Chipselect "Problem" das kann ich aktuell> nicht ganz nachvollziehen. Mit den mir bekannten Controllern> war das i.R. so, das so lange Daten kontinuerlich in den SPI> Fifo geschrieben wurden diese Frames als Sequenz rausgehen und der> Chipselect enstprechend diese Zeit gesetzt blieb.
Ist bei STM32 anders. CS wird extra bei jedem Byte oder Halbwort, je
nachdem was man einstellt, runtergezogen und wieder hoch.
Ich habe das jetzt mit dem überladenen Timer-DMA gemacht. Sieht schief
aus, funktioniert aber wunderbar.
Tycho B. schrieb:> Ist bei STM32 anders. CS wird extra bei jedem Byte oder Halbwort, je> nachdem was man einstellt, runtergezogen und wieder hoch.>> Ich habe das jetzt mit dem überladenen Timer-DMA gemacht. Sieht schief> aus, funktioniert aber wunderbar.
Hallo,
Du nutzt jetzt die eine DMA Request "Leitung" eines Timer der so
konfiguriert ist das er dir zeitlich passend die 3 Requests
zum DMA erzeugt um die 3 Byte ADC Daten über das SPI zu senden.
Oder verteilt über verschachtelte unterschiedliche Requests.
Bezüglich dem CS ist das tatsächlich allgemein so bei STM, also auch
bei anderen derivaten oder jetzt bezogen auf den F7 und dessen SPI
implementierung, das diese den CS in HW nur im takte der Framebreite
pulsen lässt?, kein HW CS für kontinuierliche Sequenzen von x*Frames.
Aus dem RM0440 z.b: (müsste für die STM32G4xx Typen sein)
1
NSS output enable (SSM=0,SSOE = 1): this configuration is only used when the
2
MCU is set as master. The NSS pin is managed by the hardware. The NSS signal
3
is driven low as soon as the SPI is enabled in master mode (SPE=1), and is kept
4
low until the SPI is disabled (SPE =0). A pulse can be generated between
5
continuous communications if NSS pulse mode is activated (NSSP=1). The SPI
6
cannot work in multimaster configuration with this NSS setting.
oder aus RM0090(von F4xx)
1
NSS output enabled (SSM = 0, SSOE = 1)
2
This configuration is used only when the device operates in master mode. The
3
NSS signal is driven low when the master starts the communication and is kept
4
low until the SPI is disabled.
Beim ersten ist angeblich für den CS das "Enable" des SPI
auschlaggebend.
Beim zweiten wird es mit start der Kommunikation gesetzt bis das SPI
"abgeschaltet" wird.
AHA mir etwas verwirrend, so mal zwei Beispiele die ich noch aus
Dokumenten fand die ich hier noch habe von der MCU suche letztes Jahr
für ein Projekt (aber dies zu der Zeit gar nicht im detail beachtet
habe)
Wie machst du das dann jetzt mit dem CS wenn das der STM32 nicht für
eine
"Sequenz" von x*Frames dies in HW macht.
Per Software in einem zeitlich passenden eh vorhanden Interrupt oder
auch
über einen (den Req) Timer der Zeitlich passend den Ausgang bedient.
Gruß
Florian