50ns Delay mit STM32H7

#7065722
Lesenswert?

Hi,

ich suche eine Methode wie ich "hübsch" feste Wartezeiten programmieren 
kann.
Konkret muss ich ein IC am SPI über den CS "abfragen" ob Daten 
vorliegen. Wenn ja, wird MISO high, ansonsten low.
Im Datenblatt wird das Delay von CS low zu MISO mit max 50ns angegeben.
Jetzt läuft der Controller mit 200MHz, ein Clock Cycle dauert also 5ns.
Wie sag ich dem Controller jetzt, dass er nach dem setzen 50ns warten 
soll bis er MISO abfrägt? 10x asm("NOP"); finde ich nicht wirklich 
schön. Wenn ich das in eine Schleife packe kommen aber vermutlich 
weitere Cycles dazu, oder?

Edit: Es muss natürlich nicht genau 50ns sein, aber eben mindestens 50ns 
und so wenig "mehr" wie technisch einfach möglich.
(Firma: S-C-I DATA GbR) #7065737
Lesenswert?

Stefan ⛄ F. schrieb:
> keine Timer-Interrupts handhaben können.

Wiso den Puls direkt als Timer Start und dann per Timerinterrupt den 
Rest abarbeiten.
Lässt sich exakt 50nS Resalisieren glaube etwa Count auf 2, der Rest des 
Delay wird vom Interrupt selber gebraucht.
Am ende des Interrupts Timer wieder zurücksetzen und auf nächsten Start 
warten.
Timercklock auf 200MHz setzen.

Gerald M. schrieb:
> Das einspringen und ausspringen aus dem Interrupt...
Die Rutine selbst im Interrupt setzen die Zeit ist dann genau im 
Datenblatt des µC Definiert.

Wenn der µC DMA unterstützt könnte man dies sogar per DMA lösen, mache 
ich gerne beim TI so ;-)
Brauche dazu 2 DMA Kanäle und die CPU kann friedlich weiter schlafen ;-)
Gast #7065746
Lesenswert?

Gerald M. schrieb:
> Konkret muss ich ein IC am SPI über den CS "abfragen" ob Daten
> vorliegen. Wenn ja, wird MISO high, ansonsten low.
> Im Datenblatt wird das Delay von CS low zu MISO mit max 50ns angegeben.
> Jetzt läuft der Controller mit 200MHz, ein Clock Cycle dauert also 5ns.
> Wie sag ich dem Controller jetzt, dass er nach dem setzen 50ns warten
> soll bis er MISO abfrägt? 10x asm("NOP"); finde ich nicht wirklich
> schön. Wenn ich das in eine Schleife packe kommen aber vermutlich

Gerade beim H7 dauern die GPIO-Zugriffe "ewig". Es dürfte schon 
vollkommen ausreichen, den Schreibzugriff zum Aktivieren von CS zweimal 
auszuführen oder alternativ den Lesezugriff aufs MISO-Bit zweimal (und 
das erste Resultat zu verwerfen). Wenn ich mich richtig entsinne, kommt 
man beim H7 gerade mal so auf ca. 13 Mhz, wenn man einen GPIO-Pin 
softwaremäßig toggelt ...
Beim G0 geht das viel schneller, da die GPIOs dort an einem extra Bus 
direkt an der CPU hängen.
(Firma: S-C-I DATA GbR) #7065757
Lesenswert?

Stefan ⛄ F. schrieb:
> Taktzyklen nötig sind, bis darin die
> erste Zeile von deinem eigenen C-Quelltext ausgeführt wird.

Das braucht mir zu viel zeit, da ich nichts zeitkritisches in C 
Programmiere wenn ich es vermeiden kann....
Laut Datenblatt (wobei ja der Genaue µC Typ nicht bekannt)
Sollte es so grad Passen wenn der erste Befehl im Interrupt die Abfrage 
bzw setzen ist.

Stefan ⛄ F. schrieb:
> bis nach einem Interrupt die ISR

Habe gelesen 4 Taktcyclen? deshalb ja auf 2 Zählen (0+1)...
#7065785
Lesenswert?

A. B, schrieb:
> Schreibzugriff zum Aktivieren von CS zweimal auszuführen oder alternativ
> den Lesezugriff aufs MISO-Bit zweimal (und das erste Resultat zu
> verwerfen)

Das finde ich schon einmal nicht schlecht und noch übersichtlich in 
einer Funktion unterzubringen.
Was mir noch einfallen würde, ich weiß bei einem Stm32F4 gab es einen 
Counter der dauerhaft mitgezählt hat. Der H7 (übrigens ist meiner der 
Stm32H753 ) hat das bestimmt auch. Hier könnte ich mir als einfach den 
Timerwert merken und dann in einer Schleife vergleichen bis ich 10 Takte 
gewartet habe. Wenn ich das durchdenke habe ich allerdings das Problem, 
dass ich nicht auf "gleich" gehen würde, sondern auf "größer gleich" da 
ich auch hier vermutlich nicht in jedem Takt vergleiche sondern auch 
wieder springen muss. Wenn es aber Sau blöd läuft, und der Timer gerade 
überläuft, kann es passieren dass ich ewig warte (Beispiel: Timer läuft 
bei 1000 über, bei 989 nehme ich den Wert und weiß dass ich bis 999 
warten muss. Ich vergleiche bei 998 und springe noch einmal hoch in der 
Schleife. Bis ich wieder vergleiche ist der Timer bei 0, also muss ich 
weiter warten bis der Timer wieder >= 999 ist). Hier würde eine 
"rettende Logik" vermutlich auch viele Takte fressen...
Gast #7067112
Lesenswert?

50 ns Delay sind 10 Taktzyklen 200 MHz.

Was willste in den 10 Takten machen?
- Register wiederherstellen, Rücksprung ausführen,
- neuen Einsprung machen, Register sichern,
- Wert lesen... (Register wiederherstellen, Rücksprung ausführen)

Das sind vielleicht 11...75 hektisch-nutzlose µC-Takte mit 1...65 Takten 
Zeitverschwendung, während du das auch ganz entspannt mit exakt (!) 10 
nutzlosen NOP-Takten aussitzen kannst.

Wie im richtigen Leben: Geiz spart ein bisschen, die Vermeidung
unnötiger Ausgaben spart meist viel mehr!
#7067126
Lesenswert?

Rudolph schrieb:
> Was von den beiden denn nun?

Steht doch unmissverständlich da. Was in Datenblatt steht sind 50ns und 
dass ich entsprechend mindestens 50ns warten muss, aber auch mit 60ns 
zufrieden bin, aber nicht mit beispielsweise 2000ns.

Kurt schrieb:
> Wie im richtigen Leben: Geiz spart ein bisschen, die Vermeidung
> unnötiger Ausgaben spart meist viel mehr!

Wie gesagt, ich kenne das, finde das nur optisch nicht schön. Wenn man 
wenigstens eine Art "Inline Schleife" machen könnte die der Compiler 
dann in 10x NOP interpretiert.
Gast #7067140
Lesenswert?

So ein enges Timing mit CPUs wie einem Cortex-M7 zu machen, ist ziemlich 
schwierig - die CPUs haben kaum noch deterministisches Zeitverhalten. 
Das fängt schon mit der Taktfrequenz an - die ist per PLL einstellbar, 
nicht gottgegeben.  Die CPU hat nen Instructioncache und sehr flexible 
Verbindungen zu Instruction-Memory - der Code muss also im ITCM liegen 
damit das berechenbar bleibt.  Das Interrupts dazwischen funken können, 
ist klar.  Der CPU-Zugriff auf GPIO läuft über (evtl mehrere) asynchrone 
Busse - CPU- und IO-Timing sind nicht gleich.  Außerdem können die 
Zugriffe durch DMA zeitlich beenflusst werden.  Das Einsynchronisieren 
muss auch beachtet werden (teilweise ist das noch konfigurierbar, 
Glitch-Filter).  Usw usf.

Es hat einen Grund, warum die bei diesen µC so viele dedizierte 
HW-Blöcke für alle möglichen IO-Arten einbauen (teilweise sogar 
zusätzliche Cortex-M0) - die CPU selbst kann es nicht mehr ordentlich.

Wenn du 50ns zwischen Output und Input brauchst, sieh zu, dass du 
irgendwas der vorhanden IO-Blöcke/Timer/DMA dafür nutzen kannst - ggf 
auch mit externer Brücke/Beschaltung.

Wenn du es doch über die CPU machen willst, kalkuliere sehr großzügig. 
Die paar NOPs sind Peanuts ...

Ansonsten würd ich mal schauen, ob du das Problem wirklich so lösen 
muss - evtl kommt der Chip ja damit zurecht, dass du das "Data Ready" 
ignorierst und einfach einen normalen SPI-Zyklus startest ...
(Firma: S-C-I DATA GbR) #7067171
Lesenswert?

Trotz allem (und da ich den Rest des Programms nicht kenne).

Wen du willst, das es immer funktioniert, pack die SPI selbst in ein IRQ 
oder via DMA.
Sonnst kann es dir passieren, das es entweder immer deutlich über 50ns 
Dauert oder halt eben mal nur 40ns.
Cortex CPU können mit unter, durch all die Eigenheiten die [foobar] 
schon erwähnte, sehr unterschiedlich auf Loops und/oder NOP reagieren, 
je nach dem was sie gerade zu tun haben.
Ein
yesitsme schrieb:
> delay_ns()
kann Funktionieren, muss aber nicht, das ist stark Kompiler und deren 
Einstellung abhängig, sprich man kann auf die Nase Fallen.

Wie ich schon weiter oben erwähnte, und auch [foobar] schon bestätigte,
Je nach Rest des Programms und ev. verwendete Library's, kann dir sonst 
ein IRQ oder DMA, oder sogar der CPU Cache, in die "Suppe Spucken".
Dann sind die Delay nur noch Willkürlich.

Wenn du deine SPI über DMA Lösen kannst, ist dies die sicherste Methode, 
den der DMA wird erst ausgelöst, wenn die SPI-USI auch tatsächlich 
bereit ist.
Ein Delay, kann nicht kontrollieren ob nicht gerade ein SPI Transver 
stattfindet. ein DMA wird durch die SPI-USI/Timer ausgelöst und zwar 
erst wenn sie Bereit ist bzw. Zeit abgelaufen ist.
Als kleine Ausschweifung(nur etwas [OT])
Ich habe in MSP von TI Video aufbau implementiert die ja sehr Zeitgenau 
sein müssen, diese realisiere ich alleinig durch Timer, DMA und IRQ, und 
über [3 USCI (RGB)/1 USCI (Monochrom)] das heißt, die CPU kann andere 
Dinge erledigen oder Schlafen, bis eine Änderung des Videospeichers 
anliegt. und durch die besondere Eigenheit der MSP TI µC im 
Interleave-DMA-Mode, wird es niemals Rauschen, oder sonstige Störungen 
im Bild geben.
Würde ich dies mit Delay realisieren, hätte die CPU kaum noch Zeit etwas 
zu "rechnen" und immer mal wieder Artefakte wären im Bild sichtbar
Ganz zu schweigen von extrem mehr Stromverbrauch.
[/OT]

Wie schon gesagt, wir kennen weder was dein "Geheimes Programm" alles 
Kann, muss, oder soll.
Genau so wenig wie Welchen µC du verwendest.
Deshalb ist eine Delay, für mich nicht zu empfehlen weil absolut 
Unbekanntes Resultat.
...oder auch völliger Schuss ins Blaue....

Aber jeder wie er mag.
73 55
#7067252
Lesenswert?

Gerne kann ich den IC nennen, typischerweise wird darauf aber nicht 
eingegangen:
ADS1261
Davon hängen mehrere (mit teils verschiedener Samplerate) am SPI und 
werden via DMA bedient. Die haben zwar eigene DataReady-Pins, doch da 
diese Funktion auch über MISO abgebildet werden kann und die Pins knapp 
waren, wurden diese nicht verbunden.
Gast #7067468
Lesenswert?

Gerald M. schrieb:
> Jetzt läuft der Controller mit 200MHz, ein Clock Cycle dauert also 5ns.
> Wie sag ich dem Controller jetzt, dass er nach dem setzen 50ns warten
> soll bis er MISO abfrägt?

Oh je!
Soviele Experten und soviel Unsinniges Zeug.
1
  GPIOB->BSRR = (SET_GPIO << PB3);    // IO-Bit set
2
  GPIOB->BSRR = (RESET_GPIO << PB3);  // IO-Bit reset
3
  GPIOB->BSRR = (SET_GPIO << PB3);    // IO-Bit set

Diese Befehlsfolge erzeugt einen negativen Impuls von 60 ns bei 200 MHz 
SystemCoreClock. Wird "IO-Bit reset" zweimal nacheinander ausgeführt, 
dauert der Impuls 120 ns.
Wo soll denn das Problem sein?
#7067495
Lesenswert?

Peter D. schrieb:
> Wo kaufst Du den überhaupt, ich hab da nur sehr lange Lieferzeiten
> (>2023) gesehen.

Es gab mal eine Zeit, da konnte man, einfach so, nicht abgekündigte 
Bauteile kaufen (in diesem Fall Oktober 2020). Da habe ich ein paar 
Waagen als Geschenk gebaut. Da der ADS1261 gerade noch so handlötbar 
ist, habe ich sicherheitshalber ein paar mehr bestellt. Da ich keinen 
kaputt gemacht habe, habe ich jetzt welche übrig und für meine 
Espressomaschine (Messung des Gewichts des Abtropfblechs, und damit die 
Menge Espresso) eingeplant.

Peter D. schrieb:
> Wie schnell läßt Du ihn denn wandeln, daß Du es so eilig hast?

Das muss ich noch schauen, momentan 50SPS. Ich habe es grundsätzlich ja 
nicht eilig, deswegen habe ich ja den Stm32H7 ausgesucht, damit ich 
nicht jeden Takt sparen muss. Und die 10x NOPs tun mir auch nicht weh, 
ich find es halt einfach nicht so hübsch und dachte da muss es etwas 
besseres geben.
Natürlich möchte ich so wenig Zeit warten wie möglich und im Notfall 
setze ich den Pin, dann mache ich etwas anderes, und dann prüfe ich. 
Aber auch das finde ich nicht so schön, weil dann alles 
auseinandergerissen wird, und in zwei Monaten frage ich mich, warum ich 
mitten im Code einen Pin setze. Dann lese ich das Kommentar, und frage 
mich ob ich das nicht hätte besser machen können.

Hans-Georg L. schrieb:
> Sind dafür nicht gerade die MSSI Bits im Register SPI_CFG2 da ?

Wenn ich die Register über SPI abfrage, habe ich natürlich schon lange 
CS gesetzt und hätte damit schon die Antwort auf meine Frage, bevor ich 
das erste Bit raus clocke.

m.n. schrieb:
> Soviele Experten und soviel Unsinniges Zeug.

Das wurde so ähnlich ja auch schon angeführt, also dass die Pins 
deutlich langsamen als die Systemclock sind, und dass es vermutlich 
reicht, einfach den Pin zu setzen und direkt danach den anderen Pin 
einzulesen.
#7067510
Lesenswert?

Gerald M. schrieb:
> ich find es halt einfach nicht so hübsch und dachte da muss es etwas
> besseres geben.

Ich mach das immer so, daß ein Timerinterrupt wartet, bis eine Messung 
garantiert fertig ist. Dann liest er das Ergebnis ein, schaltet den 
Eingangs-MUX eins weiter und startet den ADC erneut. Ich hab also ein 
Array, wo für jeden benutzten Eingang das neueste Ergebnis liegt. Die 
Mainloop kann es verarbeiten, wann sie Lust dazu hat.
Gast #7067537
Lesenswert?

Gerald M. schrieb:
> Und die 10x NOPs tun mir auch nicht weh,
> ich find es halt einfach nicht so hübsch

Die 10 x NOPs wären auch nutzlos, denn sie sind schneller abgearbeitet 
als der Pin reagiert hat ;-)

Gerald M. schrieb:
> Ich habe es grundsätzlich ja
> nicht eilig, deswegen habe ich ja den Stm32H7 ausgesucht

Das verwundert mich und auch die genannten 5 ns. Ich dachte erst, es 
wäre ein Tippfehler und es wären 2 ns gemeint. Die H7 lasse ich immer 
oberhalb 500 MHz laufen. Dazu sind sie schließlich da.

Zum "Glück" hast Du keinen F7 verwendet. Der ist bei IO-Aufgaben Faktor 
3 schneller. Da müßte man schon die Arduino-IDE nehmen, um ihn 
auszubremsen ;-)
#7067559
Lesenswert?

Kurt schrieb:
> 50 ns Delay sind 10 Taktzyklen 200 MHz.
>
> Was willste in den 10 Takten machen?
> - Register wiederherstellen, Rücksprung ausführen,
> - neuen Einsprung machen, Register sichern,
> - Wert lesen... (Register wiederherstellen, Rücksprung ausführen)
>
> Das sind vielleicht 11...75 hektisch-nutzlose µC-Takte mit 1...65 Takten
> Zeitverschwendung, während du das auch ganz entspannt mit exakt (!) 10
> nutzlosen NOP-Takten aussitzen kannst.

"exakt" ist ein wenig übertrieben, jedenfalls bei den Cortex-M.
ARM meint dazu (beim M7 und auch beim M0):
1
NOP does nothing. NOP is not necessarily a time-consuming NOP.
2
The processor might remove it from the pipeline before it reaches
3
the execution stage.
https://developer.arm.com/documentation/dui0646/a/The-Cortex-M7-Instruction-Set/Miscellaneous-instructions/NOP
Gast #7067632
Lesenswert?

Gerald M. schrieb:
> Das hier könnte gehen:
> void delay_ns(uint32_t ns) {
>   volatile uint32_t cycles = ((SystemCoreClock/1000000L)*ns)/1000;
>   volatile uint32_t start = DWT->CYCCNT;
>   do  {
>   } while(DWT->CYCCNT - start < cycles);
>
> Vermutlich auch einige Zyklen zu viel, aber ca. sollte das passen.

Den Cycle-Counter für so etwas zu nehmen scheint auf dem ersten Blick 
eine elegante Lösung zu sein. Allerdings nur solange bis einem die 
Realität einholt. Und damit meine ich z.B. einen Debugger/IDE wie Segger 
Embedded Studio. Da wird der DWT->CYCCNT jedesmal disabled wenn sich der 
Debugger (jlink) trennt in dem das SBC_DEMCR_TRCENA Bit zurückgesetzt 
wird. Eine Option in den Einstellungen konnte ich nirgends finden. Das 
Segger Forum brachte in dieser Hinsicht auch keine Hilfe:
https://forum.segger.com/index.php/Thread/8083-ABANDONED-jlink-reset-TRCENA-in-disconnect/
#7067676
Lesenswert?

Gerald M. schrieb:
 Hans-Georg L. schrieb:
>> Sind dafür nicht gerade die MSSI Bits im Register SPI_CFG2 da ?
>
> Wenn ich die Register über SPI abfrage, habe ich natürlich schon lange
> CS gesetzt und hätte damit schon die Antwort auf meine Frage, bevor ich
> das erste Bit raus clocke.
>
Dazu müsstes du auch die slave select pins verwenden und die über die 
alternate functions auf deine slaves umschalten. Kommt aber darauf an 
welche Spi, wie viel Slaves du hast und welches Gehäuse dein H7 hat. 
Dann steuert die SPI Hardware dein CS und du kannst von 0 .. 15 
Spi-Takte Pause einlegen.  Oder wenn es geht ein externer CS 
Multiplexer.


ps. Die H7 haben doch 6 Spi kannst du da nicht jedem Slave eine eigene 
spendieren. Ich nehm es zurück, du willst ja pins sparen ;-)
#7067708
Lesenswert?

Hans-Georg L. schrieb:
> Dazu müsstes du auch die slave select pins verwenden und die über die
> alternate functions auf deine slaves umschalten.

Ich benutze die CS einfach als GPIO

Hans-Georg L. schrieb:
> Die H7 haben doch 6 Spi kannst du da nicht jedem Slave eine eigene
> spendieren. Ich nehm es zurück, du willst ja pins sparen ;-)

Nicht, dass ich mich rechtfertigen müsste, aber ich will keine Pins 
sparen, ich muss. Anbei mal ein Bild aus CubeMX.
Angehängte Dateien:
Gast #7068192
Lesenswert?

Gerald M. schrieb:
> habe ich jetzt welche übrig und für meine
> Espressomaschine (Messung des Gewichts des Abtropfblechs, und
> damit die Menge Espresso) eingeplant.

Gerald M. schrieb:
> ich will keine Pins sparen, ich muss.


Jetzt werd ich aber neugierig: ein 100-Beiner und Dir werden die Pins 
knapp? Was soll denn die Espressomaschine alles können? Ein Tänzchen 
darbieten? Den Kaffe selbst einkaufen und servieren?
#7068312
Lesenswert?

mIstA schrieb:
> Jetzt werd ich aber neugierig: ein 100-Beiner und Dir werden die Pins
> knapp? Was soll denn die Espressomaschine alles können? Ein Tänzchen
> darbieten? Den Kaffe selbst einkaufen und servieren?

Ich erkenne hier durchaus den sarakstischen Unterton, aber es gibt eben 
auch Leute, die Dinge aus Spaß machen, welche nicht nur aus einer 
PID-Lib und PWM-Lib aus in der Arduino-IDE bestehen.

Schaltplan ist anbei,
außerdem:
- Bild von der Elektronik
- Bild von der "fertigen" Maschine
- Bild von der Regelung eines Thermoblocks durch eine "Modell Predictive 
Control" welche auf dem Mikrocontroller läuft, in Matlab aufgenommen 
(durch einen TCP/IP Server der auf der Maschine läuft).

Hier ein frühes Video wie der Aufbau ganz am Anfang geplant war:
https://www.youtube.com/watch?v=uOMLxQwiOoQ

Man erkennt hoffentlich, dass hier keine "klassische" Vibrationspumpe 
oder Rotationspumpe benutzt wird, sondern ein noch klassischerer Kolben, 
nur eben durch Motor und Spindel angetrieben, so dass man eine sehr gute 
Fluss und Druck Regelung hat.
Vielleicht sieht man auch jetzt wo die ganzen Pins hingehen und warum 
ich nicht überal eigene SPI-Kanäle verwenden kann.
Angehängte Dateien:
#7068425
Lesenswert?

Peter D. schrieb:
> 24V Lasten schalte ich gerne mit einem TPIC6B595, macht auch das Layout
> einfacher.

Ich sage ja nicht, dass der Schaltplan perfekt ist, in dem Fall haben 
die Ventile allerdings 8W, weshalb der IC nicht geeignet wäre 
(ungeachtet dessen, dass es vermutlich andere gibt die das können).

Peter D. schrieb:
> Die vielen 24Bit-ADCs sehe ich aber nirgends, wären auch für diese
> Anwendung völlig oversized.

Wie gesagt, die hatte ich eben noch aus einem anderen Projekt übrig. Im 
Schaltplan sind sie noch falsch benannt, die werden nur über einen 
Header angeschlossen. Die Platine mit dem IC wird direkt auf die 
Wägezellen geschraubt, so habe ich nur eine kurze analoge Strecke, das 
Signal wird von dort digital übertragen.
So oversized sind sie übrigens nicht. Ich messe damit die komplette 
Abtropfschale (Wägezelle bis 3kg) und würde am liebsten einzelne Tropfen 
detektieren (0.05g) was einer Auflösung von 16 bit entspricht (ein 
Tropfen = ein Bit). Von daher wären >20 Bit nicht schlecht, und dann 
kann ich eben gleich den nehmen.
Der erste Tropfen ist wichtig, weil damit der Start der Extraktion 
bestimmt wird. Alternativ kann ich natürlich auch die Kamera benutzen, 
welche von oben auf die Tasse gerichtet ist, und mit einem Filter oder 
einer tranierten KI den ersten Tropfen erkennen. Das habe ich aber noch 
nicht getan.
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren